連結串列排序(冒泡、選擇、插入、快排、歸併、希爾、堆排序)
參考http://www.cnblogs.com/TenosDoIt/p/3666585.html
插入排序(演算法中是直接交換節點,時間複雜度O(n^2),空間複雜度O(1))
1 class Solution { 2 public: 3 ListNode *insertionSortList(ListNode *head) { 4 // IMPORTANT: Please reset any member data you declared, as 5 // the same Solution instance will be reused for each test case.6 if(head == NULL || head->next == NULL)return head; 7 ListNode *p = head->next, *pstart = new ListNode(0), *pend = head; 8 pstart->next = head; //為了操作方便,新增一個頭結點 9 while(p != NULL) 10 { 11 ListNode *tmp = pstart->next, *pre = pstart; 12while(tmp != p && p->val >= tmp->val) //找到插入位置 13 {tmp = tmp->next; pre = pre->next;} 14 if(tmp == p)pend = p; 15 else 16 { 17 pend->next = p->next; 18 p->next = tmp; 19 pre->next = p;20 } 21 p = pend->next; 22 } 23 head = pstart->next; 24 delete pstart; 25 return head; 26 } 27 };
選擇排序(演算法中只是交換節點的val值,時間複雜度O(n^2),空間複雜度O(1))
1 class Solution { 2 public: 3 ListNode *selectSortList(ListNode *head) { 4 // IMPORTANT: Please reset any member data you declared, as 5 // the same Solution instance will be reused for each test case. 6 //選擇排序 7 if(head == NULL || head->next == NULL)return head; 8 ListNode *pstart = new ListNode(0); 9 pstart->next = head; //為了操作方便,新增一個頭結點 10 ListNode*sortedTail = pstart;//指向已排好序的部分的尾部 11 12 while(sortedTail->next != NULL) 13 { 14 ListNode*minNode = sortedTail->next, *p = sortedTail->next->next; 15 //尋找未排序部分的最小節點 16 while(p != NULL) 17 { 18 if(p->val < minNode->val) 19 minNode = p; 20 p = p->next; 21 } 22 swap(minNode->val, sortedTail->next->val); 23 sortedTail = sortedTail->next; 24 } 25 26 head = pstart->next; 27 delete pstart; 28 return head; 29 } 30 };
快速排序1(演算法只交換節點的val值,平均時間複雜度O(nlogn),不考慮遞迴棧空間的話空間複雜度是O(1))
這裡的partition我們參考陣列快排partition的第二種寫法(選取第一個元素作為樞紐元的版本,因為連結串列選擇最後一元素需要遍歷一遍),具體可以參考here
這裡我們還需要注意的一點是陣列的partition兩個引數分別代表陣列的起始位置,兩邊都是閉區間,這樣在排序的主函式中:
void
quicksort(vector<
int
>&arr,
int
low,
int
high)
{
if
(low < high)
{
int
middle = mypartition(arr, low, high);
quicksort(arr, low, middle-1);
quicksort(arr, middle+1, high);
}
}
對左邊子陣列排序時,子陣列右邊界是middle-1,如果連結串列也按這種兩邊都是閉區間的話,找到分割後樞紐元middle,找到middle-1還得再次遍歷陣列,因此連結串列的partition採用前閉後開的區間(這樣排序主函式也需要前閉後開區間),這樣就可以避免上述問題
1 class Solution { 2 public: 3 ListNode *quickSortList(ListNode *head) { 4 // IMPORTANT: Please reset any member data you declared, as 5 // the same Solution instance will be reused for each test case. 6 //連結串列快速排序 7 if(head == NULL || head->next == NULL)return head; 8 qsortList(head, NULL); 9 return head; 10 } 11 void qsortList(ListNode*head, ListNode*tail) 12 { 13 //連結串列範圍是[low, high) 14 if(head != tail && head->next != tail) 15 { 16 ListNode* mid = partitionList(head, tail); 17 qsortList(head, mid); 18 qsortList(mid->next, tail); 19 } 20 } 21 ListNode* partitionList(ListNode*low, ListNode*high) 22 { 23 //連結串列範圍是[low, high) 24 int key = low->val; 25 ListNode* loc = low; 26 for(ListNode*i = low->next; i != high; i = i->next) 27 if(i->val < key) 28 { 29 loc = loc->next; 30 swap(i->val, loc->val); 31 } 32 swap(loc->val, low->val); 33 return loc; 34 } 35 };
快速排序2(演算法交換連結串列節點,平均時間複雜度O(nlogn),不考慮遞迴棧空間的話空間複雜度是O(1))
這裡的partition,我們選取第一個節點作為樞紐元,然後把小於樞紐的節點放到一個鏈中,把不小於樞紐的及節點放到另一個鏈中,最後把兩條鏈以及樞紐連線成一條鏈。
這裡我們需要注意的是,1.在對一條子鏈進行partition時,由於節點的順序都打亂了,所以得保正重新組合成一條新連結串列時,要和該子連結串列的前後部分連線起來,因此我們的partition傳入三個引數,除了子連結串列的範圍(也是前閉後開區間),還要傳入子連結串列頭結點的前驅;2.partition後連結串列的頭結點可能已經改變
class Solution { public: ListNode *quickSortList(ListNode *head) { // IMPORTANT: Please reset any member data you declared, as // the same Solution instance will be reused for each test case. //連結串列快速排序 if(head == NULL || head->next == NULL)return head; ListNode tmpHead(0); tmpHead.next = head; qsortList(&tmpHead, head, NULL); return tmpHead.next; } void qsortList(ListNode *headPre, ListNode*head, ListNode*tail) { //連結串列範圍是[low, high) if(head != tail && head->next != tail) { ListNode* mid = partitionList(headPre, head, tail);//注意這裡head可能不再指向連結串列頭了 qsortList(headPre, headPre->next, mid); qsortList(mid, mid->next, tail); } } ListNode* partitionList(ListNode* lowPre, ListNode* low, ListNode* high) { //連結串列範圍是[low, high) int key = low->val; ListNode node1(0), node2(0);//比key小的鏈的頭結點,比key大的鏈的頭結點 ListNode* little = &node1, *big = &node2; for(ListNode*i = low->next; i != high; i = i->next) if(i->val < key) { little->next = i; little = i; } else { big->next = i; big = i; } big->next = high;//保證子連結串列[low,high)和後面的部分連線 little->next = low; low->next = node2.next; lowPre->next = node1.next;//為了保證子連結串列[low,high)和前面的部分連線 return low; } };
歸併排序(演算法交換連結串列節點,時間複雜度O(nlogn),不考慮遞迴棧空間的話空間複雜度是O(1)) 本文地址
首先用快慢指標的方法找到連結串列中間節點,然後遞迴的對兩個子連結串列排序,把兩個排好序的子連結串列合併成一條有序的連結串列。歸併排序應該算是連結串列排序最佳的選擇了,保證了最好和最壞時間複雜度都是nlogn,而且它在陣列排序中廣受詬病的空間複雜度在連結串列排序中也從O(n)降到了O(1)
class Solution { public: ListNode *mergeSortList(ListNode *head) { // IMPORTANT: Please reset any member data you declared, as // the same Solution instance will be reused for each test case. //連結串列歸併排序 if(head == NULL || head->next == NULL)return head; else { //快慢指標找到中間節點 ListNode *fast = head,*slow = head; while(fast->next != NULL && fast->next->next != NULL) { fast = fast->next->next; slow = slow->next; } fast = slow; slow = slow->next; fast->next = NULL; fast = sortList(head);//前半段排序 slow = sortList(slow);//後半段排序 return merge(fast,slow); } } // merge two sorted list to one ListNode *merge(ListNode *head1, ListNode *head2) { if(head1 == NULL)return head2; if(head2 == NULL)return head1; ListNode *res , *p ; if(head1->val < head2->val) {res = head1; head1 = head1->next;} else{res = head2; head2 = head2->next;} p = res; while(head1 != NULL && head2 != NULL) { if(head1->val < head2->val) { p->next = head1; head1 = head1->next; } else { p->next = head2; head2 = head2->next; } p = p->next; } if(head1 != NULL)p->next = head1; else if(head2 != NULL)p->next = head2; return res; } };
氣泡排序(演算法交換連結串列節點val值,時間複雜度O(n^2),空間複雜度O(1))
class Solution { public: ListNode *bubbleSortList(ListNode *head) { // IMPORTANT: Please reset any member data you declared, as // the same Solution instance will be reused for each test case. //連結串列快速排序 if(head == NULL || head->next == NULL)return head; ListNode *p = NULL; bool isChange = true; while(p != head->next && isChange) { ListNode *q = head; isChange = false;//標誌當前這一輪中又沒有發生元素交換,如果沒有則表示陣列已經有序 for(; q->next && q->next != p; q = q->next) { if(q->val > q->next->val) { swap(q->val, q->next->val); isChange = true; } } p = q; } return head; } };