1、題目描述
在 O(n log n) 時間複雜度和常數級空間複雜度下,對鏈表進行排序。
2、示例
輸入: 4->2->1->3
輸出: 1->2->3->4
3、題解
這道題限定時間複雜度O(nlogn),排序中只有快速排序、歸併排序和堆排序是這種時間複雜度。
解法一:
基本思想:歸併排序,不斷分割鏈表直至每個節點都斷開,然後不斷兩兩合併成有序鏈表。
- 技巧在於建立start節點,這樣返回start->next就是排序後的頭節點
- 快慢指針找到鏈表的中間節點,pre指向中間節點的前一個節點用來斷開鏈表
- 遞歸函數不斷返回合併後鏈表的頭節點
解法二:
基本思想:快速排序,以一個節點作爲樞軸,將小於樞軸的節點放到左邊大於樞軸的節點放到右邊,然後繼續遞歸除樞軸外兩邊的節點。
- 技巧在於建立start節點,這樣返回start->next就是排序後的頭節點
- 快速排序head->next到tail的節點,其中head節點是爲了方便比樞軸小的節點前移同時可以保持樞軸左右兩邊處於連接狀態,屬於無效節點不需要排序
- pre指向樞軸的左邊最後一個節點,pivot爲樞軸取第一個節點,end指向樞軸的右邊最後一個節點
- 遍歷樞軸右邊所有的節點,比樞軸小的全部放到樞軸的前面,比樞軸大的依然放在樞軸的後面,繼續遍歷下一個節點
- 保持左右兩邊連接,繼續遞歸樞軸左邊的節點。繼續遞歸樞軸右邊的節點。head是無效節點,最後返回head->next。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
};
void Init_ListNode(ListNode* head, vector<int> vec)
{
if (vec.size() == 0)
{
head = NULL;
return;
}
ListNode* p;
p = head;
p->val = vec[0];
for (int i = 1; i < vec.size(); i++)
{
ListNode* q = new ListNode(vec[i]);
p->next = q;
p = p->next;
}
p->next = NULL;
}
class Solution {
public:
ListNode* sortList(ListNode* head) {
//基本思想:歸併排序,不斷分割鏈表直至每個節點都斷開,然後不斷兩兩合併成有序鏈表
if (head == NULL)
return NULL;
return MergeSort(head);
}
ListNode* Merge(ListNode* l1, ListNode* l2)
{
//技巧在於建立start節點,這樣返回start->next就是排序後的頭節點
ListNode* start = new ListNode(0), * cur;
cur = start;
//合併鏈表l1和l2
while (l1 != NULL && l2 != NULL)
{
if (l1->val < l2->val)
{
cur->next = l1;
l1 = l1->next;
}
else
{
cur->next = l2;
l2 = l2->next;
}
cur = cur->next;
}
cur->next = l1 == NULL ? l2 : l1;
return start->next;
}
ListNode* MergeSort(ListNode* head)
{
if (head ->next == NULL)
return head;
else
{
//快慢指針找到鏈表的中間節點,pre指向中間節點的前一個節點用來斷開鏈表
ListNode* fast = head, * slow = head, * pre =head;
while (true)
{
if (fast == NULL || fast->next == NULL)
break;
pre = slow;
slow = slow->next;
fast = fast->next->next;
}
pre->next = NULL;
return Merge(MergeSort(head), MergeSort(slow));
}
}
};
class Solution1 {
public:
ListNode* sortList(ListNode* head) {
//基本思想:快速排序,以一個節點作爲樞軸,將小於樞軸的節點放到左邊大於樞軸的節點放到右邊,然後繼續遞歸除樞軸外兩邊的節點
if (head == NULL)
return NULL;
//技巧在於建立start節點,這樣返回start->next就是排序後的頭節點
ListNode* tail = head, * start = new ListNode(0);
start->next = head;
while (tail->next != NULL)
tail = tail->next;
return QuickSort(start, tail);
}
//快速排序head->next到tail的節點,其中head節點是爲了方便比樞軸小的節點前移同時可以保持樞軸左右兩邊處於連接狀態,屬於無效節點不需要排序
ListNode* QuickSort(ListNode* head, ListNode* tail)
{
if (head->next == tail || head == tail)
return tail;
else
{
//pre指向樞軸的左邊最後一個節點,pivot爲樞軸取第一個節點,end指向樞軸的右邊最後一個節點
ListNode* pre = head, * pivot = head->next, * end = head->next;
//遍歷樞軸右邊所有的節點
while (end->next != tail->next)
{
//比樞軸小的全部放到樞軸的前面
if (end->next->val < pivot->val)
{
ListNode* cur = end->next;
end->next = cur->next;
pre->next = cur;
pre = pre->next;
}
//比樞軸大的依然放在樞軸的後面,繼續遍歷下一個節點
else
end = end->next;
}
pre->next = pivot; //保持左右兩邊連接
QuickSort(head, pre); //繼續遞歸樞軸左邊的節點
QuickSort(pivot, end); //繼續遞歸樞軸右邊的節點
return head->next; //head是無效節點,最後返回head->next
}
}
};
int main()
{
Solution1 solute;
ListNode* head = new ListNode(0);
vector<int> vec = { -1,5,3,4,0 };
//初始化鏈表
Init_ListNode(head, vec);
ListNode* p = solute.sortList(head);
while (p != NULL)
{
cout << p->val << " ";
p = p->next;
}
cout << endl;
return 0;
}