例題:
給你一個鏈表的頭節點 head,請你編寫代碼,反覆刪去鏈表中由 總和 值爲 0 的連續節點組成的序列,直到不存在這樣的序列爲止。
刪除完畢後,請你返回最終結果鏈表的頭節點。
數據案例
輸入:head = [1,2,-3,3,1]
輸出:[3,1]
提示:答案 [1,2,1] 也是正確的。
輸入:head = [1,2,3,-3,4]
輸出:[1,2,4]
輸入:head = [1,2,3,-3,-2]
輸出:[1]
之前有做過找最大前綴和的問題,感覺這個連續最大爲0的鏈表題也應該類似。但是對於第一次解決鏈表前綴和問題,思路想了很久。
思路:
head = [1,2,-3,3,1]
1、對於這組數據來講,我們可以依次得到他的每一位的前綴和是[1,3,0,3,4],有了這一步,思路就很清晰了,只要找到一樣的前綴和,兩個數之間的數字都是滿足連續和爲0的
2、特殊情況的處理:
- [-1,1]這種剛好從頭開始滿足條件的數據案例,需要增加啞結點特殊處理
- [1,2,-3,3,1,-1]得到的子序列和是[1,3,0,3,4,3]已經斷過鏈再次出現斷鏈前內和一樣的情況,第二次斷鏈必須是從第一個3開始斷鏈,所以每次斷鏈應該把丟掉的數據及時處理
3、如何實現?這也是一個難題。最後我選擇了用hashMap存放連續和 和 結點地址。大量空間換了時間,但是這樣的效率依然不是很高
//前綴和的問題
public ListNode removeZeroSumSublists(ListNode head) {
ListNode dymmynode = new ListNode(0);
dymmynode.next = head;
ListNode cur = dymmynode;
HashMap<Integer,ListNode> map = new HashMap<>();
int count = 0;
while(cur != null) {
count += cur.val;
//不存在的時候
if(!map.containsKey(count)) {
map.put(count, cur);
}else {
cur = map.get(count).next;
int val = count + cur.val;
while(val != count) {
map.remove(val);
cur = cur.next;
val += cur.val;
}
map.get(count).next = cur.next;
}
cur = cur.next;
}
return dymmynode.next;
}
後來看了一些大佬的題解,這個題可以完全用指針實現,效率極高
class Solution {
public ListNode removeZeroSumSublists(ListNode head) {
//生成帶頭結點的鏈表
ListNode dummyHead = new ListNode(0);
dummyHead.next = head;
ListNode h = dummyHead;
while(h.next != null){
ListNode p = h.next;
ListNode q = p;
int sum = p.val; //sum是p至q的總和
while(q.next != null || sum == 0){ // 加個sum==0的條件,用來處理鏈表最後n個節點的和爲0時的情形如:[1, 2, -2]
if(sum == 0){ // 先判斷sum的值再求和,可以不用對值爲0的節點特殊處理
h.next = q.next;
break;
}
q = q.next;
sum += q.val;
}
if(sum != 0) h = h.next;
}
return dummyHead.next;
}
}