初阶
【题目描述】
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回已排序的链表。
示例1:
输入:head = [1,1,2]
输出:[1,2]
示例2:
输入:head = [1,1,2,3,3]
输出:[1,2,3]
题目来源(力扣):删除排序链表中的重复元素
【基本思路】
做题时一定要结合题目描述以及示例去理解,以免出现理解上的偏差。
根据题目以及示例,意思是把本体留下,重复的干掉。
遍历一次链表即可,根据当前节点值与下一个节点值是否相等,可分为两种情况:
1)若相等,则删掉下一个节点。
2)若不等,则将下一个节点变为当前节点。
构成迭代。
【代码实现】
typedef struct ListNode ListNode;
struct ListNode* deleteDuplicates(struct ListNode* head){
//空链表就直接返回空指针
if(!head) // 注:在条件判断中,head == NULL 和 !head 所起的作用是一样的。
return NULL;
ListNode* cur = head;
while(cur->next && cur) // 注意循环条件
{
ListNode* next = cur->next;
//若相等,则删掉下一个节点
if(cur->val == next->val)
{
cur->next = next->next;
free(next);
}
//若不等,则将下一个节点变为当前节点
else
cur = cur->next;
}
return head;
}
需要注意的是循环条件不能只有 cur 非空 这一个条件:若只有这一个循环条件,当 cur 的下一个节点是空时,会在 if 语句判断时导致程序崩溃(原因是对空指针进行解引用)。
因此,当 cur 的下一个节点是空时,为了终止循环,再加上 cur->next 不为空 就行。
进阶
【题目描述】
给定一个已排序的链表的头 head , 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回已排序的链表 。
示例1:
输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]
示例2:
输入:head = [1,1,1,2,3]
输出:[2,3]
题目来源(力扣):删除排序链表中的重复元素 II
【基本思路】
根据题目以及示例的意思,与上面的题不同的是,这个是把相同值的节点都干掉。
遍历一次链表即可,根据是否有重复值的节点出现,可分为两种情况:
1)若出现,把整个重复值节点区间删掉。
2)若没有出现,则向后一步继续遍历。
构成迭代。
由于链表开头可能会出现连续几个相同值节点的删除,涉及第一个节点,因此设置哨兵位会更好,便于问题的解决。
【代码实现】
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;
}
链表的题一般有很多细节,做的时候需要多加注意,特别是有关指针的细节。
本专栏
有关链表的题目
其它专栏
有关数组的题目