上一講大概介紹了一個排序算法的概念與內存結構圖,主要選自《算法精解》,各人認爲,這本書寫的仍是不錯的,你們能夠直接下載epub版,而後在面度閱讀APP上看,挺方便的!其實,學習,很簡單!html
排序是指將元素集合按照規定的順序排列。一般有兩種排序方法,升序排列和降序排列。例如,對整數集{5,2,7,1}進行升序排列,結果爲{1,2,5,7},對其進行降序排列結果爲{7,5,2,1}。總的來講,排序的目的是使數據可以以更有意義的形式表現出來。雖然排序最顯著的應用是排列數據以顯示它,但它每每能夠用來解決其餘的問題,特別是做爲某些已成型算法的一部分。
總的來講,排序算法分爲兩大類:比較排序和線性時間排序。比較排序依賴於比較和交換來將元素移動到正確的位置上。使人驚訝的是,並非全部的排序算法都依賴於比較。對於那些確實依賴於比較來進行排序的算法來講,它們的運行時間每每不可能小於O(nlg n)。對於線性時間排序,從它的名字就能夠看出,它的運行時間每每與它處理的數據元素個數成正比,即爲O(n)。遺憾的是,線性時間排序依賴於數據集合中的某些特徵,因此咱們並非在全部的場合都可以使用它。某些排序算法只使用數據自己的存儲空間來處理和輸出數據(這些稱爲就地排序),而有一些則須要額外的空間來處理和輸出數據(雖然可能最終結果仍是會拷貝到原始的內存空間中)。算法
/// <summary> /// 排序算法 /// 做者:倉儲大叔 /// 代碼來源:部分自寫,部分網上摘錄,都通過測試能夠放心使用 /// </summary> public class SortHelper { #region Public Methods /// <summary> /// 插入排序 /// </summary> public static void InsertSort(List<int> list) { /* * 複雜度 O(n^2) * 插入排序從根本上來講,就是每次從未排序的數據集中取出一個元素,插入已排好序的數據集中。在如下所展現的實現中,兩個數據集都存放在data中,data是一塊鏈接的存儲區域。最初,data包含size個無序元素。隨着issort的運行,data逐漸被有序數據集所取代,直到issort返回(此時,data已是一個有序數據集)。雖然實現插入排序用到連續的存儲空間,但它也能用鏈表來實現(並非全部的排序均可以使用鏈表來實現),而且效率不差。 */ for (int j = 1; j < list.Count; j++) { int i = j - 1; int currnet = list[j]; while (i >= 0 && currnet > list[i]) { list[i + 1] = list[i]; i--; } list[i + 1] = currnet; } } /// <summary> /// 快速排序 /// </summary> /// <param name="list">目標數組</param> /// <param name="left">子表的起始位置</param> /// <param name="right">子表的終止位置</param> public static void QuickSort(List<int> list, int left, int right) { /* * 複雜度 O(nlg^n) * 描述 利用快速排序將數組data中的元素進行排序。數組中的元素個數由size決定。而每一個元素的大小由esize決定。參數i和k定義當前進行排序的兩個部分,其值分別初始化爲0和size-1。函數指針compare會指向一個用戶定義的函數來比較元素大小。其函數功能與issort中描述的同樣。當qksort返回時,data包含已排序的元素 */ if (left < right) { int i = Division(list, left, right); //對樞軸的左邊部分進行排序 QuickSort(list, i + 1, right); //對樞軸的右邊部分進行排序 QuickSort(list, left, i - 1); } } /// <summary> /// 歸併排序 /// </summary> /// <param name="array">目標數組</param> /// <param name="first">子表的起始位置</param> /// <param name="last">子表的終止位置</param> public static void MergeSortFunction(List<int> array, int first, int last) { /* * 複雜度 O(nlg^n) * 描述 利用歸併排序將數組data中的元素進行排序。數組中的元素個數由size決定。而每一個元素的大小由esize決定。參數i和k定義當前進行排序的兩個部分,其值分別初始化爲0和size-1。函數指針compare會指向一個用戶定義的函數來比較元素大小。其函數功能與issort中描述的同樣。當mgsort返回時,data中包含已排序的元素。 */ if (first < last) //子表的長度大於1,則進入下面的遞歸處理 { int mid = (first + last) / 2; //子表劃分的位置 MergeSortFunction(array, first, mid); //對劃分出來的左側子表進行遞歸劃分 MergeSortFunction(array, mid + 1, last); //對劃分出來的右側子表進行遞歸劃分 MergeSortCore(array, first, mid, last); //對左右子表進行有序的整合(歸併排序的核心部分) } } /// <summary> /// 計數排序 /// </summary> /// <param name="arrayToSort">要排序的數組</param> /// <param name="maxValue">數組的最大值加一</param> /// <returns>計數排序後的結果</returns> public static List<int> CountingSort(List<int> arrayA, int arrange) { /* 複雜度 O(n+k),n爲要排序的元素的個數,k爲data中最大的整數加1。 * 計數排序是一種高效的線性排序,它經過計算一個集合中元素出現的次數來肯定集合如何排列。不一樣於以前介紹的一些算法是基於比較的,計數排序不須要進行元素比較,並且它的運行效率要比效率爲O(nlg n)比較排序高。 */ int[] arrayResult = new int[arrayA.Count]; int[] arrayTemp = new int[arrange + 1]; for (int i = 0; i <= arrange; i++) { arrayTemp[i] = 0; } for (int j = 0; j < arrayA.Count; j++) { arrayTemp[arrayA[j]] += 1; } for (int k = 1; k <= arrange; k++) { arrayTemp[k] += arrayTemp[k - 1]; } for (int m = arrayA.Count - 1; m >= 0; m--) { arrayResult[arrayTemp[arrayA[m]] - 1] = arrayA[m]; arrayTemp[arrayA[m]] -= 1; } return arrayResult.ToList(); } /// <summary> /// 冒泡排序 /// </summary> /// <param name="arr"></param> public void EbullitionSort(List<int> arr) { /* * 複雜度O(n^2) * 對1至n個記錄,在第i趟排序中設置標誌flag:=true,未排序的標誌。從下往上掃描,以j做爲內層循環變量,共作n-i次比較。在第j趟比較中,若r[j+1]<r[j]則交換,並至flag爲false。在一趟排序結束後,若flag爲true,則終止排序。 */ int i, j, temp; bool done = false; j = 1; while ((j < arr.Count) && (!done))//判斷長度 { done = true; for (i = 0; i < arr.Count - j; i++) { if (arr[i] > arr[i + 1]) { done = false; temp = arr[i]; arr[i] = arr[i + 1];//交換數據 arr[i + 1] = temp; } } j++; } } #endregion #region Private Methods /// <summary> /// 歸併排序的核心部分:將兩個有序的左右子表(以mid區分),合併成一個有序的表 /// </summary> /// <param name="array"></param> /// <param name="first"></param> /// <param name="mid"></param> /// <param name="last"></param> private static void MergeSortCore(List<int> array, int first, int mid, int last) { int indexA = first; //左側子表的起始位置 int indexB = mid + 1; //右側子表的起始位置 int[] temp = new int[last + 1]; //聲明數組(暫存左右子表的全部有序數列):長度等於左右子表的長度之和。 int tempIndex = 0; while (indexA <= mid && indexB <= last) //進行左右子表的遍歷,若是其中有一個子表遍歷完,則跳出循環 { if (array[indexA] <= array[indexB]) //此時左子表的數 <= 右子表的數 { temp[tempIndex++] = array[indexA++]; //將左子表的數放入暫存數組中,遍歷左子表下標++ } else//此時左子表的數 > 右子表的數 { temp[tempIndex++] = array[indexB++]; //將右子表的數放入暫存數組中,遍歷右子表下標++ } } //有一側子表遍歷完後,跳出循環,將另一側子表剩下的數一次放入暫存數組中(有序) while (indexA <= mid) { temp[tempIndex++] = array[indexA++]; } while (indexB <= last) { temp[tempIndex++] = array[indexB++]; } //將暫存數組中有序的數列寫入目標數組的制定位置,使進行歸併的數組段有序 tempIndex = 0; for (int i = first; i <= last; i++) { array[i] = temp[tempIndex++]; } } /// <summary> /// 獲取按樞軸值左右分流後樞軸的位置 /// </summary> /// <param name="list"></param> /// <param name="left"></param> /// <param name="right"></param> /// <returns></returns> private static int Division(List<int> list, int left, int right) { while (left < right) { int num = list[left]; //將首元素做爲樞軸 if (num > list[left + 1]) { list[left] = list[left + 1]; list[left + 1] = num; left++; } else { int temp = list[right]; list[right] = list[left + 1]; list[left + 1] = temp; right--; } } return left; //指向的此時樞軸的位置 } #endregion }
對於算法與數據結構,咱們還會繼續,有理論,有實現,但願它能夠不那麼枯燥!數組
感謝我們的閱讀!數據結構