• leetcode刷题(三)


    面试题 02.04. 分割链表

    编写程序以 x 为基准分割链表,使得所有小于 x 的节点排在大于或等于 x 的节点之前。如果链表中包含 x,x 只需出现在小于 x 的元素之后(如下所示)。分割元素 x 只需处于“右半部分”即可,其不需要被置于左右两部分之间。

    翻译:将小于x的数移到大于或者等于x的数之前

    题解

    先找到第一个大于或者等于x的节点(pos),然后遍历之后的节点,发现小于x的节点就移到(pos)之前。考察的操作:链表节点的删除插入

    实际上直接插在头节点之前也行。整道题看起是不是很像快排的(partion)操作,说明能用链表实现快排 ><

    class Solution {
    public:
        ListNode* partition(ListNode* head, int x) {
            if (!head || !head->next) return head;
    
            ListNode* root = new ListNode(-1);
            root->next = head;
    
            head = root;
            while(head->next != NULL) {
                if ((*(head->next)).val >= x) break;
                head = head->next;
            }
    
            ListNode* start = head, *pre = head;
            head = head->next; // 上面的while循环得到的是第一个大于或者等于x的节点 之前的节点
    
            while(head != NULL) {
                if ((*head).val < x) {
                    pre->next = head->next;
                    head->next = start->next;
                    start->next = head;
                
                    head = pre->next;
                }
                else {
                    pre = head;
                    head = head->next;
                }
            }
    
            return root->next;
        }
    };
    

    1367. 二叉树中的列表

    给你一棵以 root 为根的二叉树和一个 head 为第一个节点的链表。

    如果在二叉树中,存在一条一直向下的路径,且每个点的数值恰好一一对应以 head 为首的链表中每个节点的值,那么请你返回 True ,否则返回 False 。

    一直向下的路径的意思是:从树中某个节点开始,一直连续向下的路径

    题解

    思路比较明显:枚举节点,然后判断以这个节点开头是否能和给的链表匹配成功。(真的暴力)

    md,复杂度计算错了。尽管在一次匹配过程中可能要遍历(2^{len})个节点,但实际的树只有(n)个节点,所以时间复杂度(O(n*min(2^{len}, n)))。另外,我都不会写带返回值的DFS了,难受~~~

    /*
    不太喜欢这样的题,不过官方的code真的很nice,学习学习(https://leetcode-cn.com/problems/linked-list-in-binary-tree/solution/er-cha-shu-zhong-de-lie-biao-by-leetcode-solution/)
    */
    class Solution {
    public:
        bool DFS(ListNode* head, TreeNode* root) {
            if (head == NULL) return true;
            if (root == NULL) return false;
            if ((*head).val != (*root).val) return false;
            return DFS(head->next, root->left) || DFS(head->next, root->right);
        }
        bool isSubPath(ListNode* head, TreeNode* root) {
            if (head == NULL) return true;
            if (root == NULL) return false;
            return DFS(head, root) || isSubPath(head, root->left) || isSubPath(head, root->right);
        }
    };
    

    面试题35. 复杂链表的复制(好题)

    请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

    题解

    在每个节点的后面插入一个新的节点,更新random指针后,取出奇数节点,就是复制后的链表。

    想到用map实现也算是妙啊

    class Solution {
    public:
        Node* copyRandomList(Node* head) {
            if (head == NULL) return head;
    
            map<Node*, Node*> mp;     // 知道 map 和 unordered_map 的区别吗
    
            Node* start = head;
            while(start != NULL) {
                mp[start] = new Node(start->val);
                start = start->next;
            }
    
            start = head;
            while(start != NULL) {
                mp[start]->next = mp[start->next];
                mp[start]->random = mp[start->random];
                start = start->next;
            }
    
            return mp[head];
        }
    };
    

    1171. 从链表中删去总和值为零的连续节点

    给你一个链表的头节点 head,请你编写代码,反复删去链表中由 总和 值为 0 的连续节点组成的序列,直到不存在这样的序列为止。

    删除完毕后,请你返回最终结果链表的头节点。

    你可以返回任何满足题目要求的答案。

    题解

    常规思路是枚举区间,时间复杂度(O(n^2)).

    连续区间的和可由前缀和相减得到,所以只需要判断当前位置的前缀和是否在该位置以前出现过,出现,则说明这两个位置之间的区间和为0。因为是链表,故用map存一下拥有相同前缀和的节点。时间复杂度大概是(O(nlog_2(n)))

    加一个限制条件:最终序列的长度要最短。怎么破?

    class Solution {
    public:
        ListNode* removeZeroSumSublists(ListNode* head) {
            ListNode* root = new ListNode(-1);
            root->next = head;
    
            ListNode* h = root;
    
            map<int, ListNode*> mp;
            mp[0] = root;
    
            int sum = 0, temp_sum = 0;
            while(h->next != NULL) {
                h = h->next;
                sum += h->val;
                if (mp.find(sum) != mp.end()) {
                    ListNode* temp = mp[sum]->next;
                    mp[sum]->next = h->next;
                    
                    temp_sum = sum;
                    while(temp != h) {
                        temp_sum += temp->val;
                        mp.erase(temp_sum);      // 删除和为0的整个区间,避免干扰(红黑树的删除操作要调整节点,能不用就不用)
                        temp = temp->next;
                    }
                }
                else mp[sum] = h;
            }
    
            return root->next;
        }
    };
    

    1019. 链表中的下一个更大节点

    给出一个以头节点 head 作为第一个节点的链表。链表中的节点分别编号为:node_1, node_2, node_3, ... 。

    每个节点都可能有下一个更大值(next larger value):对于 node_i,如果其 next_larger(node_i) 是 node_j.val,那么就有 j > i 且 node_j.val > node_i.val,而 j 是可能的选项中最小的那个。如果不存在这样的 j,那么下一个更大值为 0 。

    返回整数答案数组 answer,其中 answer[i] = next_larger(node_{i+1}) 。

    题解

    单调栈,从后向前遍历,如果当前元素小于栈顶则压入,否则,就弹出栈顶元素。

    说哈复杂度,每个数都只入栈一次,所以(n)次循环中,总的入栈出栈次数最大可能是(2n),故复杂度(O(n))

    class Solution {
    public:
        vector<int> nextLargerNodes(ListNode* head) {
            vector<int> num;
            while(head != NULL) num.push_back(head->val), head = head->next;
    
            int n = (int)num.size();
            stack<int> st;
            st.push(0x3f3f3f3f);
    
            //vector<int> ans(n, 0);  少用一个vector,空间减少了,时间倒增加了几十ms
       
            for (int i = n - 1; i >= 0; --i) {
                while(num[i] >= st.top()) st.pop();
    
                int t = num[i];
                num[i] = st.top();
    
                st.push(t);
            }
    
            for (int &x: num) if (x == 0x3f3f3f3f) x = 0;
            return num;
        }
    };
    

    面试题 02.03. 删除中间节点

    实现一种算法,删除单向链表中间的某个节点(除了第一个和最后一个节点,不一定是中间节点),假定你只能访问该节点

    题解

    刷多了思维有点僵化,md,调换值就可以了

    /*   如果是面试,多半就over了   */
    class Solution {
    public:
        void deleteNode(ListNode* node) {
            if (node == NULL) return;
            while(node->next != NULL) {
                node->val = node->next->val;
                if (node->next->next == NULL) {
                    node->next = NULL;
                    break;
                }
                node = node->next;
            }
        }
    };
    

    实际上,将下一个节点的值赋给当前节点,然后删除下一个节点

    class Solution {
    public:
        void deleteNode(ListNode* node) {
            node->val = node->next->val;
            node->next = node->next->next;
        }
    };
    
  • 相关阅读:
    Ansible命令介绍之ansible
    Ansible命令介绍
    Ansible配置文件讲解
    博客搬家。新博客地址 http://fangjian0423.github.io/
    SpringMVC源码分析系列
    MyBatis拦截器原理探究
    通过源码分析MyBatis的缓存
    ThreadLocal原理及其实际应用
    logstash搭建日志追踪系统
    Mybatis解析动态sql原理分析
  • 原文地址:https://www.cnblogs.com/zgglj-com/p/12802972.html
Copyright © 2020-2023  润新知