【數據結構之內部排序】插入排序、快速排序、選擇排序、歸並排序


排序方法分為兩大類:一類是內部排序,指的是待排序記錄存放在計算機隨機存儲器中進行的排序過程;另一類是外部排序,指的是待排序記錄的數量很大,以致內容一次不能容納全部記錄,在排序中尚需對外存進行訪問的排序過程。

內部排序按照排序過程所需的工作量來區別的話,可分為三類:(1)簡單的排序方法,其時間復雜度為O(n^2); (2)先進的排序方法,其時間復雜度為O(nlogn); (3)基數排序,其時間復雜度為O(d*n);

這里主要就三類每一類介紹一兩個經典的算法,其中均采用順序存儲結構進行操作。

1.簡單排序

 

(1)直接插入排序(Straight Insertion Sort)

直接插入排序的基本操作是從一個有序的表中插入一個新元素,從而得到一個新的有序表。

算法如下:

 

 1 void insertsort(int *array, int num)
 2 {
 3     int temp, i, j;
 4     for (i = 1; i < num; i++)
 5     {
 6         if (array[i] < array[i - 1])        //當插入元素比前面元素小
 7         {
 8             temp = array[i];
 9             for (j = i - 1; j >= 0 && array[j] > temp; j--)       //將前i個比插入元素小的元素全部往后挪一位,然后插入待插入元素
10                     array[j + 1] = array[j];
11             
12             array[j + 1] = temp;
13         }
14     }    
15 }

 

 

(2)折半插入排序(Binary Insertion Sort)

直接插入排序適合於當待排序記錄的數量n很小時使用,但是當n很大時,則不宜使用直接插入,此時可以使用折半插入排序。

 

算法思想:

  將一個元素插入有序表中,找到表頭元素和表尾元素,設m為中間元素,比較中間元素m與待插入元素,若m大,則在m到表尾元素中尋找插入位置,否則在表頭到m中尋找插入位置,重復該過程,知道最終行成新有序表。

 

算法如下:

 1 void BinaryInsertSort(int *a, int n)   
 2 {
 3     int i, k, low, high, temp, m ;
 4     for(i = 1; i < n; i++)
 5     {
 6         low = 0;
 7         high = i - 1;
 8 
 9         while(low <= high) 
10         {
11             m = (low + high) / 2;
12             if(a[m] > a[i]) high = m - 1;
13             else low = m + 1;
14         }
15          int temp = a[i];
16          for(k = i; k > m; k--)
17               a[k] = a[k-1];
18          a[high + 1] = temp;
19     }
20 }            

 

 

總結:折半插入排序所需附加存儲空間和直接插入排序相同,從時間上比較,折半插入僅僅減少關鍵字間的比較次數,而記錄的次數不表,因此,兩種算法的時間復雜度都是O(n^2)。

 

 

2.快速排序

 

(1)冒泡排序(Bubble Sort)

冒泡排序過程很簡單,從第一個元素開始,每次和后面的元素逐個比較,如果比后面元素大則交換位置,每趟冒泡把一個最大的元素放到最后,最后形成新的有序表。

算法如下:

 

 1 void BubbleSort(int *a,int n)
 2 {
 3     int i,j,temp;
 4     for (i=0;i<n;++i)
 5         for (j=0;j<n-i-1;++j)
 6         {
 7             if (a[j]>a[j+1])
 8             {
 9                 temp=a[j];
10                 a[j]=a[j+1];
11                 a[j+1]=temp;    
12             }    
13           }
14 }

 

 

(2)快速排序(Quick Sort)

快速排序是對冒泡排序的一種改進。

 

算法思路:通過一躺排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另外一部分的關鍵字要小,然后繼續分別對這兩部分重復該步驟,最后達到整個序列有序。

 

步驟:1.首先找到一個記錄作為樞軸(pivot);

   2.以該樞軸將序列分割為兩部分;

   3.以此類推;

 

關於找樞軸的方法,主要有一下三種:

1.固定位置:取序列的最后一個元素或第一個元素;

2.隨機選取;

3.三數取中:使用左端、中間、右端三個中的中值作為樞軸。

 

這里舉了選取最后一個元素作為樞軸的算法:

 1 int partion(int *a,int low,int high)
 2 {
 3     int x=a[high];
 4     int temp;
 5     int j,i=low-1;
 6     for (j=low;j<high;j++)
 7     {
 8         if (a[j]<x)              //如果元素比最后一個小,則交換
 9         {
10             i++;
11             temp=a[i];
12             a[i]=a[j];
13             a[j]=temp;
14         }
15     }    
16     temp=a[i+1];
17     a[i+1]=a[high];
18     a[high]=temp;
19 
20     return i+1;
21 }
22 
23 void Qsort(int *a,int low,int high)
24 {
25     if (low<high)
26     {
27         int pivotloc=partion(a,low,high);        //每次利用遞歸將每個小部分划分,最終達到有序
28         Qsort(a,low,pivotloc-1);
29         Qsort(a,pivotloc+1,high);
30     }
31 }

 

具體的分析如下:

e.g:給出5個數 2 7 8 1 5 6 4

第一趟划分后變成 2 1 4 7 5 6 8

然后利用遞歸,對4左邊的序列2 1划分變成1 2,右邊部分變成5 6 7 8,最后合並有序。

 

總結:冒泡排序的時間復雜度為O(n^2),而快速排序最佳時間復雜度為O(n*logn),最壞為O(n^2)。

 

3.選擇排序

(1)簡單選擇排序(Seletion Sort)

算法思想:通過n-i次關鍵字間的比較,每一趟在n-i-1個記錄中選取關鍵字最小的記錄作為有序序列中的第i個記錄,並和第i個記錄交換值。


算法如下:

 

 1 void select_sort(int a[],int n)//n為數組a的元素個數
 2 {
 3     //進行N-1輪選擇
 4     for(int i=0; i<n-1; i++)
 5     {
 6         int min_index = i; 
 7         //找出第i小的數所在的位置
 8         for(int j=i+1; j<n; j++)
 9         {
10             if(a[j] < a[min_index])
11             {
12                 min_index = j;
13             }
14         }
15         //將第i小的數,放在第i個位置;如果剛好,就不用交換
16         if( i != min_index)
17         {
18             int temp = a[i];
19             a[i] = a[min_index];
20             a[min_index] = temp;
21         }
22     }
23 }

 

 

 

4.歸並排序

歸並排序(Merging Sort)是將兩個或兩個以上的有序表組合成一個新的有序表。

算法思路:假設初始序列有n個記錄,則可看成是n個有序的子序列,每個子序列的長度為1,然后兩兩合並,得到[n/2]個長度為2或1的有序子序列;再兩兩歸並,如此重復最終活的一個長度為n的有序序列。

算法如下:

 

 1 void Merge(int arr[],int low,int mid,int high)
 2 {
 3     int i,k;
 4     int *temp=(int *)malloc((high-low-1)*sizeof(int));
 5     int left_low=low;
 6     int left_high=mid;
 7     int right_low=mid+1;
 8     int right_high=high;
 9 
10     for (k=0;left_low<left_high&&right_low<right_high;++k)
11     {
12         if (arr[left_low]<arr[right_low])        //從左右兩個序列中挑選出最小的元素放在temp數組中
13               temp[k]=arr[left_low++];
14         else
15               temp[k]=arr[right_low++];
16      }
17 
18     //將挑選后剩余的元素放進temp數組
19     while (left_low<left_high)    
20         temp[k++]=arr[left_low++];
21     while (right_low<right_high)
22         temp[k++]=arr[right_low++];
23     
24     for (i=0;i<high-low-1;++i)          //將有序的temp數組復制到arr數組中
25         arr[low+i]=temp[i];
26     
27     free(temp);
28 }    
29 
30 void MergeSort(int arr[],int first,int last)
31 {
32     int m=0;
33     if (first<last)
34     {
35         m=(first+last)/2;
36         MergeSort(arr,first,m);              //將左序列繼續划分為小部分
37         MergeSort(arr,m+1,last);             //將右序列繼續划分為小部分
38         Merge(arr,first,m,last);
39     }
40 }    

 

 

 

 

總結:歸並排序的時間復雜度為O(n*logn)。

 

 

 

各種內部排序算法的比較:

 


免責聲明!

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



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