Remove duplicates from sorted list

本文分享了解决LeetCode上两道关于链表去重问题的心得体会。第一题要求删除链表中所有重复的数据,使得每个数据仅出现一次;第二题则需要删除所有重复的数据,只保留链表中唯一的数据。文章详细介绍了使用双重指针解决第二题的巧妙方法。

做leetcode的这两道题,感觉收获还是挺多的,总结一下。


I题题目:

给定一个单链表,删除其中重复数据,使原链表中的数据都只出现一次。


I题没有什么难点,当时做完之后针对链表的题目写了这么几句话:

1.需要几个指针先弄清楚。

2.最后需要返回的指针要预留出来。

后来在Discuss里研究别人的答案,又加了一条:

3.需要把废弃的节点都回收了。麻烦的是如果这么做一般还需要多搞出一个指针。


II题题目:

给定一个单链表,删除其中所有的重复数据,只留下原链表中单独存在的数据。


II题刚开始自己没做出来,主要是按照I题总结的方法做,越做越跑偏。

因为最终的头结点刚开始是不能确定的,如果按照上面的2来做,先要把返回节点确定下来,再继续过程,实在是太麻烦了,不知道要用到多少指针。

后来研究了别人的算法,在Discuss里号称是best c++ solution,刚开始都没看懂····分析了一半天才恍然大悟!

原来双重指针是有这样的作用啊,之前除了课本上要求,还从来没有主动用过双重指针。

大体思路是这样的:(head指针图中用h表示)

    新定义了一个指向head指针的指针runner(图中用r表示)

    1-->1-->1-->2-->3-->nullptr

    h

    r

    用*r(最开始的时候其实就是head)控制指针的后移

    如果满足条件((*runner)->next && (*runner)->val==(*runner)->next->val)则说明等于此值的所有元素需要被删除,即将*r指针后移,开始while循环,直到*runner指向一个新值,继续对上面的条件进行判定。

    1-->1-->1-->2-->3-->nullptr

                     h

                     r

    如果上面的条件不成立,则说明该节点是单一的,属于结果链表,不做处理直接跳过

    重点就在这里,此时不是简单的指针后移,而是将双重指针进行了后移,即runner指向了下一个指针:runner=&((*runner)->next);

    1-->1-->1-->2-->3-->nullptr

                     h

                           r

    所以,在head没有找到返回节点之前,head是一直跟着*runner向后移动的,当head找到了第一个结果节点的时候,runner就更改了指向,指向head指针的下一个节点,之后head就不再移动了。

    此方法可以更好的保持程序的一致性,不需要单独为寻找返回指针而写一段代码,感觉还是很好的。

你的代码的目的是去除数组中重复的元素,并将去重后的元素放回原数组 `nums` 的前面,返回去重后的长度。然而,这段代码存在一些问题: --- ### ✅ 正确的逻辑应该是: 1. 遍历数组,跳过连续相同的元素。 2. 将不重复的元素保存到一个新的结构里。 3. 最后把结果写回到原数组的前部。 --- ## ❌ 存在的问题分析: ### 问题 1:`j` 被赋值为 `i + 1` 后,你没有处理越界情况 ```java int j = i+1; if (j<=nums.length - 1&& nums[j] == nums[i]){ ``` 虽然你在判断 `j <= nums.length - 1`,但后续的 while 循环没有再次检查边界: ```java while (nums[j] == nums[i]){ j++; } ``` 当 `j` 超出数组范围时会抛出 `ArrayIndexOutOfBoundsException`。 --- ### 问题 2:`i = j;` 导致跳过了一个元素 例如数组是 `[1,1,1]`,当你在 `i=0`,进入 `while` 循环后 `j` 增加到了 3(超出数组),然后你执行了 `i = j`,此时 `i = 3`,循环结束。实际上你应该只保留第一个 `1`,其余跳过,但你并没有正确地控制这个过程。 --- ### 问题 3:`sorted.add(nums[i]);` 在某些情况下会重复添加元素 --- ## ✅ 正确的 Java 实现(使用双指针法) 下面是一个更高效、简洁、标准的实现方式: ```java class Solution { public int removeDuplicates(int[] nums) { if (nums.length == 0) return 0; int i = 0; // 指向不重复元素的最后一个位置 for (int j = 1; j < nums.length; j++) { if (nums[j] != nums[i]) { i++; // 只有不相等的时候才移动指针 nums[i] = nums[j]; // 把不重复的元素放到前面 } } return i + 1; // 返回新数组的长度 } } ``` --- ### 🔍 代码解释: - 使用两个指针 `i` 和 `j`: - `j` 是快指针,用来遍历整个数组; - `i` 是慢指针,指向当前不重复部分的最后一个位置。 - 当发现 `nums[j] != nums[i]` 时,说明遇到了新的不重复元素: - 先让 `i++`,腾出位置; - 然后把 `nums[j]` 的值复制过来。 - 最终 `[0...i]` 就是去重后的数组内容,长度为 `i+1`。 --- ### 🧪 示例输入输出: 输入: ```java int[] nums = {0,0,1,1,1,2,2,3,3,4}; ``` 输出: ```java // 修改后的 nums 前5位为 [0,1,2,3,4] // 返回值为 5 ``` ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值