一、题目
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例 1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例 2:
输入:head = [1,1,1,2,3]
输出:[2,3]
提示:
链表中节点数目在范围 [0, 300] 内
-100 <= Node.val <= 100
题目数据保证链表已经按升序 排列
二、思路
- 首先别读错题目,题目要求删除所有重复的数字,而不是只保留一位重复数字
(1->1->2) 要删成(2) - 创建一个虚拟头节点,前面帖子已经有过涉及,虚拟头节点是一种很重要的技巧
- 让两个指针p、q去遍历,从虚拟头节点开始,q初始化为p的后一位,循环判断q与q的下一位val值是否相同,如果相同就让q继续向后走一位,再重复该步骤,直到q的下一位不等于q,就让p->next指向q->next,也即实现了删除效果
三、代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
struct ListNode *dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->next = head;
struct ListNode *p = dummy;
struct ListNode *q = p->next;
while(p->next && q->next){
if(q->val == q->next->val){
//当前位置的后两个结点相同: 因为要删除相同的所有节点,所以有必要在待删除的前一个节点处停留,
// 故判断当前节点的后两个结点,这样就方便删除了。
// 此时q就是相同结点的第一个
while(q->next && q->val == q->next->val){
//从第二个结点开始一直找到最后一个相同的节点的位置 最终q停留在这里
q = q->next;
}
p->next = q->next; //p在这一串相同节点的前一位置,q在这一串相同节点的最后一个节点上,这句代码实现了删除效果
q = p->next; //q再次指向p的下一位,回到初始状态,进行下一轮便利。
}
else {
//如果当前节点的后两个结点不相同,直接p和q各向前走一位
p = p->next;
/**
// 如果if中写的p->next->val 则不需要这句代码
因为误打正着,pq会跑到同一位置去,但是q不会执行while, || q执行了while会怎么样?
并且pq在同一位置,所以p->next = q->next形同摆设,
相当于只执行了q = p->next,同样实现了q到了p后面的情况。
这样会造成一些无关代码的运行 对运行速度有一些影响
*/
q = p->next;
}
}
return dummy->next;
}
四、代码优化
- 稍做优化 没有很大影响
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
struct ListNode *p = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *ans = p;
p->next = head;
struct ListNode *q = p->next;
while(p->next && q->next){
if(q->val == q->next->val){
while(q->next && q->val == q->next->val){
q = q->next;
}
p->next = q->next;
q = p->next;
}
else {
p = p->next;
q = p->next;
}
}
return ans->next;
}