反转链表
原题
给你单链表的头节点 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
进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?
思路
方法一:迭代
在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。
方法二:递归
递归其关键在于反向工作,假设链表的其余部分已经被反转,现在应该如何反转它前面的部分,
那么可以让nk+1下一个节点指向nk,即
代码实例
//方法一:迭代
struct ListNode* reverseList(struct ListNode* head) {
struct ListNode* prev = NULL; //反转链表,第一个应该指向的尾结点为空
struct ListNode* curr = head; //当前节点为头结点
while (curr) {
struct ListNode* next = curr->next; //移动指针到下一个
curr->next = prev; //将curr指向curr前面的那个prev
prev = curr; //往下走,prev变为curr
curr = next; //往下走,curr变成后面的next
}
return prev; //最后返回prev
}
反转链表 II
原题
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
输入:head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:输入:head = [5], left = 1, right = 1
输出:[5]
提示:
链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
进阶: 你可以使用一趟扫描完成反转吗?
思路
需要设置一个哨兵节点,用于left=1时
参照该视频 :反转链表【基础算法精讲 06】_哔哩哔哩_bilibili
class Solution { public: ListNode *reverseBetween(ListNode *head, int left, int right) { ListNode *dummy = new ListNode(0, head), *p0 = dummy; for (int i = 0; i < left - 1; ++i) p0 = p0->next; ListNode *pre = nullptr, *cur = p0->next; for (int i = 0; i < right - left + 1; ++i) { ListNode *nxt = cur->next; cur->next = pre; // 每次循环只修改一个 next,方便大家理解 pre = cur; cur = nxt; } // 见视频 p0->next->next = cur; p0->next = pre; return dummy->next; } };
力扣官方穿针引线
使用「206. 反转链表」的解法,反转 left 到 right 部分以后,再拼接起来。我们还需要记录 left 的前一个节点,和 right 的后一个节点。如图所示:
算法步骤:
第 1 步:先将待反转的区域反转;
第 2 步:把 pre 的 next 指针指向反转以后的链表头节点,把反转以后的链表的尾节点的 next 指针指向 succ。
代码实例
void reverseLinkedList(struct ListNode *head) {
// 也可以使用递归反转一个链表
struct ListNode *pre = NULL;
struct ListNode *cur = head;
while (cur != NULL) {
struct ListNode *next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
}
struct ListNode *reverseBetween(struct ListNode *head, int left, int right) {
// 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
struct ListNode *dummyNode = malloc(sizeof(struct ListNode));
dummyNode->val = -1;
dummyNode->next = head;
struct ListNode *pre = dummyNode;
// 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
// 建议写在 for 循环里,语义清晰
for (int i = 0; i < left - 1; i++) {
pre = pre->next;
}
// 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
struct ListNode *rightNode = pre;
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode->next;
}
// 第 3 步:切断出一个子链表(截取链表)
struct ListNode *leftNode = pre->next;
struct ListNode *curr = rightNode->next;
// 注意:切断链接
pre->next = NULL;
rightNode->next = NULL;
// 第 4 步:同第 206 题,反转链表的子区间
reverseLinkedList(leftNode);
// 第 5 步:接回到原来的链表中
pre->next = rightNode;
leftNode->next = curr;
return dummyNode->next;
}
奇偶链表
原题
给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。
第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。
请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。
你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。
示例 1:
输入: head = [1,2,3,4,5]
输出: [1,3,5,2,4]
示例 2:输入: head = [2,1,3,5,6,4,7]
输出: [2,3,6,7,1,5,4]
提示:
n == 链表中的节点数
0 <= n <= 104
-106 <= Node.val <= 106
思路
如果链表为空,则直接返回链表。
对于原始链表,每个节点都是奇数节点或偶数节点。头节点是奇数节点,头节点的后一个节点是偶数节点,相邻节点的奇偶性不同。因此可以将奇数节点和偶数节点分离成奇数链表和偶数链表,然后将偶数链表连接在奇数链表之后,合并后的链表即为结果链表。
代码实例
struct ListNode* oddEvenList(struct ListNode* head) {
if (head == NULL) {
return head;
}
//分离
struct ListNode* evenHead = head->next;
struct ListNode* odd = head; //奇节点
struct ListNode* even = evenHead; //偶节点
while (even != NULL && even->next != NULL) {
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
//结合
odd->next = evenHead; //奇节点的尾结点是偶节点
return head;
}
作
分隔链表
原题
给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1:
输入:head = [1,4,3,2,5,2], x = 3
输出:[1,2,2,4,3,5]
示例 2:输入:head = [2,1], x = 2
输出:[1,2]
提示:
链表中节点的数目在范围 [0, 200] 内
-100 <= Node.val <= 100
-200 <= x <= 200
思路
直观来说我们只需维护两个链表 small 和 large即可,small 链表按顺序存储所有小于 xxx 的节点,large 链表按顺序存储所有大于等于 xxx 的节点。遍历完原链表后,我们只要将 small链表尾节点指向 large链表的头节点即能完成对链表的分隔。
代码实例
struct ListNode* partition(struct ListNode* head, int x) {
struct ListNode* small = malloc(sizeof(struct ListNode));
struct ListNode* smallHead = small;
struct ListNode* large = malloc(sizeof(struct ListNode));
struct ListNode* largeHead = large;
while (head != NULL) {
if (head->val < x) {
small->next = head;
small = small->next;
} else {
large->next = head;
large = large->next;
}
head = head->next;
}
large->next = NULL;
small->next = largeHead->next;
return smallHead->next;
}