算法就是這麼一回事(排序)(第三部分)

6、快速排序ios

  快速排序是經過一種把集合中的元素按照第一個元素(這個是動態過程變化)做爲標杆來分爲兩部分,前面一部分比他小(或等),後面一部分比它大。而後就是經過適當的程序來遞歸這個過程,當最後沒有交換說明須要退出遞歸。算法

  上圖數據結構

  

快速排序使用分治法(Divide and conquer)策略來把一個序列(list)分爲兩個子序列(sub-lists)。less

步驟爲:dom

  1. 從數列中挑出一個元素,稱爲 "基準"(pivot),
  2. 從新排序數列,全部元素比基準值小的擺放在基準前面,全部元素比基準值大的擺在基準的後面(相同的數能夠到任一邊)。在這個分區退出以後,該基準就處於數列的中間位置。這個稱爲分區(partition)操做。
  3. 遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序。

遞歸的最底部情形,是數列的大小是零或一,也就是永遠都已經被排序好了。雖然一直遞歸下去,可是這個算法總會退出,由於在每次的迭代(iteration)中,它至少會把一個元素擺到它最後的位置去。ide

代碼:函數

  1 #include <iostream>
  2 #include <iterator>
  3 #include <vector>
  4 #include <ctime>
  5 #include <random>
  6 #include <functional>
  7 #include <algorithm>
  8 using namespace std;
  9 int intSwap(int& a,int& b)
 10 {
 11     int intswaptemp=a;
 12     a=b;
 13     b=intswaptemp;
 14     return 0;
 15 }
 16 /*----------------------------------------------------
 17 -----------------快速排序(STL版本)---------------------
 18 參數:迭代器、cmp
 19 cmp能夠爲less、greater等函數
 20   template<typename _Tp>
 21     struct less_equal : public binary_function<_Tp, _Tp, bool>
 22     {
 23       bool
 24       operator()(const _Tp& __x, const _Tp& __y) const
 25       { return __x <= __y; }
 26     };
 27 這個東西第一次學須要本身拿筆算寫,不須要想,由於太費勁了。
 28 ----------------------------------------------------*/
 29 template<typename Conditerator,typename Compare>
 30 int quickSortIter(Conditerator begin,Conditerator end,Compare cmp)
 31 {
 32     if(begin!=end)//遞歸終止條件
 33     {
 34         Conditerator left=begin;//
 35         Conditerator right=end;//
 36         Conditerator pivot=left++;//用於做爲參考相對大小的數子
 37         while(left!=right)
 38         {
 39             if(cmp(*left,*pivot))//從begin開始下一個比較是否小於begin,    left<begin(pivot)
 40                 ++left;//若是成立,left移向下一個未和begin比較的值
 41             else
 42             {
 43                 while((left!=right)&&cmp(*pivot,*right))//begin(pivot)<right
 44                     right--;
 45                 iter_swap(left,right);
 46             }
 47         }
 48         if(cmp(*pivot,*left))//這裏就是爲了防止left和right重合
 49             --left;//由於在上面程序中,最後會致使left和right重合,須要分離left
 50         iter_swap(begin,left);//保留了pivot,經過交換到前面一組中的最後一位
 51         quickSortIter(begin,left,cmp);
 52         quickSortIter(right,end,cmp);
 53     }
 54     return 0;
 55 }
 56 template<typename T>
 57 inline int quickSort(T begin,T end)
 58 {
 59     quickSortIter(begin,end,
 60                   less_equal<typename iterator_traits<T>::value_type>());
 61     return 0;
 62 }
 63 inline int QuickSortVector(vector<int> &ivec)
 64 {
 65     quickSort(ivec.begin(),ivec.end());
 66     return 0;
 67 }
 68 /*----------------------------------------------------
 69 -----------------快速排序(vector版本)------------------
 70 參數:vector<int>
 71 關鍵信息:經過合適的交換來實現,以第一個begin值爲臨時交換的參考值
 72 解釋同上
 73 注意:必定要搞清楚>=和<=的邏輯關係,不然error或者死循環
 74 ----------------------------------------------------*/
 75 int quicksort_vector(vector<int>& ivec,int begin,int end)
 76 {
 77     if(begin!=end)
 78     {
 79         int left=begin;
 80         int right=end;
 81         int pivot=left++;//設置參考值(用於比較)
 82         while(left!=right)
 83         {
 84             if(ivec[pivot]>=ivec[left])
 85                 ++left;
 86             else
 87             {
 88                 while((left!=right)&&(ivec[pivot]<=ivec[right]))
 89                     --right;
 90                 intSwap(ivec[left],ivec[right]);
 91             }
 92         }
 93         if(ivec[pivot]<=ivec[left])
 94             left--;
 95         intSwap(ivec[begin],ivec[left]);
 96         quicksort_vector(ivec,begin,left);
 97         quicksort_vector(ivec,right,end);
 98     }
 99     return 0;
100 }
101 inline int quicksort1(vector<int> &ivec)
102 {
103     quicksort_vector(ivec,0,ivec.size()-1);
104     return 0;
105 }
106 int main()
107 {
108     clock_t start,end;
109     vector<int> ivec,copyivec;
110     srand(14);
111     for(int i=0;i<10000;i++)//10k
112         ivec.push_back((int)rand());
113     copyivec=ivec;
114     start=clock();
115     QuickSortVector(ivec);
116     end=clock();
117     for(int i=0;i<10000;i+=500)
118         cout<<ivec[i]<<'\t';
119     cout<<endl;
120     cout<<"the time  of 1 is "<<end-start<<endl;
121     start=clock();
122     quicksort1(copyivec);
123     end=clock();
124     for(int i=0;i<10000;i+=500)
125         cout<<ivec[i]<<'\t';
126     cout<<endl;
127     cout<<"the time  of 2 is "<<end-start<<endl;
128 
129     return 0;
130 }

  亂數快速排序有一個值得注意的特性,在任意輸入數據的情況下,它只須要O(n log n)的指望時間。是什麼讓隨機的基準變成一個好的選擇?ui

  假設咱們排序一個數列,而後把它分爲四個部份。在中央的兩個部份將會包含最好的基準值;他們的每個至少都會比25%的元素大,且至少比25%的元素小。若是咱們能夠一致地從這兩個中央的部份選出一個元素,在到達大小爲1的數列前,咱們可能最多僅須要把數列分區2log2 n次,產生一個 O(nlogn)算法。spa

  不幸地,亂數選擇只有一半的時間會從中間的部份選擇。出人意外的事實是這樣就已經足夠好了。想像你正在翻轉一枚硬幣,一直翻轉一直到有 k 次人頭那面出現。儘管這須要很長的時間,平均來講只須要 2k 次翻動。且在 100k 次翻動中得不到 k 次人頭那面的機會,是像天文數字同樣的很是小。藉由一樣的論證,快速排序的遞歸平均只要2(2log2 n)的調用深度就會終止。可是若是它的平均調用深度是O(log n)且每一階的調用樹狀過程最多有 n 個元素,則所有完成的工做量平均上是乘積,也就是 O(n log n)。code

 

數據結構 不定
最差時間複雜度 \Theta(n^2)
最優時間複雜度 \Theta(n\log n)
平均時間複雜度 \Theta(n\log n)
最差空間複雜度 根據實現的方式不一樣而不一樣
相關文章
相關標籤/搜索