[LeetCode] 1171. Remove Zero Sum Consecutive Nodes from Linked List 從鏈表中刪去總和值為零的連續節點



Given the head of a linked list, we repeatedly delete consecutive sequences of nodes that sum to 0 until there are no such sequences.

After doing so, return the head of the final linked list.  You may return any such answer.

(Note that in the examples below, all sequences are serializations of ListNode objects.)

Example 1:

Input: head = [1,2,-3,3,1]
Output: [3,1]
Note: The answer [1,2,1] would also be accepted.

Example 2:

Input: head = [1,2,3,-3,4]
Output: [1,2,4]

Example 3:

Input: head = [1,2,3,-3,-2]
Output: [1]

Constraints:

  • The given linked list will contain between 1 and 1000 nodes.
  • Each node in the linked list has -1000 <= node.val <= 1000.

這道題讓從一個鏈表中移除所有和為0的連續的結點,並給了好幾個例子幫助我們理解題意。好久沒玩鏈表的題了,對於一道 Medium 的題來說,應該不會太復雜。其實鏈表的問題都可以當成數組問題來思考,二者的區別的是鏈表不能像數組那樣可以直接通過下標來訪問元素,處理起來會麻煩一些。這道題可以看作是要找和為0的所有子數組,這里難道要遍歷所有子數組求和么?對於數組可能還好,但對於鏈表來說簡直是災難,所以一定要用些技巧,這里要借助累加和來做,比如對於例子1來說 [1,2,-3,3,1],建立累加和數組為 [1,3,0,3,4],可以發現出現0了,這表示前面所有的數字之和一定為0。還可以發現出現了兩個3,這表示中間的某個子數組之和為0了。再比如對於例子3來說 [1,2,3,-3,-2],建立累加和數組為 [1,3,6,3,1],可以發現,0不一定總會出現,但是一旦出現相同的數字,則表示一定有和為0的子數組出現,所以可以利用這個特點來快速定位子數組的位置進行操作。為了快速地找出重復,並且還需要記錄位置,可以用 HashMap 來建立累加和跟其對應位置結點之間的映射。對於鏈表的題,基本上都需要建立一個 dummy 結點,將其 next 結點指向 head,因為 head 結點很有可能被移除掉。用 cur 結點來遍歷鏈表,初始化指向 dummy 結點,用變量 curSum 來記錄當前的累加和,初始化為0。開始遍歷鏈表,將當前遍歷到的結點值加到 curSum 中,若這個 curSum 已經在 HashMap 中出現了,表示已經有了和為0的連續結點,此時需要從 HashMap 中移除這些結點,將 cur 指向前一個累加和為 curSum 的結點位置的下一個結點,然后將該結點值加上 curSum 存入臨時變量t中,若這個t不存在 HashMap 中,進行循環,從 HashMap 中移除t的映射對兒,然后 cur 指向下一個結點,t再加上下一個結點值繼續循環。循環退出后,將 m[curSum] 結點的下一個位置指向 cur->next,這樣就相當於跳過了和為0的所有連續結點。若 curSum 不在 HashMap 中,則建立 curSum 和結點 cur 之間的映射。最后返回 dummy->next 即可,參見代碼如下:


解法一:

class Solution {
public:
    ListNode* removeZeroSumSublists(ListNode* head) {
        ListNode *dummy = new ListNode(-1), *cur = dummy;
        dummy->next = head;
        unordered_map<int, ListNode*> m;
        int curSum = 0;
        while (cur) {
            curSum += cur->val;
            if (m.count(curSum)) {
                cur = m[curSum]->next;
                int t = curSum + cur->val;
                while (t != curSum) {
                    m.erase(t);
                    cur = cur->next;
                    t += cur->val;
                }
                m[curSum]->next = cur->next;
            } else {
                m[curSum] = cur;
            }
            cur = cur->next;
        }
        return dummy->next;
    }
};

我們還可以通過兩次遍歷讓寫法更簡潔一些,雖然沒有上面的解法高效。但整體思路還是很像的,都是利用累加和相同來確定和為0的連續結點的位置,這里通過第一次遍歷先建立累加和跟最后一次出現該累加和位置的結點的遍歷,因為累加和可能會出現重復,后面的映射會覆蓋前面的映射。這樣在第二次遍歷的時候,每次都用將 cur->next 賦值為 m[curSum]->next,那么遇到相同的累加和時,中間和為0的連續結點就被跳過了,實現了同樣的目的,參見代碼如下:


解法二:

class Solution {
public:
    ListNode* removeZeroSumSublists(ListNode* head) {
        ListNode *dummy = new ListNode(-1);
        dummy->next = head;
        unordered_map<int, ListNode*> m;
        int curSum = 0;
        for (ListNode *cur = dummy; cur; cur = cur->next) {
            m[curSum += cur->val] = cur;
        }
        curSum = 0;
        for (ListNode *cur = dummy; cur; cur = cur->next) {
            cur->next = m[curSum += cur->val]->next;
        }
        return dummy->next;
    }
};

Github 同步地址:

https://github.com/grandyang/leetcode/issues/1171


類似題目:

Delete N Nodes After M Nodes of a Linked List


參考資料:

https://leetcode.com/problems/remove-zero-sum-consecutive-nodes-from-linked-list/

https://leetcode.com/problems/remove-zero-sum-consecutive-nodes-from-linked-list/discuss/366350/C%2B%2B-O(n)-(explained-with-pictures)

https://leetcode.com/problems/remove-zero-sum-consecutive-nodes-from-linked-list/discuss/366319/JavaC%2B%2BPython-Greedily-Skip-with-HashMap


LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM