• LeetCode-从链表中删去总和值为零的连续结点


    目录

    1.题目描述

     2.解题思路

    2.1 思路一:[前缀和 + 哈希表]

    2.2 思路二:[前缀和+"双指针法"]


    1.题目描述

    🍁题目要求

     🍁示例分析

     🍁提示

     2.解题思路

    准备工作: 

    🍃1.从示例一的输入输出,我们很容易就想到了前缀和,通过一个 sum 变量就能知道什么时候需要做删除操作。

    🍃2.删除链表总和为 0 的结点,头结点也存在被删除的可能,所以我们需要一个辅助结点,来帮助我们进行删除操作。

    分析解法:

    示例1 提供的两种输出结果,其实间接的告诉了我们此题至少两种做法。

    2.1 思路一:[前缀和 + 哈希表]

    示例1 的输出结果如果是 [3,1],我们可以联想到哈希表。

    🍃1.从上图上,我们发现 sum 在加加的过程中,如果存在总和为 0 的结点,那么中间一定有两次 sum 的值相同,交叉出现相同的值。可能出现多对相同的值,但是我们只关心最前面那对值(例如上图 3 也重复出现了,但是 0 比 3 先出现,并且在将两个 0  之间的值删除后,3 就只剩下一个了)。

    🍃2.既然需要删除,又有相同的值,而哈希表恰好具备这些特点,当 key 值相同的时候,就会产生覆盖的效果,而这种效果如果放在链表中,那么刚好可以做到删除!!

    🍃3.于是,我们的思路是:将 ListNode.val 作为哈希表的 键(key),将 ListNode 作为哈希表的 值(val)。我们在添加 key 值的时候,就可以将 sum 添加进去;取值的时候,我们依旧按照链表的值加和的顺序来取,假设我们此时取出一个值为 0 ,此时的 0 就已经不再是 0x99 这个结点了,而是 0x33 这个结点了。(结合上图理解)

    代码示例

    1. class Solution {
    2. public ListNode removeZeroSumSublists(ListNode head) {
    3. HashMap map = new HashMap<>();
    4. ListNode newHead = new ListNode(0);
    5. newHead.next = head;
    6. int sum = 0;
    7. ListNode cur = newHead;
    8. while(cur != null) {
    9. sum += cur.val;
    10. // map在添加元素的时候,key值相同,结点就会覆盖,就意味着前一个 key 一直加到后一个 key 的结果为 0
    11. // 这也是为什么 node.val 作 key 的关键
    12. map.put(sum,cur);
    13. cur = cur.next;
    14. }
    15. sum = 0;
    16. cur = newHead;
    17. while(cur != null) {
    18. sum += cur.val;
    19. // 当后面的 key 值出现与前面的相等时,通过改变指针指向就可以删去中间的结点
    20. cur.next = map.get(sum).next;
    21. cur = cur.next;
    22. }
    23. return newHead.next;
    24. }
    25. }

    复杂度分析

    时间复杂度:O(N),遍历两次链表,空间换取时间。

    空间复杂度:O(N) 

    2.2 思路二:[前缀和+"双指针法"]

    示例2的输出结果如果是 [1,2,1],那么我们就可以使用 "双指针法"。

    输出这个结果,无非就是让 -3 和 3 相加变为 0 。此处多加一组示例 [1,2,3,-3,1] 便于后续对比理解。

    此处的 "双指针法",并不是两两指针一起走,而是分别控制内外两层循环。并且此处的删除条件就仅仅是 sum = 0,不再是出现相同的和。

    🍃1.我们申请一个结点 prev 指向 newHead(不指向 head, 是因为如果出现 sum = 0 的时候,prev 和 cur 刚好指向同一个结点,那就没法进行删除了)。

    🍃2.申请一个结点 cur 指向 head,不断往后计算链表的前缀和。

    cur 在每一轮循环计算前缀和的时候,会存在两种情况:

    • cur 在遍历链表计算前缀和的过程中遇上了 sum = 0,可能在链表末尾,也可能在链表中间
    • cur 在遍历链表计算前缀和的过程中没有遇上 sum = 0,但是不代表链表中没有和为 0 的结点。所以控制外层循环的指针需要往后走一步,cur 内层循环进行下一次判定。(不断缩小 cur 遍历链表的范围)

    画图理解上述两种情况:

    情况一

    情况二:

     
     

    代码示例 

    1. public ListNode removeZeroSumSublists1(ListNode head) {
    2. ListNode newHead = new ListNode(0);
    3. newHead.next = head;
    4. ListNode prev = newHead;
    5. while(prev != null) {
    6. ListNode cur = prev.next;
    7. int sum = 0;
    8. while(cur != null) {
    9. sum += cur.val;
    10. // 链表 val 加和为 0 的时候,说明此时 prev 后面的数据,
    11. // cur.next 之前的数据都需要删除
    12. if(sum == 0) {
    13. prev.next = cur.next;
    14. break;
    15. }
    16. cur = cur.next;
    17. }
    18. // 1.如果内存循环正常走到链表末尾,则进行下一轮循环
    19. // 2.如果内存循环结束的时候,cur 还没走到连链表末尾,说明此时在中途遇到 和为0 了
    20. // 进行删除操作,并且这轮循环继续向下执行
    21. if(cur == null) {
    22. prev = prev.next;
    23. }
    24. }
    25. return newHead.next;
    26. }

     复杂度分析

    时间复杂度:O(N^2),prev 每移动一次,cur 遍历一次链表。

    空间复杂度:O(1)


    本篇博客就到这里了,谢谢观看!

  • 相关阅读:
    【校招VIP】测试方案之测试需求分析
    elasticsearch 7.7.0 单节点配置x-pack
    2022-08-18 第六小组 瞒春 学习笔记
    笔试题:金额拆分
    Redis主从复制
    【Pandas】数据分组groupby
    【机器学习】聚类算法中的 K-means 算法及其原理
    PCIe系列专题之二:2.1 TLP的前世今生
    Redis数据类型之Set的使用
    22-07-29 西安 分布式事务、Seata
  • 原文地址:https://blog.csdn.net/xaiobit_hl/article/details/126230840