文章目录
- 题目描述
- 思路一
- 代码示例
- 思路二
- 代码示例
题目描述
在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。
图片示例:
思路一
解决该问题较简单,且在写代码时不易出错的做法如下:
遍历一遍链表,记录重复结点的结点值。
再遍历一遍链表,逐个删除重复结点。
注:该方法需要遍历两遍链表,且需要开辟额外的内存空间存储重复结点的结点值,所以一般不提倡。
代码示例
typedef struct ListNode ListNode;
struct ListNode* deleteDuplicates(struct ListNode* head){
//空或只有一个节点
if(!head || !(head->next))
return head;
//设置哨兵位
ListNode* dummy = (ListNode*)malloc(sizeof(ListNode));
dummy->next = head;
ListNode* pre = dummy; // 重复值节点区间的前驱节点
ListNode* cur = pre->next;
while(cur && cur->next) // cur->next:当下面的if语句中next是空指针时,避免空指针的解引用
{
ListNode* next = cur->next;
if(cur->val == next->val)
{
//跳过重复数字的节点,next是重复值节点区间的后一个节点
while(next && cur->val == next->val) // next:避免next最后是空指针,比如链表为[1,1]
next = next->next;
//连接
pre->next = next;
//删相同数字的节点
while(cur != next)
{
ListNode* tmp = cur->next;
free(cur);
cur = tmp;
}
}
else
{
pre = cur;
cur = cur->next;
}
}
head = dummy->next;
free(dummy);
dummy = NULL;
return head;
}
思路二
我们当然应该尽可能在遍历一遍链表的情况下解决该问题,这时我们需要使用两个指针配合完成,该过程当中包含大量细节,大致步骤如下:
1.为了后续操作方便,先为该链表创建一个头结点。
2.使用指针prev和last遍历链表,初始时prev指向头结点,last指向头结点的下一个结点。
3.当last指向的结点值与其后一个结点的结点值相同时,last独自后移,直到last指向结点的结点值与其下一个结点的结点值不同为止,此时让prev指向的结点指向last的后一个结点,最后让last指向下一个结点(图中未后移)。
4.当last指向的结点值与其后一个结点的结点值不同时,prev和last一同向后移。
如此进行下去,直到last将链表遍历完,链表当中重复的结点也就全部被删除了,最后返回头结点指向的链表即可。
代码示例
public ListNode deleteDuplicates(ListNode head) {
if(head == null||head.next== null){
return head;
}
ListNode dummyHead = new ListNode(-101);
dummyHead.next = head;
ListNode prev = dummyHead;
ListNode cur = prev.next;
while(cur!= null){
ListNode sec = cur.next;
if(sec == null){
break;
}
if(cur.val!= sec.val){
prev = prev.next;
}else {
while (sec!=null&&sec.val == cur.val){
sec = sec.next;
}
//到达此处有三种情况:
//1、没有需要删除的重复结点,是因为last->next == nullptr到此
//2、有需要删除的重复结点,是因为last->next == nullptr到此(链表后半段都需要删除)
//3、有需要删除的重复结点,是因为last->val != last->next->val到此(链表中间某段需要删除)
prev.next = sec;
}
cur = sec;
}
return dummyHead.next;
}