删除排序链表中的重复元素(C语言)
初阶
【题目描述】 给定一个已排序的链表的头 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]
【基本思路】 根据题目以及示例的意思,与上面的题不同的是,这个是把相同值的节点都干掉。 遍历一次链表即可,根据是否有重复值的节点出现,可分为两种情况: 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; }
链表的题一般有很多细节,做的时候需要多加注意,特别是有关指针的细节。
本专栏
其它专栏
下一篇:
cursor自动生成代码使用