JZ76 删除链表中重复的结点
描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5。
示例1
输入:
{1,2,3,3,4,4,5}
返回值:
{1,2,5}
示例2
输入:
{1,1,1,8}
返回值:
{8}
解析
本题的要求很简单,就是要删除链表中出现了两次以上的节点(全部删除,不是删除多余的)。对于这个问题,有两种思路为使用辅助空间和不使用辅助空间。
不使用辅助空间
因为链表是有序的,所以我们可以不使用辅助空间,即只在原链表上进行操作而实现目标。此时我们通过 pre 指针和 cur 指针对链表进行遍历:
-
先设置 pre 指针比 cur 指针慢一步;
-
对 cur 指针走到的节点进行判断,若当前节点( cur )与下一个节点( cur.next )的值相同,说明有重复节点;
-
则 pre 指针留在原地,cur 指针持续往前移动,直到 cur 与 cur.next 的值不再相同为止,也就是说将 cur 从重复段的开头移动到了结尾;
-
此时 cur 位于重复段结尾,故将位于重复段开头前的 pre 指针的 next 设置为位于重复段结尾的 cur 的下一个节点( cur.next ),就相当于删除了重复段。
以上就是解决问题的基本步骤,但在实现时遇到了一个问题:pre 指针需要比 cur 指针慢一步,在初始化时,是不是要将 pre 设置为第一个节点,cur 设置为第二个节点呢?这是行不通的,因为会导致第一个节点和第二个节点重复时无法删除。
要解决这个问题,需要一个比较巧妙的方法:手动添加一个伪头节点 head,其后继是链表的头节点 pHead,同时保证 head 的值不会与 pHead 的值相同。有了这个伪头节点,cur 就可以设置为 pHead,pre 就设置为 head,这样 cur 就可以从链表头节点开始遍历了!在返回时,只需要返回 head.next 就相当于删除掉这个伪头节点了!
代码清单
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if(pHead == null)
return null;
ListNode head = new ListNode(Integer.MAX_VALUE);
head.next = pHead;
ListNode pre = head;
ListNode cur = head.next;
while( cur != null ){
if( cur.next != null && cur.val == cur.next.val){
while( cur.next != null && cur.val == cur.next.val)
cur = cur.next;
cur = cur.next;
pre.next = cur;
}else{
pre = pre.next;
cur = cur.next;
}
}
return head.next;
}
}
使用辅助空间
因为问题涉及到重复元素,所以也可以直接使用 Set 集合。先进行一次遍历将重复的元素存入 Set 中(遍历时当前元素和下一个元素相同就存入,由于 Set 的性质即使多次重复也只会存入一次),再进行二次遍历检验每个元素是否存在于 Set 中,若存在则说明是重复元素,需要调整指针关系删除当前元素。
与不使用辅助空间相同,因为头结点具有特殊性,所以也需要创建一个伪头结点,具体内容同上。
代码清单
import java.util.Set;
import java.util.HashSet;
/*
public class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
*/
public class Solution {
public ListNode deleteDuplication(ListNode pHead) {
if(pHead == null)
return null;
Set<Integer> nodeSet = new HashSet<>();
ListNode p1 = pHead;
while(p1.next != null){
if(p1.val == p1.next.val)
nodeSet.add(p1.val);
p1 = p1.next;
}
ListNode head = new ListNode(Integer.MAX_VALUE);
ListNode p2 = pHead;
head.next = p2;
ListNode pre = head;
ListNode cur = p2;
while(cur != null){
if(nodeSet.contains(cur.val)){
cur = cur.next;
pre.next = cur;
continue;
}
cur = cur.next;
pre = pre.next;
}
return head.next;
}
}
总结
最后一道链表的问题了,对于链表相关的题目,基本都需要调整指针关系,所以多设置几个指针的思路会经常用到。
摸了好久,看情况决定后面写什么吧。