[143. 重排链表]
「这是我参与2022首次更文挑战的第26天,活动详情查看:2022首次更文挑战」。
题目描述
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例
示例1 :
输入: head = [1,2,3,4]
输出: [1,4,2,3]
示例 2:
输入: head = [1,2,3,4,5]
输出: [1,5,2,4,3]
提示:
- 链表的长度范围为
[1, 5 * 104] 1 <= node.val <= 1000
思路
这还是一道属于双指针在链表上使用的题,如果是在线性表的话,直接从头从尾开始,交错合并为新的线性表即可。但是是在链表上,没有办法从头开始遍历,当然也可以将链表转化为线性表后处理。这里我们来看一下不用转怎么做。我们来看一下线性表的做法。头指针向后遍历。链表同样可以做到。尾指针向前遍历,相当于将链表的后半截翻转,在从中间开始向后遍历,最后再将俩指针指向的链表合并,这样就可以了。
代码实现
按以上思路,需要实现找中间节点,翻转链表,合并链表的操作,具体的做法在之前的文章已有说明,这里不在复述,细节见代码注释。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
void reorderList(ListNode* head) {
if (head == nullptr) {
return;
}
ListNode* mid = middleNode(head);
ListNode* l1 = head;
ListNode* l2 = mid->next;
mid->next = nullptr; //将原链表切断,方便下来翻转
l2 = reverseList(l2); //翻转
mergeList(l1, l2); //合并
}
ListNode* middleNode(ListNode* head) { //快慢指针找中间节点
ListNode* slow = head;
ListNode* fast = head;
while (fast->next != nullptr && fast->next->next != nullptr) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
ListNode* reverseList(ListNode* head) { //翻转链表
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr != nullptr) {
ListNode* nextTemp = curr->next;
curr->next = prev;
prev = curr;
curr = nextTemp;
}
return prev;
}
void mergeList(ListNode* l1, ListNode* l2) { //合并链表
ListNode* l1_tmp;
ListNode* l2_tmp;
while (l1 != nullptr && l2 != nullptr) {
l1_tmp = l1->next;
l2_tmp = l2->next;
l1->next = l2;
l1 = l1_tmp;
l2->next = l1;
l2 = l2_tmp;
}
}
};
总结
在链表上使用双指针,综合使用了链表上的多种操作。