算法導論中的四種基本排序


算法導論中常見的四種排序

                                                        by方陽

版權聲明:本文為博主原創文章,轉載請指明轉載地址

http://www.cnblogs.com/fydeblog/p/7067382.html 

1. 前言

好久沒寫博客了,今天來一篇最近開始看的算法導論,這篇博客主要介紹插入排序,歸並排序,堆排序和快速排序的原理性能分析以及程序實現!廢話不多說,let's go!

 

2. 原理解析

 

2.1 插入排序

參考下面的圖片,再想想我們平時玩撲克牌,它的基本思路就清晰多了,假設待排序的記錄存放在數組R[1..n]中。初始時,R[1]自成1個有序區,無序區為R[2..n]。從i=2起直至i=n為止,依次將R[i]插入當前的有序區R[1..i-1]中,生成含n個記錄的有序區

2.2 歸並排序

參考下面的圖片(從pdf上截的,有點歪),可以看出是將原數組進行分解,然后排序,在進行合並歸並排序分治(Divide and conquer)策略的一個很好的例子,將復雜的問題分成許多小問題,分開解決,然后合並。

2.3 堆排序

堆排序涉及到的知識比較多,它主要分為三部分MAX-HESPIFY,BUILD-MAX-HEAP 和 HEAPSORT

MAX-HESPIFY維持最大堆的性質。

BUILD-MAX-HEAP:將無序的數據數組構造成一個最大堆。

HEAPSORT:對一個數組進行原址排序。

這里面涉及蠻多知識的,下面分別介紹一下!

最大堆的定義:在最大堆中,最大堆特性是指除了根以外的每個結點i,有A[PARENT(i)] >= A[i]。這樣,堆的最大元素就存放在根結點中。

那下面的圖片來說,就是序號1為根結點(也叫父結點),要大於序號2和3的元素(2,3也叫子類結點,左子結點和右子結點),其他結點類似按照這個來推


原址排序的定義:在排序輸入數組時,只有常數個元素被存放到數組以外的空間中去。就是說不需要花數組那樣大的空間來緩存數據,大部分排序都在數組內部進行!

MAX-HESPIFY(過程見下圖,詳細看代碼)

 

BUILD-MAX-HEAP(過程見下圖,詳細看代碼)

 

HEAPSORT(過程見下圖,詳細看代碼)

 

2.4 快速排序

 

快速排序使用分治策略來把一個數組分為兩個子數組。

 

步驟為:

 

  1. 從數組中挑出一個元素,稱為 "基准"(pivot),
  2. 重新排序數組,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分割之后,該基准是它的最后位置。這個稱之為分割(partition)操作。
  3. 遞歸地(recursive)把小於基准值元素的子數組和大於基准值元素的子數組排序。

 如下圖所示

 

3.性能分析

3.1 運行時間

從圖可以看出,在n很大時,歸並排序和堆排序(它們接近最優)運行時間上比插入排序和快速排序,n值小時,插入和快排較快。實際應用中,快排用的較多,它一般快於堆排序。

歸並排序和堆排序都是以二叉樹形式,它的高度是lgn,每一層都要進行n次比較,所以最后最壞結果都是nlgn,而插入排序最壞的情況是每一個都要去比較,即n的平方,快排最壞的情況是分組一邊0個元素,另一邊是n-1,平均情況還是類似歸並排序,也是分治法的最優情況,nlgn。

3.2 存儲空間

插入排序和快速排序是原址排序算法,歸並排序和堆排序不是,所以插入排序和快速排序占用空間小,更節省內存。

4. 程序實現

4.1. main.cpp

 

 1 #include <iostream>
 2 #include "fy_sort.h"
 3 using namespace std;
 4  
 5 
 6 int main()
 7 { 
 8   fy_sort sort1; 
 9   int num;
10   int a[30],b[30],c[30],d[30];
11   cout<<"please enter the number of input elements: "<<endl;
12   cin >>  num;
13   cout << "Input the elements:\n";
14   for(int i=0;i<num;i++)
15   {
16       cin>>a[i];
17       b[i]=a[i];
18       c[i]=a[i];
19       d[i]=a[i];
20   }
21   cout << "insert_sort:\n";
22   sort1.insert_sort(a,num);//插入排序
23   cout << "merge_sort:\n";
24   sort1.merge_sort(b,0,num-1);//歸並排序
25   sort1.display(b,num);
26   cout << "heap_sort:\n";
27   sort1.heap_sort(c,num);//堆排序
28   cout << "quick_sort:\n";
29   sort1.quick_sort(d,0,num-1);//快速排序
30   sort1.display(d,num);
31 
32   return 0;
33 }

4.2  fy_sort.h

 

 1 #pragma once
 2 
 3 #ifndef FY_SORT_H
 4 #define FY_SORT_H
 5 
 6 class fy_sort //排序類
 7 {
 8 private:
 9     
10     void swap (int& n, int& m);                 //交換函數
11     void merge(int a[],int p,int q,int r);      //歸並排序函數
12     inline int left  (int i) {return 2 * i + 1;}//內聯函數,用於堆排序的左手序號  
13     inline int right (int i){return 2 *(i + 1);}//內聯函數,用於堆排序的右手序號  
14     void max_heapify (int a[], int i);          //最大堆排序中的核心,維護最大堆的性質,即父類節點大於子類節點
15     void build_max_heap (int a[], int n);       //建造最大堆,調用n/2次max_heapify,將無序的數組構造成最大堆
16     int Partition(int a[], int beg, int end);   //快排的核心,即分割,將大於數組最后一個元素的元素移到右邊,小於的移到左邊
17 
18 public:
19 
20    void display(const int a[],int k);        //顯示函數
21    void insert_sort(int a[],int n);          //插入排序
22    void quick_sort(int a[],int beg, int end);//快速排序(遞歸調用)
23    void merge_sort(int a[],int p,int r);     //歸並排序(遞歸調用)
24    void heap_sort(int a[],int n);            //堆排序
25    
26 };
27 
28 #endif

4.3 fy_sort.cpp

 

 

  1 #include "fy_sort.h"
  2 #include<iostream>
  3 
  4 using namespace std;
  5 
  6 int heap_size; 
  7 //顯示函數
  8 void fy_sort::display(const int a[],int k) 
  9 {
 10     for(int i=0;i<k;i++)
 11        cout<<a[i]<<" ";
 12        cout<<endl;
 13 }
 14 //交換函數
 15 void fy_sort::swap (int& n, int& m)  
 16 {  
 17     int temp;  
 18     temp = n;  
 19     n = m;  
 20     m = temp;  
 21 } 
 22 
 23 void fy_sort::insert_sort(int a[],int k)//插入排序
 24 {
 25      int key;
 26      for (int i=1;i<k;i++)
 27      {
 28       key=a[i]; //先將要比較的元素暫存,后面要替換
 29       int j=i-1;
 30       //循環實現的功能找到比a[i]大的前面元素(序號小的),找到替換,無則不變
 31         while((j>-1)&&(key<a[j])) 
 32         {
 33           a[j+1]=a[j];
 34           j--;
 35         }
 36       a[j+1]=key;
 37      }
 38      display(a,k);    
 39 }
 40 
 41 void fy_sort::merge_sort(int a[],int p,int r) //歸並排序
 42 {
 43     if(p<r)
 44     {
 45         int q=(p+r)/2;     //以q值為划分區間,將數組划分成兩個小數組
 46         merge_sort(a,p,q); //小數組再划分,遞歸調用,直到剩下一個
 47         merge_sort(a,q+1,r);//小數組再划分,遞歸調用,直到剩下一個
 48         merge(a,p,q,r);     //將分解后的最小數組,進行排序和合並
 49     }
 50     
 51 }
 52 
 53 
 54 void fy_sort::merge(int a[],int p,int q,int r)
 55 {
 56     int n1 = q-p+1;  
 57     int n2 = r-q;  
 58     //生成兩個新空間,存划分后的數組
 59     int *L = new int[n1+1];  
 60     int *R = new int[n2+1];  
 61     int i, j, k;  
 62     //將大數組的值賦給兩個小數組      
 63     for (i=0; i<n1; i++){  
 64         L[i] = a[p+i];  
 65     }  
 66     for (j=0; j<n2; j++){  
 67         R[j] = a[q+j+1];  
 68     }
 69     //確定邊界
 70     L[n1] = 10000000;  
 71     R[n2] = 10000000;  
 72     //按位比較,在合並成大數組  
 73     for (i=0, j=0, k=p; k<=r; k++)  
 74     {  
 75         if (L[i]<=R[j])  
 76         {  
 77             a[k] = L[i];  
 78             i++;  
 79         }else{  
 80             a[k] = R[j];  
 81             j++;  
 82         }  
 83     }  
 84     //釋放空間  
 85     delete []L;  
 86     delete []R;  
 87 }
 88 
 89 void fy_sort::max_heapify (int a[], int i)  
 90 {   
 91     //左手和右手的序號
 92     int l = left(i);  
 93     int r = right(i);
 94     //largest存儲父節點和子節點中最大的數
 95     int largest;  
 96     //子類左結點與父節點比較,將其中大的賦給largest
 97     if ((l < heap_size) && a[l] > a[i])  
 98         largest = l;  
 99     else  
100         largest = i;  
101    //子類左結點與largest比較,將其中大的賦給largest
102     if ((r < heap_size) && a[r] > a[largest])  
103         largest = r;  
104    //若根節點變化,會導致下面分支的最大堆性質可能變化,這里對下面進行重新分堆
105     if (largest != i)  
106     {  
107         swap (a[largest], a[i]);  
108         max_heapify (a, largest);  
109     }  
110 }  
111 
112 void fy_sort::build_max_heap (int a[], int n)  
113 {  
114     heap_size=n; 
115     //記住一點,最大堆是以由底向上分堆
116     for (int i = (n - 1) / 2; i >= 0; i--)  
117         max_heapify (a, i);  
118 }  
119 
120 void fy_sort::heap_sort (int a[], int n)  
121 {  
122     build_max_heap (a, n);
123     //實現的是原址排序,將最大堆轉化成從小到大的數組
124     //原理是將最大堆的根節點換成最大堆最末尾的數,然后再進行最大堆
125     //直到只剩下一個節點,即為最小值
126     for (int i = n - 1; i >= 1; i--)  
127     {  
128         swap (a[i], a[0]);  
129         heap_size--;  
130         max_heapify (a, 0);  
131     }  
132     display(a,n);
133 } 
134 //快速排序的核心,也跟堆排序一樣,是原址排序
135 int fy_sort::Partition(int a[], int beg, int end)
136 {
137     int sentinel = a[end];
138     int i = beg-1;
139     //這個循環以最后一個為基准,小於a[end]的放到左邊
140     //大於a[end]放到右邊
141     for(int j=beg; j<=end-1; ++j)
142     {
143         if(a[j] <= sentinel)
144         {
145             i++;
146             swap(a[i], a[j]);
147         }
148     }
149     swap(a[i+1], a[end]);
150  
151     return i+1;
152 }
153 
154 void fy_sort::quick_sort(int a[], int beg, int end)
155 {
156     if(beg < end)
157     {
158         int pivot = Partition(a, beg, end);
159         //后邊這兩個是對一部分划分的左右部分在進行快排
160         quick_sort(a, beg, pivot-1);
161         quick_sort(a, pivot+1, end);
162     }
163     
164 }

 

5.運行結果

 

6.結束語

建議大家去看算法導論,里面講的非常細,就是太厚了,容易看不下去!(笑哭)挑重點看吧,下次見

這里再分享兩位網友的筆記,寫得很好!

Tanky Woo:  《算法導論》學習總結—【目錄】

Adoo          :   《算法導論》筆記匯總


免責聲明!

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



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