题目描述
给定一个链表,请反复删去链表中和为0
的连续序列,直到不存在这样的序列为止。
比如 [1,2,-3,3]
, 可能的答案为 [3]
,或者[1,2]
思路
连续和为0
,连续和,容易想到前缀和。顺着前缀和来想,什么时候能够判断出一段连续序列的和为0
呢。只要2个位置的前缀和相等即可。比如链表为[1,2,3,-4,2,2]
, 算出其前缀和数组为[1,3,6,2,4,6]
。可以看到分别在第3,第6个位置上的前缀和,皆为6
。我们只需要删除第4
到第6
个位置,即可。
也就是说,只要在从左往右遍历的过程中,出现了相同的前缀和,我们就要进行删除操作。由于我们操作的是链表,则需要一个Map
来存储链表节点,Map
的key
为前缀和,value
为链表节点。
我们只需要遍历链表,并累加计算前缀和,并每次判断当前前缀和的值,是否已存在,若是,则从Map
从取出对应的节点,并删除2个节点之间的部分(注意需要将被删除的节点,从Map
中移除)。
下面借这篇LeetCode题解中的图片来进行说明
特别注意:
- 第一个节点可能是需要删除的节点,比如
[1,2,-3,4]
,所以我们需要一个虚拟头节点。 - 当整个数组的连续和才为
0
时,可能不存在重复的前缀和,比如[1,2,-3]
,前缀和为[1,3,0]
, 此时我们要将虚拟头节点也加入到Map
中,并且设其前缀和为0
代码
class Solution {
public ListNode removeZeroSumSublists(ListNode head) {
ListNode hh = new ListNode(0, head);
Map<Integer, ListNode> map = new HashMap<>();
map.put(0, hh);
int sum = 0;
while (head != null) {
sum += head.val; // 当前位置的前缀和
if (map.containsKey(sum)) {
ListNode node = map.get(sum);
ListNode deleteBegin = node.next;
node.next = head.next; // 将中间部分切掉
int dSum = sum;
while (deleteBegin != head) {
// 从map中删除, 删除到head前一个节点, 注意不能删除head这个节点, 因为head节点本身没有被put进去
dSum += deleteBegin.val;
map.remove(dSum);
deleteBegin = deleteBegin.next;
}
} else {
map.put(sum, head);
}
head = head.next;
}
return hh.next;
}
}
扩展
另一种思路,不需要在遍历过程中维护Map
的中间状态。但需要2次遍历。第一次遍历计算前缀和,并直接插入到Map
,相同的前缀和的值,会被后面的节点覆盖掉。
第二次遍历时,计算前缀和,并判断该前缀和是否存在于Map
中, 由于Map
中存的都是最右侧的节点,则能保证删除的是最长的。删除之后,并不需要从Map
中移除中间被删除的节点,因为对于后续的遍历,若出现了前缀和相同,则Map
中的节点一定是当前节点更往右的,不可能出现在前面已被删除的部分。
这种思路的好处是,代码很好写。
注意:
- 第二次遍历时,要从虚拟头节点开始。
- 实际在判断
map.containsKey(sum)
,并作链表切断时,做了很多无用的切断。比如根本不存在连续和为0
的序列,如[1,2,3]
。在第二次遍历时,每个节点的位置,都会遇到map
中存在这个前缀和的情况,都会做一次cur = cur.next
这样的无效操作
class Solution {
public ListNode removeZeroSumSublists(ListNode head) {
ListNode hh = new ListNode(0, head);
Map<Integer, ListNode> map = new HashMap<>();
map.put(0, hh);
int sum = 0;
for (ListNode cur = head; cur != null; cur = cur.next) {
sum += cur.val;
map.put(sum, cur);
}
sum = 0;
for (ListNode cur = hh; cur != null; cur = cur.next) {
sum += cur.val;
if (map.containsKey(sum)) cur.next = map.get(sum).next;
}
return hh.next;
}
}