1 删除排序链表中的重复元素 II
给定一个已排序的链表的头 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
- 题目数据保证链表已经按升序 排列
思路:
检查空链表:如果链表为空,直接返回空链表。
创建虚拟头节点:虚拟头节点的 next 指针指向原链表的头节点。
遍历链表:使用 cur 指针遍历链表(需要注意 cur.next 以及 cur.next.next 可能为空节点 所以其循环结束条件是两者都不可以为空),检查当前节点和下一个节点的值(cur.next 与 cur.next.next)是否相等。 处理重复节点:如果发现重复节点,记录重复节点的值,并删除所有值相同的节点 返回结果:返回虚拟头节点的 next 指针,即处理后的链表头节点。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head) {
// 如果链表为空,直接返回空链表
if (!head) {
return head;
}
// 创建一个虚拟头节点,便于处理可能出现在头节点的重复情况
struct ListNode* dummy = malloc(sizeof(struct ListNode));
dummy->next = head;
// 定义一个指针 cur,用于遍历链表,初始指向虚拟头节点
struct ListNode* cur = dummy;
// 遍历链表,当 cur 的下一个节点和下下一个节点都存在时继续循环
while (cur->next && cur->next->next) {
// 如果当前节点的值和下一个节点的值相等,说明有重复
if (cur->next->val == cur->next->next->val) {
// 记录重复的值
int x = cur->next->val;
// 删除所有值为 x 的节点
while (cur->next && cur->next->val == x) {
cur->next = cur->next->next;
}
} else {
// 如果没有重复,则继续移动 cur 指针
cur = cur->next;
}
}
// 返回虚拟头节点的下一个节点,即处理后的链表头节点
return dummy->next;
}
2移除链表元素
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
示例 1:
输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2:
输入:head = [], val = 1 输出:[]
示例 3:
输入:head = [7,7,7,7], val = 7 输出:[]
提示:
- 列表中的节点数目在范围
[0, 104]
内 1 <= Node.val <= 50
0 <= val <= 50
思路:
- 创建虚拟头节点:虚拟头节点的
next
指针指向原链表的头节点。 - 遍历链表:使用
cur
指针遍历链表,检查当前节点的下一个节点的值是否等于目标值val
。 - 删除目标值节点:如果发现目标值节点,记录该节点并删除它,释放其内存。
- 更新链表头:更新链表头节点为虚拟头节点的
next
指针。 - 释放虚拟头节点:释放虚拟头节点的内存。
- 返回结果:返回处理后的链表头节点。
代码:
struct ListNode* removeElements(struct ListNode* head, int val) {
// 创建一个虚拟头节点,用于简化删除操作
struct ListNode* dummy;
dummy = (struct ListNode*)malloc(sizeof(struct ListNode));
dummy->next = head; // 虚拟头节点的下一个节点指向原链表的头节点
// 定义一个指针 cur,用于遍历链表,初始指向虚拟头节点
struct ListNode* cur = dummy;
// 遍历链表,直到 cur 的下一个节点为空
while (cur->next != NULL) {
// 如果当前节点的下一个节点的值等于目标值 val
if (cur->next->val == val) {
// 记录需要删除的节点
struct ListNode* temp = cur->next;
// 跳过需要删除的节点
cur->next = cur->next->next;
// 释放被删除节点的内存
free(temp);
} else {
// 如果当前节点的下一个节点的值不等于目标值 val,继续遍历
cur = cur->next;
}
}
// 更新链表头节点为虚拟头节点的下一个节点
head = dummy->next;
// 释放虚拟头节点的内存
free(dummy);
// 返回处理后的链表头节点
return head;
}
3反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
示例 1:
输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2] 输出:[2,1]
示例 3:
输入:head = [] 输出:[]
提示:
- 链表中节点的数目范围是
[0, 5000]
-5000 <= Node.val <= 5000
思路:
- 初始化指针:使用
pre
指针指向当前节点的前一个节点,初始为NULL
。 - 遍历链表:使用
head
指针遍历链表。 - 保存下一个节点:在每次迭代中,保存当前节点的下一个节点到
temp
。 - 反转操作:将当前节点的
next
指针指向pre
,完成反转。 - 更新指针:更新
pre
指针为当前节点,更新head
指针为下一个节点。 - 返回结果:当
head
为NULL
时,遍历结束,返回pre
指针,即反转后的链表头节点。
代码:
struct ListNode* reverseList(struct ListNode* head) {
// 保存当前节点的下一个节点
struct ListNode* temp;
// pre指针指向前一个节点,初始为NULL
struct ListNode* pre = NULL;
// 用head代替cur,也可以再定义一个cur节点指向head
while (head) {
// 保存下一个节点的位置
temp = head->next;
// 翻转操作:将当前节点的next指针指向前一个节点
head->next = pre;
// 更新pre指针,使其指向当前节点
pre = head;
// 更新head指针,使其指向下一个节点
head = temp;
}
// 返回翻转后的链表头节点
return pre;
}