例题:
给你一个链表的头节点 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;
}
}