哈希表题目:从链表中删去总和值为零的连续结点

该文章描述了一种算法,用于从链表中删除所有连续节点,这些节点的值之和为零。算法首先创建一个哑节点,然后遍历链表两次,使用哈希表记录节点和,以便找到和为零的节点序列并进行删除操作。最后返回处理后的链表头节点。
摘要由CSDN通过智能技术生成

题目

标题和出处

标题:从链表中删去总和值为零的连续结点

出处:1171. 从链表中删去总和值为零的连续结点

难度

6 级

题目描述

要求

给你一个链表的头结点 head \texttt{head} head,请你编写代码,反复删去链表中由总和值为 0 \texttt{0} 0 的连续结点组成的序列,直到不存在这样的序列为止。

删除完毕后,请你返回最终结果链表的头结点。你可以返回任何满足题目要求的答案。

(注意,下面示例中的所有序列,都是对 ListNode \texttt{ListNode} ListNode 对象序列化的表示。)

示例

示例 1:

输入: head   =   [1,2,-3,3,1] \texttt{head = [1,2,-3,3,1]} head = [1,2,-3,3,1]
输出: [3,1] \texttt{[3,1]} [3,1]
提示:答案 [1,2,1] \texttt{[1,2,1]} [1,2,1] 也是正确的。

示例 2:

输入: head   =   [1,2,3,-3,4] \texttt{head = [1,2,3,-3,4]} head = [1,2,3,-3,4]
输出: [1,2,4] \texttt{[1,2,4]} [1,2,4]

示例 3:

输入: head   =   [1,2,3,-3,-2] \texttt{head = [1,2,3,-3,-2]} head = [1,2,3,-3,-2]
输出: [1] \texttt{[1]} [1]

数据范围

  • 给你的链表中可能有 1 \texttt{1} 1 1000 \texttt{1000} 1000 个结点
  • 对于链表中的每个结点,结点的值满足 -1000 ≤ node.val ≤ 1000 \texttt{-1000} \le \texttt{node.val} \le \texttt{1000} -1000node.val1000

解法

思路和算法

这道题要求从链表中删去总和值为零的连续结点。为了得到总和值为零的连续结点片段,需要对每个结点计算从链表的头结点到该结点的所有结点值的总和。定义一个结点的「结点总和」为从链表的头结点到该结点的所有结点值的总和。

由于需要删除链表中的结点,且头结点可能被删除,因此需要创建哑结点 dummyHead \textit{dummyHead} dummyHead,令 dummyHead . val = 0 \textit{dummyHead}.\textit{val} = 0 dummyHead.val=0 dummyHead . next = head \textit{dummyHead}.\textit{next} = \textit{head} dummyHead.next=head

假设 x x x y y y 是链表中的两个结点,从 x x x 开始向后移动一次或多次可以到达 y y y,且 x x x y y y 的结点总和相等,则从结点 x . next x.\textit{next} x.next y y y 的所有结点值的和为零,可以将这些结点全部删除,删除这些结点的方法是令 x . next x.\textit{next} x.next 指向 y . next y.\textit{next} y.next,执行该操作之后, x x x 的下一个结点为原始链表中 y y y 的下一个结点,因此达到删除从结点 x . next x.\textit{next} x.next y y y 的所有结点的效果。

为了得到每个结点的结点总和,需要遍历链表,并使用哈希表记录每个结点总和对应的结点。从哑结点开始,对于每个结点,将结点值加到结点总和(哑结点的结点值为 0 0 0),然后将结点总和与当前结点存入哈希表。遍历结束之后,哈希表中存储了所有出现过的结点总和,以及每个结点总和对应的最后一个结点。

得到所有出现过的结点总和与对应的最后一个结点之后,从哑结点开始再次遍历链表并重新计算每个结点的结点总和。当遍历到结点 x x x 时,如果结点总和是 sum \textit{sum} sum,则从哈希表中得到结点总和 sum \textit{sum} sum 对应的结点 y y y,令 x . next x.\textit{next} x.next 指向 y . next y.\textit{next} y.next。可能有两种情况:

  • 如果 x x x y y y 是同一个结点,则该操作不会改变链表结构;

  • 如果 x x x y y y 是不同的结点,则该操作删除从结点 x . next x.\textit{next} x.next y y y 的所有结点,被删除的结点的结点值的和为零。

遍历结束之后,链表中剩下的结点一定不存在总和值为零的连续结点,理由如下:假设遍历到结点 x x x 时,对应的结点总和为 sum \textit{sum} sum,结点 y y y 是满足结点总和为 sum \textit{sum} sum 的最后一个结点,则令 x . next x.\textit{next} x.next 指向 y . next y.\textit{next} y.next,在该操作之后,链表中只剩下结点 x x x 满足结点总和为 sum \textit{sum} sum,因此只有一个结点满足结点总和为 sum \textit{sum} sum,同理可知,遍历结束之后,每个结点总和的结点都是唯一的,不存在两个结点对应的结点总和相等,因此也不存在总和值为零的连续结点。

遍历结束之后,返回 dummyHead . next \textit{dummyHead}.\textit{next} dummyHead.next

代码

class Solution {
    public ListNode removeZeroSumSublists(ListNode head) {
        ListNode dummyHead = new ListNode(0, head);
        Map<Integer, ListNode> map = new HashMap<Integer, ListNode>();
        int sum = 0;
        ListNode node = dummyHead;
        while (node != null) {
            sum += node.val;
            map.put(sum, node);
            node = node.next;
        }
        sum = 0;
        node = dummyHead;
        while (node != null) {
            sum += node.val;
            node.next = map.get(sum).next;
            node = node.next;
        }
        return dummyHead.next;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表两次,每次哈希表操作的时间都是 O ( 1 ) O(1) O(1),因此总时间复杂度是 O ( n ) O(n) O(n)

  • 空间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要使用哈希表记录每个结点总和对应的最后一个结点,不同的结点总和的数量不会超过链表中的结点数量。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值