基本排序算法一
一 選擇排序
原理:選擇排序很簡單,他的步驟如下:
之所以稱之為選擇排序,是因為每一次遍歷未排序的序列我們總是從中選擇出最小的元素。下面是選擇排序的動畫演示:
?
public class Sort {//選擇排序public static void SelectionSort(int[] array) {int n = array.length;for (int i = 0; i < n; i++) {int min = i; // 從第i+1個元素開始,找最小值for (int j = i + 1; j < n; j++) {if (array[min] > array[j])min = j;}Swap(array, i, min);}}//插入排序public static void insertionSort(int[] array){int n = array.length;for (int i =1; i < n; i++) {for (int j = i; j >0; j--) {if (array[j] < array[j-1])Swap(array, j, j-1);elsebreak;} }}//冒泡排序public static void bubbleSort(int[] array){int n = array.length;for (int i =0; i < n; i++) {for (int j = n-1; j >i; j--) {if (array[j] < array[j-1])Swap(array, j, j-1);} }}//希爾排序public static void shellSort(int[] arr){int N=arr.length;int h=1;while(h<N/3){h=3*h+1;}while (h>=1) {for(int i =h; i <N; i++) {for (int j =i; j>=h&&(arr[j]<arr[j-h]); j-=h) {swap(arr, j, j-h); }}h=h/3;}}private static void Swap(int[] array, int i, int min) {int temp = array[i];array[i] = array[min];array[min] = temp;}private static void printArr(int[] array){for (int i = 0; i < array.length; i++) {System.out.print(array[i]+" ");}System.out.println("");}public static void main(String[] args) {int[] array = new int[] { 1, 3, 1, 4, 2, 4, 2, 3, 2, 4, 7, 6, 6, 7, 5,5, 7, 7 };System.out.println("Before Sort:");printArr(array);//SelectionSort(array);//insertionSort(array); bubbleSort(array);System.out.println("After Sort:");printArr(array);} } View Code下圖分析了選擇排序中每一次排序的過程,您可以對照圖中右邊的柱狀圖來看。
(array);分析:
選擇排序的在各種初始條件下的排序效果如下:
二 插入排序
原理:
插入排序也是一種比較直觀的排序方式。可以以我們平常打撲克牌為例來說明,假設我們那在手上的牌都是排好序的,那么插入排序可以理解為我們每一次將摸到的牌,和手中的牌從左到右依次進行對比,如果找到合適的位置則直接插入。具體的步驟為:
下面是插入排序的動畫演示:
分析:
插入排序的在各種初始條件下的排序效果如下:
1. 插入排序平均需要N2/4次比較和N2/4 次交換。在最壞的情況下需要N2/2 次比較和交換;在最好的情況下只需要N-1次比較和0次交換。
先考慮最壞情況,那就是所有的元素逆序排列,那么第i個元素需要與前面的i-1個元素進行i-1次比較和交換,所有的加起來大概等于N(N- 1) / 2 ~ N2?/ 2,在數組隨機排列的情況下,只需要和前面一半的元素進行比較和交換,所以平均需要N2/4次比較和N2/4 次交換。
在最好的情況下,所有元素都排好序,只需要從第二個元素開始都和前面的元素比較一次即可,不需要交換,所以為N-1次比較和0次交換。
2. 插入排序中,元素交換的次數等于序列中逆序元素的對數。元素比較的次數最少為元素逆序元素的對數,最多為元素逆序的對數 加上數組的個數減1。
3.總體來說,插入排序對于部分有序序列以及元素個數比較小的序列是一種比較有效的方式。
如上圖中,序列AEELMOTRXPS,中逆序的對數為T-R,T-P,T-S,R-P,X-S 6對。典型的部分有序隊列的特征有:
- 數組中每個元素離最終排好序后的位置不太遠
- 小的未排序的數組添加到大的已排好序的數組后面
- 數組中只有個別元素未排好序
對于部分有序數組,插入排序是比較有效的。當數組中逆元素的對數越低,插入排序要比其他排序方法要高效的多。
選擇排序和插入排序的比較:
上圖展示了插入排序和選擇排序的動畫效果。圖中灰色的柱子是不用動的,黑色的是需要參與到比較中的,紅色的是參與交換的。圖中可以看出:插入排序不會動右邊的元素,選擇排序不會動左邊的元素;由于插入排序涉及到的未觸及的元素要比插入的元素要少,涉及到的比較操作平均要比選擇排序少一半。
3.冒泡排序
冒泡排序也被稱為下沉排序,是一個簡單的排序算法,通過多次重復比較每對相鄰的元素,并按規定的順序交換他們,最終把數列進行排好序。一直重復下去,直到結束。該算法得名于較小元素“氣泡”會“浮到”列表頂部。由于只使用了比較操作,所以這是一個比較排序。冒泡排序算法的運作如下:
時間復雜度
若文件的初始狀態是正序的,一趟掃描即可完成排序。所需的關鍵字比較次數 C和記錄移動次數 M均達到最小值: Cmin=n-1,Mmin=0。所以,冒泡排序最好的時間復雜度為 O(n)。 ? 若初始文件是反序的,需要進行 n-1趟排序。每趟排序要進行 n-i次關鍵字的比較(1≤i≤n-1),且每次比較都必須移動記錄三次來達到交換記錄位置。在這種情況下,比較和移動次數均達到最大值: 冒泡排序的最壞時間復雜度為O(n*n)綜上,因此冒泡排序總的平均時間復雜度為O(n*n)。算法穩定性
冒泡排序就是把小的元素往前調或者把大的元素往后調。比較是相鄰的兩個元素比較,交換也發生在這兩個元素之間。所以,如果兩個元素相等,我想你是不會再無聊地把他們倆交換一下的;如果兩個相等的元素沒有相鄰,那么即使通過前面的兩兩交換把兩個相鄰起來,這時候也不會交換,所以相同元素的前后順序并沒有改變,所以冒泡排序是一種穩定排序算法。?
希爾排序
原理:希爾排序也稱之為遞減增量排序,它是對插入排序的改進。在插入排序中,我們知道,插入排序對于近似已排好序的序列來說,效率很高,可以達到線性排序的效率。但是插入排序效率也是比較低的,他 一次只能將數據向前移一位。比如如果一個長度為N的序列,最小的元素如果恰巧在末尾,那么使用插入排序仍需一步一步的向前移動和比較,要N-1次比較和交 換。希爾排序通過將待比較的元素劃分為幾個區域來提升插入排序的效率。這樣可以讓元素可以一次性的朝最終位置邁進一大步,然后算法再取越來越小的步長進行排序,最后一步就是步長為1的普通的插入排序的,但是這個時候,整個序列已經是近似排好序的,所以效率高。
如下圖,我們對下面數組進行 排序的時候,首先以4為步長,這是元素分為了LMPT,EHSS,ELOX,AELR幾個序列,我們對這幾個獨立的序列 進行插入排序,排序完成之后,我們減小步長繼續排序,最后直到步長為1,步長為1即為一般的插入排序,他保證了元素一定會被排序。
希爾排序的增量遞減算法可以隨意指定,可以以N/2遞減,只要保證最后的步長為1即可。
實現:
public static void shellSort(int[] arr){int N=arr.length;int h=1;while(h<N/3){h=3*h+1;}while (h>=1) {for(int i =h; i <N; i++) {for (int j =i; j>=h; j-=h) {if(arr[j]<arr[j-h]){swap(arr, j, j-h);}else{break;}}}h=h/3;} }可以看到,希爾排序的實現是在插入排序的基礎上改進的,插入排序的步長為1,每一次遞減1,希爾排序的步長為我們定義的h,然后每一次和前面的-h位置上的元素進行比較。算法中,我們首先獲取小于N/3 的最大的步長,然后逐步長遞減至步長為1的一般的插入排序。
下面是希爾排序在各種情況下的排序動畫:
分析:
1. 希爾排序的關鍵在于步長遞減序列的確定,任何遞減至1步長的序列都可以,目前已知的比較好的序列有:
- Shell’s 序列: N/2 , N/4 , …, 1 (重復除以2);
- Hibbard’s 序列: 1, 3, 7, …, 2k?– 1 ;
- Knuth’s 序列: 1, 4, 13, …, (3k?– 1) / 2 ;該序列是本文代碼中使用的序列。
- 已知最好的序列是 Sedgewick’s (Knuth的學生,Algorithems的作者)的序列: 1, 5, 19, 41, 109, ….
該序列由下面兩個表達式交互獲得:
- 1, 19, 109, 505, 2161,….., 9(4k?– 2k) + 1, k = 0, 1, 2, 3,…
- 5, 41, 209, 929, 3905,…..2k+2?(2k+2?– 3 ) + 1, k = 0, 1, 2, 3, …
“比較在希爾排序中是最主要的操作,而不是交換。”用這樣步長的希爾排序比插入排序和堆排序都要快,甚至在小數組中比快速排序還快,但是在涉及大量數據時希爾排序還是比快速排序慢。
2. 希爾排序的分析比較復雜,使用Hibbard’s 遞減步長序列的時間復雜度為O(N3/2),平均時間復雜度大約為O(N5/4) ,具體的復雜度目前仍存在爭議。
3. 實驗表明,對于中型的序列( 萬),希爾排序的時間復雜度接近最快的排序算法的時間復雜度nlogn。
最后總結一下本文介紹的三種排序算法的最好最壞和平均時間復雜度。
| 名稱 | 最好 | 平均 | 最壞 | 內存占用 | 穩定排序 |
| 插入排序 | n | n2 | n2 | 1 | 是 |
| 選擇排序 | n2 | n2 | n2 | 1 | 否 |
| 希爾排序 | n | nlog2n 或 n3/2 | 依賴于增量遞減序列目前最好的是 nlog2n | 1 | 否 |
轉載于:https://www.cnblogs.com/wxgblogs/p/5499569.html
總結
- 上一篇: 来一个可能防止恶意采集和爬虫的SH
- 下一篇: nginx源码学习Unix - Unix