一、奇怪的現象
研究快速排序很久了,發現一個古怪的實情:這算法描述起來很簡單,寫一個正確的出來實在不容易.寫一個優秀的快速排序算法更是難上加難.
也難怪該算法提出來過了很久才有人寫出一個正確的算法,過了很久才優秀的版本出來.
二、原理描述
- 從數列中挑出一個元素,稱為 "基准"(pivot),
- 重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作。
- 遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序.
三、最容易讓人理解的版本
一個List形式的數組,找到其中的第一個做基准,遍歷剩下的數據,然后創建兩個左右List,大的放右邊,小的放左邊.然后對左右List各自進行如上操作.......
詳情見代碼
using System; using System.Collections.Generic; using System.Text; namespace test { class Program { static void Main(string[]args) { List<int> array=new List<int>(); array.AddRange(new int[]{3,2,9,5,8,6,23,23,222,12,21}); var t1=DateTime.Now.Ticks; sort(array); var t2=DateTime.Now.Ticks; Console.WriteLine(t2-t1); Console.ReadLine(); } static int Count=0; public static void sort(List<int> array) { var guid=Count++; Console.WriteLine(); Console.Write("目標字符串:("+guid+")"); Show(array); if (array.Count<=1) { return; } if (array.Count==2) { if (array[0]>array[1]) { var temp=array[0]; array[0]=array[1]; array[1]=temp; Console.Write("雙值交換:"); Show(array); } } else{ var xData=array[0]; var leftList=new List<int>(); var rightList=new List<int>(); for (int i = 1; i < array.Count; i++) { var t=array[i]; if (t<=xData) { leftList.Add(t); }else{ rightList.Add(t); } } Console.WriteLine("中間值:"+xData); Console.Write("左邊的字符串("+guid+"):"); Show(leftList); Console.Write("右邊的字符串("+guid+"):"); Show(rightList); sort(leftList); Console.Write("左邊的字符串(排序后)("+guid+"):"); Show(leftList); sort(rightList); Console.Write("右邊的字符串(排序后)("+guid+"):"); Show(rightList); array.Clear(); array.AddRange(leftList); array.Add(xData); array.AddRange(rightList); Console.Write("排好的("+guid+"):"); Show(array); Console.WriteLine(); leftList.Clear(); rightList.Clear(); } } public static void Show(List<int>array){ foreach (var a in array) { Console.Write(a+","); } Console.WriteLine(); } } }
四、正確但不優化的版本
using System; namespace Sort { class Program { static void Main(string[] args) { string result = string.Empty; int[] unsort = { 2, 0, 3, 7, 5,6 }; //快速排序 QuickSort(unsort, 0, unsort.Length - 1); Console.ReadLine(); } /// <summary> /// 調用快速排序算法 /// </summary> /// <param name="unsort">待排序的整形數組</param> /// <param name="left">左邊起始點</param> /// <param name="right">右邊結束點</param> public static void QuickSort(int[] unsort, int left, int right) { if (left < right) { //獲取一次排序的中間索引位置 int midPosition = GetSplitNum(unsort, left, right); //遞歸實現 QuickSort(unsort, left, midPosition - 1); QuickSort(unsort, midPosition + 1, right); } } static int ORDER_INDEX=1; /// <summary> /// 獲取一次排序的中間索引位置 /// </summary> /// <param name="unsort">待排序的整形數組</param> /// <param name="left">左邊起始點</param> /// <param name="right">右邊結束點</param> public static int GetSplitNum(int[] unsort, int left, int right) { int splitNum = unsort[left]; while (left < right) { /** * 從右端開始比較 * (1)假如從右端過來的數比分隔數要大,則不用處理 * (2)假如從右端過來的數比分隔數要小,則需要挪到分隔線左邊 * */ while (left < right && splitNum <= unsort[right]) { right--; } unsort[left] = unsort[right]; GetPrint(unsort); /** * 從從端開始比較 * (1)假如從左端過來的數比分隔數要小,則不用處理 * (2)假如從左端過來的數比分隔數要大,則需要挪到分隔線右邊 * */ while (left < right && splitNum >= unsort[left]) { left++; } unsort[right] = unsort[left]; GetPrint(unsort); } //一趟比較之后,分隔數的位置就可以確認起來 unsort[left] = splitNum; Console.WriteLine(string.Format("第{0}輪排序完畢",(ORDER_INDEX++))); GetPrint(unsort); return left; } /// <summary> /// 打印輸出結果 /// </summary> /// <param name="unsort">數據</param> public static string GetPrint(int[] unsort) { string result = string.Empty; foreach (int n in unsort) { if (!string.IsNullOrEmpty(result)) { result += string.Format("->{0}", n); } else { result = string.Format("{0}", n); } } Console.WriteLine(result); return result; } public static string GetPrint(int[] unsort,int replaceIndex) { string result = string.Empty; foreach (int n in unsort) { if (!string.IsNullOrEmpty(result)) { result += string.Format("->{0}", n); } else { result = string.Format("{0}", n); } } Console.WriteLine(result+" ==>"+replaceIndex); return result; } } }