題目:重排鏈表
給定一個單鏈表 L:L0→L1→…→Ln-1→Ln ,
將其重新排列後變爲: L0→Ln→L1→Ln-1→L2→Ln-2→…
你不能只是單純的改變節點內部的值,而是需要實際的進行節點交換。
示例 1:
給定鏈表 1->2->3->4, 重新排列爲 1->4->2->3.
示例 2:
給定鏈表 1->2->3->4->5, 重新排列爲 1->5->2->4->3.
方法1:遍歷一遍鏈表,把結點存入棧中,然後再遍歷一次鏈表,將棧中的結點依次插到鏈表前面;
當遍歷到的結點或它的下一結點和棧中的結點相同時,結束插入,同時斷開剩下的結點的連接
時間複雜度 O(n) 空間複雜度 O(n)
void reorderList(ListNode* head) {
if(head == nullptr || head->next == nullptr || head->next->next == nullptr) return;
stack<ListNode* > nodes;
ListNode* p = head;
while(p){ // 先將結點存入棧中
nodes.push(p);
p = p->next;
}
p = head;
while(p && !nodes.empty()){ // 將棧中的結點插到鏈表前面
ListNode* tmp = nodes.top();// 取棧頂結點
nodes.pop();
if(p == tmp){ // 鏈表長度爲奇數的情況,當前結點與棧頂結點相同則結束
p->next = nullptr;
break;
}else if(p->next == tmp){ // 鏈表長度爲偶數的情況,當前結點的下一節點與棧頂結點相同則結束(不需要再插入了)
p->next->next = nullptr;
break;
}else{ // 插入鏈表
tmp->next = p->next;
p->next = tmp;
p = tmp->next;
}
}
}
方法2:先找到鏈表中點,從中點斷開鏈表,然後翻轉中點之後的鏈表,最後遍歷倆鏈表合併爲一個鏈表
時間複雜度 O(n) 空間複雜度 O(1)
void reorderList(ListNode* head) {
if(head == nullptr || head->next == nullptr || head->next->next == nullptr) return;
ListNode* mid_node = findMidOfList(head); // 找中點
ListNode* right_head = mid_node->next;
mid_node->next = nullptr; // 斷開中點之後的鏈表
right_head = reverseList(right_head); // 翻轉右半部分
ListNode* left_p = head; // 用於遍歷左半部鏈表
ListNode* right_p = right_head; // 用於遍歷右半部鏈表
while(left_p && right_p){ // 合併倆鏈表
ListNode* temp_next1 = left_p->next; // 暫存下一結點
ListNode* temp_next2 = right_p->next; // 暫存下一結點
left_p->next = right_p; // 右部分鏈表的結點插入左部分結點之後
right_p->next = temp_next1;
left_p = temp_next1; // 指針後移
right_p = temp_next2;
}
}
// 查找鏈表的中間結點並返回
ListNode* findMidOfList(ListNode* head){
ListNode* slow = head;
ListNode* fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
// 翻轉鏈表,返回翻轉後鏈表的頭結點
ListNode* reverseList(ListNode* head){
ListNode* pre_node = nullptr;
ListNode* cur_node = head;
while(cur_node){
ListNode* next_node = cur_node->next;
cur_node->next = pre_node;
pre_node = cur_node;
cur_node = next_node;
}
return pre_node;
}
方法3:遞歸法,遞歸地斷開最後一個結點,直到鏈表只剩一個或兩個節點,然後層層返回開始拼接
時間複雜度較高 約爲 O(n^2) 空間複雜度約爲 O(n), 因爲每次遞歸減少兩個節點(首尾結點),遞歸深度近似爲 n。
void reorderList(ListNode* head){
if(head == nullptr || head->next == nullptr) return;
ListNode* pre = nullptr;
ListNode* p = head;
while(p->next){ // p 遍歷到最後一個結點,pre 遍歷到倒數第二個結點
pre = p;
p = p->next;
}
pre->next = nullptr; // 斷開最後一個結點
ListNode* temp_next = head->next; // 暫存頭結點的下一節點,便於拼接
reorderList(temp_next);
head->next = p;
p->next = temp_next;
}