在 O(n log n) 時間複雜度和常數級空間複雜度下,對鏈表進行排序。
示例 1:
輸入: 4->2->1->3
輸出: 1->2->3->4
示例 2:
輸入: -1->5->3->4->0
輸出: -1->0->3->4->5
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/sort-list
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
完整代碼
看到時間複雜度爲O(nlogn)的要求,就考慮用快排來實現,但是傳統的快排(以第一個元素爲基準)無法實現鏈表的移動,這裏是單向鏈表。採用瞭如下的移動方法(以第一個元素爲基準)
快排
#include<bits/stdc++.h>
using namespace std;
void swap(int &a, int &b){//交換兩個元素
int temp = a;
a = b;
b = temp;
}
void quickSort(vector<int> &nums, int s, int e){
if(s >= e)//遞歸終止條件
return;
int flag = nums[e];
int i = s - 1;
for(int j = s; j < e; ++j){
if(nums[j] < flag){
swap(nums[j], nums[++i]);
}
}
swap(nums[++i], nums[e]);
quickSort(nums, s, i - 1);
quickSort(nums, i + 1, e);
}
int main(){
vector<int> nums = {4, 2, 3, 1};
quickSort(nums, 0, nums.size() - 1);
for(auto a : nums)
cout << a << " ";
cout <<"Dsa";
return 0;
}
基本思想
快排
- 交換兩個節點時, 這裏只是交換的鏈表的值,並沒有改變鏈表的指向
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
//快排來實現
if(head == NULL || head->next == NULL)
return head;
ListNode* n_head = new ListNode(0), *end_Node;
n_head->next = head;//指向真正的頭節點的節點
ListNode* p = head;
while(p){//尾節點
end_Node = p;
p = p->next;
}
fastSort(n_head, end_Node);
return n_head->next;
}
private:
void fastSort(ListNode* n_head, ListNode* end_Node){
if(n_head == end_Node)//遞歸結束的條件
return;
ListNode* flag = end_Node;
ListNode* res = n_head, *left = n_head->next;
while(left != end_Node){
if(left->val < end_Node->val){
res = res->next;
swap(res, left);//所需交換的兩個節點的前驅
}
left = left->next;
}
//res是左半部分末尾的節點
swap(end_Node, res->next);//已經排好序的值放在了res->next中
fastSort(n_head, res);
fastSort(res->next, end_Node);
}
void swap(ListNode* a, ListNode* b){//交換兩個節點的值
int temp = a->val;
a->val = b->val;
b->val = temp;
}
};
在排序算法中除了快排,還有歸併排序的時間複雜度也是O(nlogn)
歸併排序
參考:添加鏈接描述
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
//歸併排序
if(head == NULL || head->next == NULL)
return head;
ListNode *slow = head, *fast = head->next;
while(fast != NULL && fast->next != NULL){//尋找中間結點
slow = slow->next;
fast = fast->next->next;
}
ListNode* right_head = slow->next;
slow->next = NULL;
ListNode* left = sortList(head);
ListNode* right = sortList(right_head);
//歸併鏈表
ListNode* dummy = new ListNode(0), *p;
p = dummy;
while(left && right){
if(left->val < right->val){
p->next = left;
left = left->next;
}
else{
p->next = right;
right = right->next;
}
p = p->next;
}
if(left)
p->next = left;
if(right)
p->next = right;
return dummy->next;
}
};
非遞歸形式的歸併排序
基本思想:
對於整個鏈表而言,先兩兩合併,再四四合並,知道待合併的長度大於等於鏈表長度時不再合併
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* sortList(ListNode* head) {
//歸併:從低向上,非遞歸形式
if(head == NULL || head->next == NULL)
return head;
//1.統計鏈表長度
int len = 0;
ListNode *p = head;
while(p){
p = p->next;
++len;
}
int intv = 1;//間隔以2的整數次冪的方式遞增
ListNode *res = new ListNode(0);
res->next = head;
while(intv < len){
ListNode *pre = res;
p = res->next;
while(p){//將間隔爲intv的鏈表合併
//第一段鏈表
ListNode* h1 = p;
int i = intv;
while(i > 0 && p){
p = p->next;
i--;
}
if(p == NULL)//沒有第二段鏈表,無需合併
break;
//第二段鏈表,(長度不一定是intv)
ListNode* h2 = p;
i = intv;
while(i > 0 && p){
p = p->next;
i--;
}
//合併兩段鏈表
int l1 = intv, l2 = intv - i;
while(l1 > 0 && l2 > 0){
if(h1->val < h2->val){
pre->next = h1;
h1 = h1->next;
--l1;
}
else{
pre->next = h2;
h2 = h2->next;
--l2;
}
pre = pre->next;
}
while(l1){
pre->next = h1;
pre = pre->next;
h1 = h1->next;
--l1;
}
while(l2){
pre->next = h2;
pre = pre->next;
h2 = h2->next;
--l2;
}
// pre->next = (l1 == 0) ? h2 : h1;
// while (l1 > 0 || l2 > 0) {
// pre = pre->next;
// l1--;
// l2--;
// }
pre->next = p;//將已排好序的那一步分和後面的連接上
}
intv *= 2;
}
return res->next;
}
};