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

文章目录

      • 题目描述
      • 思路
      • 代码
      • 扩展

题目描述

给定一个链表,请反复删去链表中和为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来存储链表节点,Mapkey为前缀和,value为链表节点。
我们只需要遍历链表,并累加计算前缀和,并每次判断当前前缀和的值,是否已存在,若是,则从Map从取出对应的节点,并删除2个节点之间的部分(注意需要将被删除的节点,从Map中移除)。

下面借这篇LeetCode题解中的图片来进行说明

LeetCode 1171. 从链表中删去总和值为零的连续节点_第1张图片

LeetCode 1171. 从链表中删去总和值为零的连续节点_第2张图片

特别注意:

  • 第一个节点可能是需要删除的节点,比如[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;
    }
}

你可能感兴趣的:(算法,链表,leetcode,算法)