目錄
簡介
排序是計算機內經常進行的一種操作,其目的是將一組“無序”的記錄序列調整為“有序”的記錄序列。分內部排序和外部排序。若整個排序過程不需要訪問外存便能完成,則稱此類排序問題為內部排序。反之,若參加排序的記錄數量很大,整個序列的排序過程不可能在內存中完成,則稱此類排序問題為外部排序。內部排序的過程是一個逐步擴大記錄的有序序列長度的過程。
將雜亂無章的數據元素,通過一定的方法按關鍵字順序排列的過程叫做排序。假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,則稱這種排序算法是穩定的;否則稱為不穩定的。
分類
穩定排序:假設在待排序的文件中,存在兩個或兩個以上的記錄具有相同的關鍵字,在
用某種排序法排序后,若這些相同關鍵字的元素的相對次序仍然不變,則這種排序方法
是穩定的。其中冒泡,插入,基數,歸並屬於穩定排序,選擇,快速,希爾,堆屬於不穩定排序。
就地排序:若排序算法所需的輔助空間並不依賴於問題的規模n,即輔助空間為O(1),
則稱為就地排序。(百度百科)
交換排序
冒泡排序
已知一組無序數據a[1]、a[2]、……a[n],需將其按升序排列。首先比較a[1]與a[2]的值,若a[1]大於a[2]則交換兩者的值,否則不變。再比較a[2]與a[3]的值,若a[2]大於a[3]則交換兩者的值,否則不變。再比較a[3]與a[4],以此類推,最后比較a[n-1]與a[n]的值。這樣處理一輪后,a[n]的值一定是這組數據中最大的。再對a[1]~a[n-1]以相同方法處理一輪,則a[n-1]的值一定是a[1]~a[n-1]中最大的。再對a[1]~a[n-2]以相同方法處理一輪,以此類推。共處理n-1輪后a[1]、a[2]、……a[n]就以升序排列了。降序排列與升序排列相類似,若a[1]小於a[2]則交換兩者的值,否則不變,后面以此類推。 總的來講,每一輪排序后最大(或最小)的數將移動到數據序列的最后,理論上總共要進行n(n-1)/2次交換。
代碼實現
1 /// <summary> 2 /// 冒泡排序 3 /// </summary> 4 /// <param name="arry">要排序的整數數組</param> 5 public static void BubbleSort(this int[] arry) 6 { 7 for (int i = 0; i < arry.Length-1; i++) 8 { 9 for (int j = 0; j < arry.Length - 1 - i; j++) 10 { 11 //比較相鄰的兩個元素,如果前面的比后面的大,則交換位置 12 if (arry[j] > arry[j + 1]) 13 { 14 int temp = arry[j + 1]; 15 arry[j + 1] = arry[j]; 16 arry[j] = temp; 17 } 18 } 19 } 20 }
簡單測試
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int[] arry = new int[] { 1, 2, 3, 4, 1, -1 }; 6 arry.BubbleSort(); 7 for (int i = 0; i < arry.Length; i++) 8 { 9 Console.Write("\t" + arry[i]); 10 } 11 Console.Read(); 12 } 13 }
結果
這里采用的是為整型數組添加擴展方法實現的冒泡排序。
優點:穩定
缺點:慢,每次只移動相鄰的兩個元素。
時間復雜度:理想情況下(數組本來就是有序的),此時最好的時間復雜度為o(n),最壞的時間復雜度(數據反序的),此時的時間復雜度為o(n*n) 。冒泡排序的平均時間負責度為o(n*n).
快速排序
設要排序的數組是A[0]……A[N-1],首先任意選取一個數據(通常選用數組的第一個數)作為關鍵數據,然后將所有比它小的數都放到它前面,所有比它大的數都放到它后面,這個過程稱為一趟快速排序。值得注意的是,快速排序不是一種穩定的排序算法,也就是說,多個相同的值的相對位置也許會在算法結束時產生變動。
一趟快速排序的算法是:
1)設置兩個變量i、j,排序開始的時候:i=0,j=N-1;
2)以第一個數組元素作為關鍵數據,賦值給key,即key=A[0];
3)從j開始向前搜索,即由后開始向前搜索(j--),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向后搜索,即由前開始向后搜索(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重復第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指針位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令循環結束)。
代碼實現
1 /// <summary> 2 /// 快速排序 3 /// </summary> 4 /// <param name="arry">要排序的數組</param> 5 /// <param name="left">低位</param> 6 /// <param name="right">高位</param> 7 public static void QuickSort(this int[] arry, int left, int right) 8 { 9 //左邊索引小於右邊,則還未排序完成 10 if (left < right) 11 { 12 //取中間的元素作為比較基准,小於他的往左邊移,大於他的往右邊移 13 int middle = arry[(left + right) / 2]; 14 int i = left - 1; 15 int j = right + 1; 16 while (true) 17 { 18 //移動下標,左邊的往右移動,右邊的向左移動 19 while (arry[++i] < middle && i < right); 20 while (arry[--j] > middle && j > 0); 21 if (i >= j) 22 break; 23 //交換位置 24 int number = arry[i]; 25 arry[i] = arry[j]; 26 arry[j] = number; 27 28 } 29 QuickSort(arry, left, i - 1); 30 QuickSort(arry, j + 1, right); 31 } 32 }
簡單測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12,1,1}; 4 //arry.BubbleSort(); 5 arry.QuickSort(0, arry.Length-1 ); 6 for (int i = 0; i < arry.Length; i++) 7 { 8 Console.Write("\t" + arry[i]); 9 } 10 Console.Read(); 11 }
結果
插入排序
直接插入排序
每次從無序表中取出第一個元素,把它插入到有序表的合適位置,使有序表仍然有序。
第一趟比較前兩個數,然后把第二個數按大小插入到有序表中; 第二趟把第三個數據與前兩個數從前向后掃描,把第三個數按大小插入到有序表中;依次進行下去,進行了(n-1)趟掃描以后就完成了整個排序過程。
直接插入排序屬於穩定的排序,最壞時間復雜性為O(n^2),空間復雜度為O(1)。
直接插入排序是由兩層嵌套循環組成的。外層循環標識並決定待比較的數值。內層循環為待比較數值確定其最終位置。直接插入排序是將待比較的數值與它的前一個數值進行比較,所以外層循環是從第二個數值開始的。當前一數值比待比較數值大的情況下繼續循環比較,直到找到比待比較數值小的並將待比較數值置入其后一位置,結束該次循環。
值得注意的是,我們必需用一個存儲空間來保存當前待比較的數值,因為當一趟比較完成時,我們要將待比較數值置入比它小的數值的后一位 插入排序類似玩牌時整理手中紙牌的過程。插入排序的基本方法是:每步將一個待排序的記錄按其關鍵字的大小插到前面已經排序的序列中的適當位置,直到全部記錄插入完畢為止。
代碼實現
1 /// <summary> 2 /// 直接插入排序 3 /// </summary> 4 /// <param name="arry">要排序的數組</param> 5 public static void InsertSort(this int[] arry) 6 { 7 //直接插入排序是將待比較的數值與它的前一個數值進行比較,所以外層循環是從第二個數值開始的 8 for (int i = 1; i < arry.Length; i++) 9 { 10 //如果當前元素小於其前面的元素 11 if (arry[i] < arry[i - 1]) 12 { 13 //用一個變量來保存當前待比較的數值,因為當一趟比較完成時,我們要將待比較數值置入比它小的數值的后一位 14 int temp = arry[i]; 15 int j = 0; 16 for (j = i - 1; j >= 0 && temp < arry[j]; j--) 17 { 18 arry[j + 1] = arry[j]; 19 } 20 arry[j + 1] = temp; 21 } 22 } 23 }
測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12}; 4 //arry.BubbleSort(); 5 //arry.QuickSort(0, arry.Length-1 ); 6 arry.InsertSort(); 7 for (int i = 0; i < arry.Length; i++) 8 { 9 Console.Write("\t" + arry[i]); 10 } 11 Console.Read(); 12 }
結果
希爾排序
希爾排序(Shell Sort)是插入排序的一種。也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法。該方法因DL.Shell於1959年提出而得名。
希爾排序是基於插入排序的以下兩點性質而提出改進方法的:
插入排序在對幾乎已經排好序的數據操作時,效率高,即可以達到線性排序的效率。
但插入排序一般來說是低效的,因為插入排序每次只能將數據移動一位。
基本思想
先取一個小於n的整數d1作為第一個增量,把文件的全部記錄分組。所有距離為d1的倍數的記錄放在同一個組中。先在各組內進行直接插入排序;然后,取第二個增量d2<d1重復上述的分組和排序,直至所取的增量 =1( < …<d2<d1),即所有記錄放在同一組中進行直接插入排序為止。
該方法實質上是一種分組插入方法
比較相隔較遠距離(稱為增量)的數,使得數移動時能跨過多個元素,則進行一次比[2] 較就可能消除多個元素交換。D.L.shell於1959年在以他名字命名的排序算法中實現了這一思想。算法先將要排序的一組數按某個增量d分成若干組,每組中記錄的下標相差d.對每組中全部元素進行排序,然后再用一個較小的增量對它進行,在每組中再進行排序。當增量減到1時,整個要排序的數被分成一組,排序完成。
一般的初次取序列的一半為增量,以后每次減半,直到增量為1。
代碼實現
1 /// <summary> 2 /// 希爾排序 3 /// </summary> 4 /// <param name="arry">待排序的數組</param> 5 public static void ShellSort(this int[] arry) 6 { 7 int length = arry.Length; 8 for (int h = length / 2; h > 0; h = h / 2) 9 { 10 //here is insert sort 11 for (int i = h; i < length; i++) 12 { 13 int temp = arry[i]; 14 if (temp < arry[i - h]) 15 { 16 for (int j = 0; j < i; j += h) 17 { 18 if (temp<arry[j]) 19 { 20 temp = arry[j]; 21 arry[j] = arry[i]; 22 arry[i] = temp; 23 } 24 } 25 } 26 } 27 } 28 }
測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12}; 4 //arry.BubbleSort(); 5 //arry.QuickSort(0, arry.Length-1 ); 6 //arry.InsertSort(); 7 arry.ShellSort(); 8 for (int i = 0; i < arry.Length; i++) 9 { 10 Console.Write("\t" + arry[i]); 11 } 12 Console.Read(); 13 }
結果
選擇排序
簡單選擇排序
設所排序序列的記錄個數為n。i取1,2,…,n-1,從所有n-i+1個記錄(Ri,Ri+1,…,Rn)中找出排序碼最小的記錄,與第i個記錄交換。執行n-1趟 后就完成了記錄序列的排序。
在簡單選擇排序過程中,所需移動記錄的次數比較少。最好情況下,即待排序記錄初始狀態就已經是正序排列了,則不需要移動記錄。
最壞情況下,即待排序記錄初始狀態是按逆序排列的,則需要移動記錄的次數最多為3(n-1)。簡單選擇排序過程中需要進行的比較次數與初始狀態下待排序的記錄序列的排列情況無關。當i=1時,需進行n-1次比較;當i=2時,需進行n-2次比較;依次類推,共需要進行的比較次數是(n-1)+(n-2)+…+2+1=n(n-1)/2,即進行比較操作的時間復雜度為O(n^2),進行移動操作的時間復雜度為O(n)。
代碼實現
1 /// <summary> 2 /// 簡單選擇排序 3 /// </summary> 4 /// <param name="arry">待排序的數組</param> 5 public static void SimpleSelectSort(this int[] arry) 6 { 7 int tmp = 0; 8 int t = 0;//最小數標記 9 for (int i = 0; i < arry.Length; i++) 10 { 11 t = i; 12 for (int j = i + 1; j < arry.Length; j++) 13 { 14 if (arry[t] > arry[j]) 15 { 16 t = j; 17 } 18 } 19 tmp = arry[i]; 20 arry[i] = arry[t]; 21 arry[t] = tmp; 22 } 23 }
測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12}; 4 //arry.BubbleSort(); 5 //arry.QuickSort(0, arry.Length-1 ); 6 //arry.InsertSort(); 7 //arry.ShellSort(); 8 arry.SimpleSelectSort(); 9 for (int i = 0; i < arry.Length; i++) 10 { 11 Console.Write("\t" + arry[i]); 12 } 13 Console.Read(); 14 }
結果
堆排序
堆排序(Heapsort)是指利用堆積樹(堆)這種數據結構所設計的一種排序算法,它是選擇排序的一種。可以利用數組的特點快速定位指定索引的元素。堆分為大根堆和小根堆,是完全二叉樹。大根堆的要求是每個節點的值都不大於其父節點的值,即A[PARENT[i]] >= A[i]。在數組的非降序排序中,需要使用的就是大根堆,因為根據大根堆的要求可知,最大的值一定在堆頂。
代碼實現
1 /// <summary> 2 /// 堆排序 3 /// </summary> 4 /// <param name="arry"></param> 5 public static void HeapSort(this int[] arry, int top) 6 { 7 List<int> topNode = new List<int>(); 8 9 for (int i = arry.Length / 2 - 1; i >= 0; i--) 10 { 11 HeapAdjust(arry, i, arry.Length); 12 } 13 14 for (int i = arry.Length - 1; i >= arry.Length - top; i--) 15 { 16 int temp = arry[0]; 17 arry[0] = arry[i]; 18 arry[i] = temp; 19 HeapAdjust(arry, 0, i); 20 } 21 } 22 /// <summary> 23 /// 構建堆 24 /// </summary> 25 /// <param name="arry"></param> 26 /// <param name="parent"></param> 27 /// <param name="length"></param> 28 private static void HeapAdjust(int[] arry, int parent, int length) 29 { 30 int temp = arry[parent]; 31 32 int child = 2 * parent + 1; 33 34 while (child < length) 35 { 36 if (child + 1 < length && arry[child] < arry[child + 1]) child++; 37 38 if (temp >= arry[child]) 39 break; 40 41 arry[parent] = arry[child]; 42 43 parent = child; 44 45 child = 2 * parent + 1; 46 } 47 48 arry[parent] = temp; 49 }
測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12}; 4 //arry.BubbleSort(); 5 //arry.QuickSort(0, arry.Length-1 ); 6 //arry.InsertSort(); 7 //arry.ShellSort(); 8 //arry.SimpleSelectSort(); 9 arry.HeapSort(arry.Length); 10 for (int i = 0; i < arry.Length; i++) 11 { 12 Console.Write("\t" + arry[i]); 13 } 14 Console.Read(); 15 }
結果
歸並排序
歸並排序是建立在歸並操作上的一種有效的排序算法,該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合並,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合並成一個有序表,稱為二路歸並。
歸並過程為:比較a[i]和a[j]的大小,若a[i]≤a[j],則將第一個有序表中的元素a[i]復制到r[k]中,並令i和k分別加上1;否則將第二個有序表中的元素a[j]復制到r[k]中,並令j和k分別加上1,如此循環下去,直到其中一個有序表取完,然后再將另一個有序表中剩余的元素復制到r中從下標k到下標t的單元。歸並排序的算法我們通常用遞歸實現,先把待排序區間[s,t]以中點二分,接着把左邊子區間排序,再把右邊子區間排序,最后把左區間和右區間用一次歸並操作合並成有序的區間[s,t]。
歸並操作(merge),也叫歸並算法,指的是將兩個順序序列合並成一個順序序列的方法。
如 設有數列{6,202,100,301,38,8,1}
初始狀態:6,202,100,301,38,8,1
第一次歸並后:{6,202},{100,301},{8,38},{1},比較次數:3;
第二次歸並后:{6,100,202,301},{1,8,38},比較次數:4;
第三次歸並后:{1,6,8,38,100,202,301},比較次數:4;
總的比較次數為:3+4+4=11,;
逆序數為14;
歸並操作的工作原理如下:
第一步:申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並后的序列
第二步:設定兩個指針,最初位置分別為兩個已經排序序列的起始位置
第三步:比較兩個指針所指向的元素,選擇相對小的元素放入到合並空間,並移動指針到下一位置
重復步驟3直到某一指針超出序列尾
將另一序列剩下的所有元素直接復制到合並序列尾
代碼實現
1 /// <summary> 2 /// 歸並排序 3 /// </summary> 4 /// <param name="arry"></param> 5 /// <param name="first"></param> 6 /// <param name="last"></param> 7 public static void MergeSort(this int[] arry, int first, int last) 8 { 9 if (first + 1 < last) 10 { 11 int mid = (first + last) / 2; 12 MergeSort(arry, first, mid); 13 MergeSort(arry, mid, last); 14 Merger(arry, first, mid, last); 15 } 16 } 17 /// <summary> 18 /// 歸並 19 /// </summary> 20 /// <param name="arry"></param> 21 /// <param name="first"></param> 22 /// <param name="mid"></param> 23 /// <param name="last"></param> 24 private static void Merger(int[] arry, int first, int mid, int last) 25 { 26 Queue<int> tempV = new Queue<int>(); 27 int indexA, indexB; 28 //設置indexA,並掃描subArray1 [first,mid] 29 //設置indexB,並掃描subArray2 [mid,last] 30 indexA = first; 31 indexB = mid; 32 //在沒有比較完兩個子標的情況下,比較 v[indexA]和v[indexB] 33 //將其中小的放到臨時變量tempV中 34 while (indexA < mid && indexB < last) 35 { 36 if (arry[indexA] < arry[indexB]) 37 { 38 tempV.Enqueue(arry[indexA]); 39 indexA++; 40 } 41 else 42 { 43 tempV.Enqueue(arry[indexB]); 44 indexB++; 45 } 46 } 47 //復制沒有比較完子表中的元素 48 while (indexA < mid) 49 { 50 tempV.Enqueue(arry[indexA]); 51 indexA++; 52 } 53 while (indexB < last) 54 { 55 tempV.Enqueue(arry[indexB]); 56 indexB++; 57 } 58 int index = 0; 59 while (tempV.Count > 0) 60 { 61 arry[first + index] = tempV.Dequeue(); 62 index++; 63 } 64 }
測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12}; 4 //arry.BubbleSort(); 5 //arry.QuickSort(0, arry.Length-1 ); 6 //arry.InsertSort(); 7 //arry.ShellSort(); 8 //arry.SimpleSelectSort(); 9 //arry.HeapSort(arry.Length); 10 arry.MergeSort(0, arry.Length); 11 for (int i = 0; i < arry.Length; i++) 12 { 13 Console.Write("\t" + arry[i]); 14 } 15 Console.Read(); 16 }
結果
基數排序
基數排序(radix sort)屬於“分配式排序”(distribution sort),又稱“桶子法”(bucket sort)或bin sort,顧名思義,它是透過鍵值的部份資訊,將要排序的元素分配至某些“桶”中,藉以達到排序的作用,基數排序法是屬於穩定性的排序,其時間復雜度為O (nlog(r)m),其中r為所采取的基數,而m為堆數,在某些時候,基數排序法的效率高於其它的穩定性排序法。
代碼實現
1 /// <summary> 2 /// 基數排序 3 /// 約定:待排數字中沒有0,如果某桶內數字為0則表示該桶未被使用,輸出時跳過即可 4 /// </summary> 5 /// <param name="arry">待排數組</param> 6 /// <param name="array_x">桶數組第一維長度</param> 7 /// <param name="array_y">桶數組第二維長度</param> 8 public static void RadixSort(this int[] arry, int array_x = 10, int array_y = 100) 9 { 10 /* 最大數字不超過999999999...(array_x個9) */ 11 for (int i = 0; i < array_x; i++) 12 { 13 int[,] bucket = new int[array_x, array_y]; 14 foreach (var item in arry) 15 { 16 int temp = (item / (int)Math.Pow(10, i)) % 10; 17 for (int l = 0; l < array_y; l++) 18 { 19 if (bucket[temp, l] == 0) 20 { 21 bucket[temp, l] = item; 22 break; 23 } 24 } 25 } 26 for (int o = 0, x = 0; x < array_x; x++) 27 { 28 for (int y = 0; y < array_y; y++) 29 { 30 if (bucket[x, y] == 0) continue; 31 arry[o++] = bucket[x, y]; 32 } 33 } 34 } 35 36 }
測試
1 static void Main(string[] args) 2 { 3 int[] arry = new int[] { 34,1,221,50,44,58,12}; 4 //arry.BubbleSort(); 5 //arry.QuickSort(0, arry.Length-1 ); 6 //arry.InsertSort(); 7 //arry.ShellSort(); 8 //arry.SimpleSelectSort(); 9 //arry.HeapSort(arry.Length); 10 //arry.MergeSort(0, arry.Length); 11 arry.RadixSort(); 12 for (int i = 0; i < arry.Length; i++) 13 { 14 Console.Write("\t" + arry[i]); 15 } 16 Console.Read(); 17 }
結果
總結
整數數組各種排序算法擴展類

1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Threading.Tasks; 6 7 namespace Wolfy.Sort 8 { 9 /// <summary> 10 /// 排序輔助類 11 /// </summary> 12 public static class SortExtention 13 { 14 /// <summary> 15 /// 冒泡排序 16 /// </summary> 17 /// <param name="arry">要排序的整數數組</param> 18 public static void BubbleSort(this int[] arry) 19 { 20 for (int i = 0; i < arry.Length-1; i++) 21 { 22 for (int j = 0; j < arry.Length - 1 - i; j++) 23 { 24 //比較相鄰的兩個元素,如果前面的比后面的大,則交換位置 25 if (arry[j] > arry[j + 1]) 26 { 27 int temp = arry[j + 1]; 28 arry[j + 1] = arry[j]; 29 arry[j] = temp; 30 } 31 } 32 } 33 } 34 /// <summary> 35 /// 快速排序 36 /// </summary> 37 /// <param name="arry">要排序的數組</param> 38 /// <param name="left">低位</param> 39 /// <param name="right">高位</param> 40 public static void QuickSort(this int[] arry, int left, int right) 41 { 42 //左邊索引小於右邊,則還未排序完成 43 if (left < right) 44 { 45 //取中間的元素作為比較基准,小於他的往左邊移,大於他的往右邊移 46 int middle = arry[(left + right) / 2]; 47 int i = left - 1; 48 int j = right + 1; 49 while (true) 50 { 51 //移動下標,左邊的往右移動,右邊的向左移動 52 while (arry[++i] < middle && i < right) ; 53 while (arry[--j] > middle && j > 0) ; 54 if (i >= j) 55 break; 56 //交換位置 57 int number = arry[i]; 58 arry[i] = arry[j]; 59 arry[j] = number; 60 61 } 62 QuickSort(arry, left, i - 1); 63 QuickSort(arry, j + 1, right); 64 } 65 } 66 /// <summary> 67 /// 直接插入排序 68 /// </summary> 69 /// <param name="arry">要排序的數組</param> 70 public static void InsertSort(this int[] arry) 71 { 72 //直接插入排序是將待比較的數值與它的前一個數值進行比較,所以外層循環是從第二個數值開始的 73 for (int i = 1; i < arry.Length; i++) 74 { 75 //如果當前元素小於其前面的元素 76 if (arry[i] < arry[i - 1]) 77 { 78 //用一個變量來保存當前待比較的數值,因為當一趟比較完成時,我們要將待比較數值置入比它小的數值的后一位 79 int temp = arry[i]; 80 int j = 0; 81 for (j = i - 1; j >= 0 && temp < arry[j]; j--) 82 { 83 arry[j + 1] = arry[j]; 84 } 85 arry[j + 1] = temp; 86 } 87 } 88 } 89 /// <summary> 90 /// 希爾排序 91 /// </summary> 92 /// <param name="arry">待排序的數組</param> 93 public static void ShellSort(this int[] arry) 94 { 95 int length = arry.Length; 96 for (int h = length / 2; h > 0; h = h / 2) 97 { 98 //here is insert sort 99 for (int i = h; i < length; i++) 100 { 101 int temp = arry[i]; 102 if (temp < arry[i - h]) 103 { 104 for (int j = 0; j < i; j += h) 105 { 106 if (temp < arry[j]) 107 { 108 temp = arry[j]; 109 arry[j] = arry[i]; 110 arry[i] = temp; 111 } 112 } 113 } 114 } 115 } 116 } 117 /// <summary> 118 /// 簡單選擇排序 119 /// </summary> 120 /// <param name="arry">待排序的數組</param> 121 public static void SimpleSelectSort(this int[] arry) 122 { 123 int tmp = 0; 124 int t = 0;//最小數標記 125 for (int i = 0; i < arry.Length; i++) 126 { 127 t = i; 128 for (int j = i + 1; j < arry.Length; j++) 129 { 130 if (arry[t] > arry[j]) 131 { 132 t = j; 133 } 134 } 135 tmp = arry[i]; 136 arry[i] = arry[t]; 137 arry[t] = tmp; 138 } 139 } 140 /// <summary> 141 /// 堆排序 142 /// </summary> 143 /// <param name="arry"></param> 144 public static void HeapSort(this int[] arry, int top) 145 { 146 List<int> topNode = new List<int>(); 147 148 for (int i = arry.Length / 2 - 1; i >= 0; i--) 149 { 150 HeapAdjust(arry, i, arry.Length); 151 } 152 153 for (int i = arry.Length - 1; i >= arry.Length - top; i--) 154 { 155 int temp = arry[0]; 156 arry[0] = arry[i]; 157 arry[i] = temp; 158 HeapAdjust(arry, 0, i); 159 } 160 } 161 /// <summary> 162 /// 構建堆 163 /// </summary> 164 /// <param name="arry"></param> 165 /// <param name="parent"></param> 166 /// <param name="length"></param> 167 private static void HeapAdjust(int[] arry, int parent, int length) 168 { 169 int temp = arry[parent]; 170 171 int child = 2 * parent + 1; 172 173 while (child < length) 174 { 175 if (child + 1 < length && arry[child] < arry[child + 1]) child++; 176 177 if (temp >= arry[child]) 178 break; 179 180 arry[parent] = arry[child]; 181 182 parent = child; 183 184 child = 2 * parent + 1; 185 } 186 187 arry[parent] = temp; 188 } 189 /// <summary> 190 /// 歸並排序 191 /// </summary> 192 /// <param name="arry"></param> 193 /// <param name="first"></param> 194 /// <param name="last"></param> 195 public static void MergeSort(this int[] arry, int first, int last) 196 { 197 if (first + 1 < last) 198 { 199 int mid = (first + last) / 2; 200 MergeSort(arry, first, mid); 201 MergeSort(arry, mid, last); 202 Merger(arry, first, mid, last); 203 } 204 } 205 /// <summary> 206 /// 歸並 207 /// </summary> 208 /// <param name="arry"></param> 209 /// <param name="first"></param> 210 /// <param name="mid"></param> 211 /// <param name="last"></param> 212 private static void Merger(int[] arry, int first, int mid, int last) 213 { 214 Queue<int> tempV = new Queue<int>(); 215 int indexA, indexB; 216 //設置indexA,並掃描subArray1 [first,mid] 217 //設置indexB,並掃描subArray2 [mid,last] 218 indexA = first; 219 indexB = mid; 220 //在沒有比較完兩個子標的情況下,比較 v[indexA]和v[indexB] 221 //將其中小的放到臨時變量tempV中 222 while (indexA < mid && indexB < last) 223 { 224 if (arry[indexA] < arry[indexB]) 225 { 226 tempV.Enqueue(arry[indexA]); 227 indexA++; 228 } 229 else 230 { 231 tempV.Enqueue(arry[indexB]); 232 indexB++; 233 } 234 } 235 //復制沒有比較完子表中的元素 236 while (indexA < mid) 237 { 238 tempV.Enqueue(arry[indexA]); 239 indexA++; 240 } 241 while (indexB < last) 242 { 243 tempV.Enqueue(arry[indexB]); 244 indexB++; 245 } 246 int index = 0; 247 while (tempV.Count > 0) 248 { 249 arry[first + index] = tempV.Dequeue(); 250 index++; 251 } 252 } 253 254 /// <summary> 255 /// 基數排序 256 /// 約定:待排數字中沒有0,如果某桶內數字為0則表示該桶未被使用,輸出時跳過即可 257 /// </summary> 258 /// <param name="arry">待排數組</param> 259 /// <param name="array_x">桶數組第一維長度</param> 260 /// <param name="array_y">桶數組第二維長度</param> 261 public static void RadixSort(this int[] arry, int array_x = 10, int array_y = 100) 262 { 263 /* 最大數字不超過999999999...(array_x個9) */ 264 for (int i = 0; i < array_x; i++) 265 { 266 int[,] bucket = new int[array_x, array_y]; 267 foreach (var item in arry) 268 { 269 int temp = (item / (int)Math.Pow(10, i)) % 10; 270 for (int l = 0; l < array_y; l++) 271 { 272 if (bucket[temp, l] == 0) 273 { 274 bucket[temp, l] = item; 275 break; 276 } 277 } 278 } 279 for (int o = 0, x = 0; x < array_x; x++) 280 { 281 for (int y = 0; y < array_y; y++) 282 { 283 if (bucket[x, y] == 0) continue; 284 arry[o++] = bucket[x, y]; 285 } 286 } 287 } 288 289 } 290 291 } 292 293 }
各排序算法時間復雜度與空間復雜度
參考文章
遞歸算法學習系列二(歸並排序) 張玉彬
百度百科