143.重排链表
1.题目介绍
给定一个单链表 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]
2.题解
2.1 线性表(首尾指针)
思路
用数组存储所有节点指针,然后双指针(首尾指针)重排链表即可,注意最后i==j的时候,将链表末尾->next = nullptr;
若是奇数,正常i++ , j--后最终达到\(i==j\)
若是偶数,在某次i++后就达到了\(i==j\),必须要break跳出循环!!!
代码
/**
* 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;
vector<ListNode*> vec;
ListNode* node = head;
while(node != nullptr){
vec.push_back(node);
node = node->next;
}
ListNode *dummyHead = new ListNode(0);
int i = 0, j = vec.size() - 1;
while(i < j){
vec[i]->next = vec[j];
i++;
if(i == j) break;
vec[j]->next = vec[i];
j--;
}
vec[j]->next = nullptr;
}
};
2.2 寻找链表中点 + 链表逆序 + 合并链表
思路
这里主要的思路是将链表拆成两份,将后面一份链表逆序,之后再合并两链表即可
1.获取中间节点,这里其实说的是第一份链表的最后一个节点而不是第二份链表第一个节点!!!这样一来方便链表1断开与后续链表连接,链表2头结点用next也可以得到
而这里的条件为 while(fast != nullptr && fast->next != nullptr)
或者 while(fast->next != nullptr && fast->next->next != nullptr)
均可
- 1.1 当使用 while(fast != nullptr && fast->next != nullptr):
偶数情况时,触发fast != nullptr
,其实mid指向的是中间偏后一个节点,也就是本来第二个链表第一个节点,但由于后面第二个链表逆序,合并链表后这个节点还是放在最后一个,所以这里将其提前放在第一个链表的最后一个也是可以的,实际上偷懒少合并一个已知必定是尾结点的节点(不推荐)成立!
若是奇数情况,触发fast->next != nullptr
;此时mid恰好指向链表的中间节点,第一个链表比第二个链表长度大一,这时第二个链表逆序融入到第一个链表中,成立 - 1.2 当使用
while(fast->next != nullptr && fast->next->next != nullptr):
偶数情况:触发fast->next->next != nullptr
,mid指向第一个链表最后一个,正常合并即可
技术情况:触发fast->next != nullptr;
同上 - 1.3(错误做法) 使用
while(fast != nullptr){slow = slow->next; fast = fast->next; if(fast!=nullptr) fast = fast->next;}
偶数情况:同1.1,恰好成立
奇数情况: mid指向的也是实际中点位置后面一个,这里就不满足偶数时的那种特殊情况了,所以导致结果错误!!!
2.获取两链表头节点,同时断开链表1和后续节点的连接
3.反转链表2,经典的三段式反转:prev,curr,next
4.合并链表,这里与其使用一个虚拟头结点来合并,不如直接使用原有链表来合并,不然还要考虑到最后l1多出一个,还得加上去比较麻烦。使用l1,l2本来就带着这最后一个就没问题了。但是必须注意这里需要提前保存l1->next;l2->next;它们的值都发生了改变!!!
代码
/**
* 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* fast = head, *slow = 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, *curr = head;
while (curr != nullptr){
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
// 交替合并链表
void mergeList(ListNode* l1, ListNode* l2) {
// ListNode *dummyHead = new ListNode(0);
// ListNode *curr = dummyHead;
// while(l1 != nullptr && l2 != nullptr){
// curr->next = l1;
// curr = l1;
// l1 = l1->next;
//
// curr->next = l2;
// curr = l2;
// l2 = l2->next;
// }
while(l1 != nullptr && l2 != nullptr){
ListNode* l1_temp = l1->next, *l2_temp = l2->next;
l1->next = l2;
l1 = l1_temp;
l2->next = l1;
l2 = l2_temp;
}
}
};
};