<img src="https://www.cnblogs.com/image...; width="100%" hegiht="20%" align=center />golang
關於排序,江湖盛傳有一種分治思想,能大幅度提升排序心法的性能。所謂分治,即:化大爲小,分而治之。達到治小而治大的成效。多年來基於分治思想衍生出多種排序心法,然萬變不離其宗!雖然江湖上算法內功繁多,可是好的算法小編認爲必須符合如下幾個條件,方能真正提升習練者實力。算法
在算法時間複雜度維度,咱們主要對比較和交換的次數作對比,其餘不交換元素的算法,主要會以訪問數組的次數的維度作對比。
其實有不少修煉者對於算法的時間複雜度有點模糊,分不清什麼所謂的 O(n),O(nlogn),O(logn)...等,也許下圖對一些人有一些更直觀的認識。c#
排序算法的額外內存開銷和運行時間同等重要。 就算一個算法時間複雜度比較優秀,空間複雜度很是差,使用的額外內存很是大,菜菜認爲它也算不上一個優秀的算法。
這個指標是菜菜本身加上的,我始終認爲一個優秀的算法最終獲得的結果必須是正確的。就算一個算法擁有很是優秀的時間和空間複雜度,可是結果不正確,致使修煉者經脈逆轉,走火入魔,又有什麼意義呢?
基本思想:選取一個元素做爲分割點,經過遍歷把小於分割點的元素放到分割點左邊,把大於分割點的元素放到分割點元素右邊。而後再按此方法對兩部分數據分別排序,以此類推,直到分割的數組大小爲1。 整個排序過程能夠遞歸進行,以此達到整個數據變成有序序列。
實現快速排序的方式有不少,其中以相似指針移動方式最爲常見,爲何最多見呢?由於它的空間複雜度爲O(1),也就是說是原地排序。數組
{23 58 13 10 57 62} 65 {106 78 95 85} {10 13} 23 {58 57 62} 65 {85 78 95} 106 10 13 23 57 58 62 65 78 85 95 106
關於複雜度相關O(n)等公式,我這裏須要強調一點,公式表明的是算法的複雜度增加的趨勢,而不是具體計算複雜度的公式。好比:O(n²)和O(n)相比較,只是說明 O(n²)增加的趨勢要比o(n)快,並非說明O(n²)的算法比O(n)的算法所用時間必定就要多。
快速排序平均時間複雜度爲O(nlogn),最好狀況下爲O(nlogn),最壞狀況下O(n²)app
基於以上例子來實現的快排,空間複雜度爲O(1),也就是原地排序。dom
舉個例子:
待排序數組:int a[] ={1, 2, 2, 3, 4, 5, 6};性能
在快速排序的隨機選擇比較子(即pivot)階段:
若選擇a[2](即數組中的第二個2)爲比較子,,而把大於等於比較子的數均放置在大數數組中,則a[1](即數組中的第一個2)會到pivot的右邊, 那麼數組中的兩個2非原序(這就是「不穩定」)。
若選擇a[1]爲比較子,而把小於等於比較子的數均放置在小數數組中,則數組中的兩個2順序也非原序。可見快速排序不是穩定的排序。ui
經過以上分析各位俠士是否可以分析出來快速排序有哪些地方存在瑕疵呢?spa
static void Main(string[] args) { List<int> data = new List<int>(); for (int i = 0; i < 11; i++) { data.Add(new Random(Guid.NewGuid().GetHashCode()).Next(1, 100)); } //打印原始數組值 Console.WriteLine($"原始數據: {string.Join(",", data)}"); quickSort(data, 0, data.Count - 1); //打印排序後的數組 Console.WriteLine($"排序數據: {string.Join(",", data)}"); Console.Read(); } public static void quickSort(List <int> source, int left, int right) { int pivot = 0; if (left < right) { pivot = partition(source, left, right); quickSort(source, left, pivot - 1); quickSort(source, pivot + 1, right); } } //對一個數組/列表按照第一個元素 分組排序,返回排序以後key所在的位置索引 private static int partition(List<int> source, int left, int right) { int key = source[left]; while (left < right) { //從右邊篩選 大於選取的值的不動,小於key的交換位置 while (left < right && source[right] >= key) { right--; } source[left] = source[right]; while (left < right && source[left] <= key) { left++; } source[right] = source[left]; } source[left] = key; return left; }
package main import ( "fmt" "math/rand" ) func main() { var data []int for i := 0; i < 10; i++ { data = append(data, rand.Intn(100)) } fmt.Println(data) quickSort(data[:], 0, len(data)-1) fmt.Println(data) } func quickSort(source []int, left int, right int) { var pivot = 0 if left < right { pivot = partition(source, left, right) quickSort(source, left, pivot-1) quickSort(source, pivot+1, right) } } func partition(source []int, left int, right int) int { var key = source[left] for left < right { for left < right && source[right] >= key { right-- } source[left] = source[right] for left < right && source[left] <= key { left++ } source[right] = source[left] } source[left] = key return left }
運行結果:指針
[81 87 47 59 81 18 25 40 56 0] [0 18 25 40 47 56 59 81 81 87]
添加關注,查看更精美版本,收穫更多精彩