各種排序算法的實現及其比較


本人介紹的排序算法主要有:插入排序,選擇排序,冒泡排序,快速排序,堆排序,歸並排序,希爾排序,二叉樹排序,桶排序,基數排序(后兩者為非比較排序,前面的為比較排序)。

 

排序的穩定性和復雜度:

      不穩定:

      選擇排序(selection sort)— O(n2)

      快速排序(quicksort)— O(nlogn) 平均時間, O(n2) 最壞情況; 對於大的、亂序串列一般認為是最快的已知排序

      堆排序 (heapsort)— O(nlogn)

      希爾排序 (shell sort)— O(nlogn)

      基數排序(radix sort)— O(n·k); 需要 O(n) 額外存儲空間 (K為特征個數)

 

      穩定:

      插入排序(insertion sort)— O(n2)

      冒泡排序(bubble sort) — O(n2)

      歸並排序 (merge sort)— O(nlogn); 需要 O(n) 額外存儲空間

      二叉樹排序(Binary tree sort) — O(nlogn); 需要 O(n) 額外存儲空間

      桶排序 (bucket sort)— O(n); 需要 O(k) 額外存儲空間

 

1、插入排序

     對於一個序列{a[0]……a[n]},當記錄值是第i個元素時,前面i-1個元素已經排好序了,那么這個記錄值從第i-1個元素一直往前比較,找到屬於它的位置后插進去。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 int main()
 5 {
 6     int a[]={1,99,2,88,3,77,4,66};
 7     int n=sizeof(a)/4;
 8     for(int i=0; i<n; i++)
 9     {
10         int tp=a[i], j;
11         for(j=i-1; j>=0&&a[j]>tp; j--) a[j+1]=a[j];
12         a[j+1]=tp;
13     }
14     cout << a[0] ;
15     for(int i=1; i<n; i++) cout << " " << a[i];
16     cout <<endl;
17     return 0;
18 }
View Code

 

 

2、選擇排序

    對於一個序列{a[0]……a[n]},前面i-1個元素都是已經排好序的,那么從第i到第n個元素,找到最小值的那個元素,如果下標不是i,則讓第i個元素和那個最小的元素位置互換。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 int main()
 5 {
 6     int a[]={1,99,2,88,3,77,4,66};
 7     int n=sizeof(a)/4;
 8     for(int i=0; i<n; i++)
 9     {
10         int pos=-1, minn=a[i];
11         for(int j=i+1; j<n; j++)
12         {
13             if(a[j]<minn) minn=a[j], pos=j;
14         }
15         if(pos!=-1) swap(a[i],a[pos]);
16     }
17     cout << a[0] ;
18     for(int i=1; i<n; i++) cout << " " << a[i];
19     cout <<endl;
20     return 0;
21 }
View Code

 

3、冒泡排序

    冒泡排序顧名思義就是從最后往前兩個元素開始進行兩兩比較,如果a[i]小於a[i-1],那么讓他們互換位置,每比較一輪必有一個最小的元素冒泡到這些所比較元素的前面。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 int main()
 5 {
 6     int a[]={1,99,2,88,3,77,4,66};
 7     int n=sizeof(a)/4;
 8     for(int i=0; i<n; i++)
 9     {
10         for(int j=n-1; j>i; j--)
11             if(a[j]<a[j-1]) swap(a[j],a[j-1]);
12     }
13     cout << a[0] ;
14     for(int i=1; i<n; i++) cout << " " << a[i];
15     cout <<endl;
16     return 0;
17 }
View Code

 

4、快速排序

    基本思想就是取一個數作為中間數(一般是取第一個數作為中間數),小於它的都放到左邊,大於它的都放到右邊,再對每一邊利用同樣的思想進行處理。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 void QuickSort(int *a, int l, int r)
 5 {
 6     if(a==NULL||l>=r) return ;
 7 
 8     int i=l, j=r, tmp=a[l];
 9     while(i<j)
10     {
11         while(j>i&&a[j]>=tmp) j--;
12         a[i]=a[j];
13         while(i<j&&a[i]<=tmp) i++;
14         a[j]=a[i];
15     }
16     a[i]=tmp;
17     QuickSort(a,l,i-1);
18     QuickSort(a,i+1,r);
19 }
20 
21 int main()
22 {
23     int a[]= {1,99,2,88,3,77,4,66};
24     int n=sizeof(a)/4;
25     QuickSort(a,0,n-1);
26     cout << a[0] ;
27     for(int i=1; i<n; i++) cout << " " << a[i];
28     cout <<endl;
29     return 0;
30 }
View Code

 

5、堆排序

堆排序其實要利用到二叉堆,二叉堆其實完全可以理解為一顆有限制的完全二叉樹。

二叉堆的定義:二叉堆可以分為最大堆和最小堆。最大堆為對於所有節點它的左右節點權值一定比它小,最小堆為對於所有節點它的左右節點權值一定比它大。

二叉堆的插入:將一個序列下表從0開始一個一個往堆里插入,因為滿足完全二叉樹性質,所以這么做是可行的。對於插入的第i個數,那么從下往上,它的父親節點為(i-1)/2個數,再根據二叉堆的性質進行調整。

二叉堆的刪除:每次進行一次堆調整之后,根節點必是最大的(最大堆),每次把根節點a[0]取出和數組第n個數互換,然后再用數組第1個到第n-1個數再次建堆,如此反復取出再建堆,那么得到的新序列必是一個有序序列。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 void BuildHeap(int *a, int i, int n)  //建二叉堆
 5 {
 6     int j=(i-1)/2;  //j為i節點的父親節點
 7     while(i>0)
 8     {
 9         if(a[j]>=a[i]) break;
10         swap(a[i],a[j]);
11         i=j;
12         j=(i-1)/2;
13     }
14 }
15 
16 void MaxHeapSort(int *a, int i, int n)  //二叉堆排序
17 {
18     int j=2*i+1, tmp=a[i];
19     while(j<n)
20     {
21         if(a[j+1]>a[j]&&j+1<n) j++; //選出i節點左右孩子節點的最大值
22         if(tmp>=a[j]) break;
23         a[i]=a[j];
24         i=j;
25         j=2*i+1;
26     }
27     a[i]=tmp;
28 }
29 
30 
31 int main()
32 {
33     int a[]= {1,99,2,88,3,77,4,66};
34     int n=sizeof(a)/4;
35     for(int i=0; i<=n-1; i++)
36           BuildHeap(a,i,n);
37     for(int i=n-1; i>=1; i--)
38     {
39         swap(a[i],a[0]);
40         MaxHeapSort(a,0,i);
41     }
42     cout << a[0] ;
43     for(int i=1; i<n; i++) cout << " " << a[i];
44     cout <<endl;
45     return 0;
46 }
View Code

 

6、歸並排序

歸並的意思就是兩個或兩個以上的有序表組合成一個新的有序表。整個歸並排序需要進行【lgn取上限】次,總的時間復雜度為O(nlgn)。與快速排序相比,歸並排序的最大特點是:它是一種穩定的排序方法。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 void Merge(int *a, int s, int m, int t)
 5 {
 6     int *b=new int[10];
 7     int i=s, j=m+1, num=0;
 8     while(i<=m&&j<=t)
 9     {
10         if(a[i]<=a[j]) b[num++]=a[i], i++;
11         else b[num++]=a[j],j++;
12     }
13     while(i<=m) b[num++]=a[i], i++;
14     while(j<=t) b[num++]=a[j], j++;
15     for(int i=s; i<=t; i++) a[i]=b[i-s];
16     delete[] b;
17 }
18 
19 void MergeSort(int *a, int s, int t)
20 {
21     if(a==NULL) return ;
22     if(s<t)
23     {
24         int mid=(s+t)/2;
25         MergeSort(a,s,mid);
26         MergeSort(a,mid+1,t);
27         Merge(a,s,mid,t);
28     }
29 }
30 
31 
32 int main()
33 {
34     int a[]= {1,99,2,88,3,77,4,66};
35     int n=sizeof(a)/4;
36     MergeSort(a,0,n-1);
37     cout << a[0] ;
38     for(int i=1; i<n; i++) cout << " " << a[i];
39     cout <<endl;
40     return 0;
41 }
View Code

 

7、希爾排序

很多人都說希爾排序是插入排序的一種改進,我看了半天也沒看明白這句話。

希爾排序就是利用無空位的跳躍值gap進行跳躍排序,如果n為8,為gap的取值則為8  4  2  1, gap=gap/2,gap初始值為n/2。

對於每個gap值,都要從后往前掃一遍(以gap值大小跳躍比較),即i,i-gap,i-2*gap……。注意:只限在相鄰兩個掃到的數比較。

時間復雜度:O(nlog(n*n))。

 1 #include <iostream>
 2 using  namespace std;
 3 
 4 void ShellSort(int *a, int n)
 5 {
 6     for(int gap=n/2; gap>0; gap/=2)
 7         for(int i=gap; i<n; i++)
 8             for(int j=i-gap; j>=0; j-=gap)
 9                 if(a[j]>a[j+gap]) swap(a[j],a[j+gap]);
10 }
11 
12 int main()
13 {
14     int a[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428};
15     int n=sizeof(a)/4;
16     ShellSort(a,n);
17     cout << a[0];
18     for(int i=1; i<n; i++) cout << " " << a[i];
19     cout << endl;
20     return 0;
21 }
View Code

 

8、二叉樹排序 

二叉樹的性質:對於每個節點,它的左孩子的鍵值一定比它小,右孩子的鍵值一定比它大。

二叉樹排序簡單點說就是先隨便設置一個根節點,然后將其他數一個一個插入到樹中,權值小於此節點則往左走,大於往右走,一直找到合適的位置建立自己新的節點位置。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <sstream>
 4 #include <algorithm>
 5 using  namespace std;
 6 
 7 struct Node
 8 {
 9     int key;
10     Node *l, *r;
11     Node(){ l=NULL; r=NULL;}
12 };
13 
14 Node* Insert(Node *rt, int key)
15 {
16     if(rt==NULL)
17     {
18         Node *rt=new Node();
19         rt->key=key;
20         return rt;
21     }
22     if(key<rt->key) rt->l=Insert(rt->l,key);
23     else rt->r=Insert(rt->r,key);
24     return rt;
25 }
26 
27 void Printf(Node *rt)
28 {
29     if(rt->l!=NULL) Printf(rt->l);
30     cout << rt->key << " ";
31     if(rt->r!=NULL) Printf(rt->r);
32 }
33 
34 int main()
35 {
36     int a[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428};
37     int n=sizeof(a)/4;
38     Node *root=new Node();
39     root->key=a[0];
40     for(int i=1; i<n; i++)
41     {
42         Insert(root,a[i]);
43     }
44     Printf(root);
45     cout << endl;
46     return 0;
47 }
View Code

 

9、基數排序

   基數是一種不穩定的排序,它的時間復雜度為O(k*n),k表示最大數的位數,所以當一個序列中有一個很大很大的數時,它排序所花費的時間是非常高昂的。

   基數排序的原理是一位一位來排序:先按個位大小排序,再按十位大小排序,接着百位……,一直排到最大位數停止。

  比如這樣一個數列排序: 342 ,58, 576, 356

第一次排序(個位):

3 4 2

5 7 6

3 5 6

0 5 8

第二次排序(十位):

4 2

5 6

5 8

7 6

第三次排序(百位):

0 5 8

3 4 2

3 5 6

5 7 6

結果: 58 342 356 576。

 1 #include <iostream>
 2 #include <cstring>
 3 #include <sstream>
 4 #include <algorithm>
 5 using  namespace std;
 6 
 7 int maxdigit(int *a, int n) //返回數組中最大數的位數
 8 {
 9     int maxx=0;
10     for(int i=0; i<n; i++)
11     {
12         stringstream sa;
13         sa<<a[i];
14         string s=sa.str();
15         maxx=max(maxx,int(s.size()));
16     }
17     return maxx;
18 }
19 
20 void BaseSort(int *a, int n)
21 {
22     int *count=new int[10];
23     int *tmp=new int[n];
24     int m=maxdigit(a,n);
25     int base=1;
26     for(int i=1; i<=m; i++)
27     {
28         for(int j=0; j<10; j++) count[j]=0;
29         for(int j=0; j<n; j++)
30         {
31             int k=a[j]/base%10;
32             count[k]++;
33         }
34         for(int j=1; j<10; j++)
35                 count[j]+=count[j-1];
36         for(int j=n-1; j>=0; j--)
37         {
38             int k=a[j]/base%10;
39             count[k]--;
40             tmp[  count[k] ]=a[j];
41         }
42         for(int j=0; j<n; j++)  a[j]=tmp[j];
43         base*=10;
44     }
45     delete[] count;
46     delete[] tmp;
47 }
48 
49 int main()
50 {
51     int a[]= {1,99,2,88,3,77,4,66,123,321,58,324,127,428};
52     int n=sizeof(a)/4;
53     BaseSort(a,n);
54     cout << a[0] ;
55     for(int i=1; i<n; i++) cout << " " << a[i];
56     cout <<endl;
57     return 0;
58 }
View Code

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM