排序方法分為兩大類:一類是內部排序,指的是待排序記錄存放在計算機隨機存儲器中進行的排序過程;另一類是外部排序,指的是待排序記錄的數量很大,以致內容一次不能容納全部記錄,在排序中尚需對外存進行訪問的排序過程。
內部排序按照排序過程所需的工作量來區別的話,可分為三類:(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)。
各種內部排序算法的比較:

