1. 程式人生 > >對連結串列進行排序(不能額外空間,時間為O(NL))

對連結串列進行排序(不能額外空間,時間為O(NL))

Sort a linked list in O(n log n) time using constant space complexity.         對一個連結串列進行排序,且時間複雜度要求為 O(n log n) ,空間複雜度為常量。一看到 O(n log n) 的排序,首先應該想到歸併排序和快速排序,但是通常我們使用這兩種排序方法時都是針對陣列的,現在是連結串列了。         歸併排序法:在動手之前一直覺得空間複雜度為常量不太可能,因為原來使用歸併時,都是 O(N)的,需要複製出相等的空間來進行賦值歸併。對於連結串列,實際上是可以實現常數空間佔用的。利用歸併的思想,遞迴地將當前連結串列分為兩段,然後merge,分兩段的方法是使用 fast-slow 法,用兩個指標,一個每次走兩步,一個走一步,知道快的走到了末尾,然後慢的所在位置就是中間位置,這樣就分成了兩段。merge時,把兩段頭部節點值比較,用一個 p 指向較小的,且記錄第一個節點,然後 兩段的頭一步一步向後走,p也一直向後走,總是指向較小節點,直至其中一個頭為NULL,處理剩下的元素。最後返回記錄的頭即可。程式碼如下: [cpp] view plaincopyprint?
  1. class Solution {  
  2. public:  
  3.     ListNode *sortList(ListNode *head) {  
  4.         if(!head||!head->next)  
  5.             return head;  
  6.         return mergeSort(head);  
  7.     }  
  8.     ListNode * mergeSort(ListNode *head){  
  9.         if(!head||!head->next)   //just one element
  10.             return head;  
  11.         ListNode *p=head, *q=head, *pre=NULL;  
  12.         while(q&&q->next!=NULL){  
  13.             q=q->next->next;  
  14.             pre=p;  
  15.             p=p->next;  //divide into two parts
  16.         }  
  17.         pre->next=NULL;  
  18.         ListNode *lhalf=mergeSort(head);  
  19.         ListNode *rhalf=mergeSort(p);  //recursive
  20.         return merge(lhalf, rhalf);   //merge
  21.     }  
  22.     ListNode * merge(ListNode *lh, ListNode *rh){  
  23.         ListNode *temp=new ListNode(0);  
  24.         ListNode *p=temp;  
  25.         while(lh&&rh){  
  26.             if(lh->val<=rh->val){  
  27.                 p->next=lh;  
  28.                 lh=lh->next;  
  29.             }  
  30.             else{  
  31.                 p->next=rh;  
  32.                 rh=rh->next;  
  33.             }  
  34.             p=p->next;  
  35.         }  
  36.         if(!lh)  
  37.             p->next=rh;  
  38.         else
  39.             p->next=lh;  
  40.         p=temp->next;  
  41.         temp->next=NULL;  
  42.         delete temp;  
  43.         return p;  
  44.     }  
  45. };  


            快速排序:使用快排也行,就是每次利用頭節點作為基準,將小於它的交換到連結串列左側,大於它的放在右側,最後把它換到中間某個位置,然後遞迴處理左側和右側。但是相對於歸併來說,它的元素交換次數太多了,可能會超時。