快速排序(Quicksort)是對冒泡排序的一種改進。
它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然后再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。快速排序的核心思想是二分查找,找出一個中間值,使得比該中間值大的值均在其右側,比該中間值小的值均在其左側,並且遞歸執行,從而達到有序排列。二分查找相當於構造一顆紅黑樹,從而實現元素的有序排列:
【注1:紅黑樹的實質,是在一組待排序的數據中,找出一個處於中間位置的值,比該值小的數據位於其左下側,比該值大的數據位於其右下側;以此類推。由於選用了紅色和黑色這兩種反差明顯的顏色構建了一顆數Tree,而且具備一些特點,故稱為紅黑樹】
【注2:紅黑樹是一種近似平衡的二叉查找樹,它能夠確保任何一個節點的左右子樹的高度差不會超過二者中較低那個的一陪。具體來說,紅黑樹是滿足如下條件的二叉查找樹(binary search tree):
每個節點要么是紅色,要么是黑色;
根節點必須是黑色;
紅色節點不能連續(也即是,紅色節點的孩子和父親都不能是紅色);
對於每個節點,從該點至null(樹末端)的任何路徑,都含有相同個數的黑色節點。
在樹的結構發生改變時(插入或者刪除操作),往往會破壞上述條件3或條件4,需要通過調整使得查找樹重新滿足紅黑樹的條件。】
【注3:數的高度,是從下往上看,從1開始,最底層是1層;數的深度,是從上往下看,從0開始,只有一個根節點(為於0層),根節點下面的層數就是數的深度。一般來說,數的高度=樹的深度+1。】
我們還使用上述的待排序數據集,演示快速排序。
package 排序算法.快速排序; /** * 快速排序 * @author 李章勇 */ import java.util.Arrays; public class Demo1 { static int k = 0; public static void quickSort(int[] arr, int _left, int _right) { if (_left > _right) { return; } int left = _left; int right = _right; int temp = arr[left]; while (left != right) { while (right > left && arr[right] >= temp) { right--; } arr[left] = arr[right]; while (left < right && arr[left] <= temp) { left++; } arr[right] = arr[left]; }
//到此,left和right重疊相等,得到一個中間值,再繼續對該中間值左右兩側的數據們進行快速排序 arr[left] = temp; System.out.println("第" + (++k) + " 次:" + Arrays.toString(arr)); quickSort(arr, _left, left - 1); quickSort(arr, left + 1, _right); } public static void main(String[] args) { int[] arr = new int[]{15, 32, 14, 86, 54, 78, 36}; System.out.println("排序前的數組:" + Arrays.toString(arr)); quickSort(arr, 0, arr.length - 1); System.out.println("排序后的數組:" + Arrays.toString(arr)); } }
上述代碼運行后,結果如下:
排序前的數組:[15, 32, 14, 86, 54, 78, 36] 第1 次:[14, 15, 32, 86, 54, 78, 36] 第2 次:[14, 15, 32, 86, 54, 78, 36] 第3 次:[14, 15, 32, 86, 54, 78, 36] 第4 次:[14, 15, 32, 36, 54, 78, 86] 第5 次:[14, 15, 32, 36, 54, 78, 86] 第6 次:[14, 15, 32, 36, 54, 78, 86] 第7 次:[14, 15, 32, 36, 54, 78, 86] 排序后的數組:[14, 15, 32, 36, 54, 78, 86]
接下來,我再對上述代碼進行優化:
package 排序算法.快速排序; import java.util.Arrays; /** * 快速排序的優化;本質上是二分查找 */ public class Test { static int k=0; static void quickSort(int[] arr,int _left,int _right){ //左右兩個指針,要確保左指針不能大於右指針,否則不和邏輯;如果左右指針相等,說明數組里只有一個元素,無需排序 if(_left>=_right){ return; } // System.out.println(_left+"--"+_right); boolean boo=true;//默認全部有序,為true for(int i=_left;i<_right;i++){ //如果arr[_left]不是最小值,則意味着出現無序 if(arr[_left]>arr[i+1]){ boo=false; } } if(boo){ quickSort(arr,_left+1,_right); return; } int left=_left; int right=_right; int temp=arr[left]; while(left!=right){//即左指針小於右指針時 while(right>left && arr[right]>=temp){//從右指針開始向左移動,並且比左指針處的元素大時 right--;//右指針向左移動 } //一旦出現右指針指向的元素小於左指針處的元素,則將小數賦予左指針處的元素 arr[left]=arr[right]; //再將左指針向右移動 while(left<right && arr[left]<=temp){ left++; } arr[right]=arr[left]; } arr[left]=temp; System.out.println("第"+(++k)+"次排序:"+ Arrays.toString(arr)); quickSort(arr,_left,left-1); quickSort(arr,left+1,_right); } public static void main(String[] args) { int[] arr=new int[]{15,32,14,86,54,78,36}; System.out.println("排序前的數組:"+Arrays.toString(arr)); quickSort(arr, 0, arr.length-1); System.out.println("排序后的數組:"+Arrays.toString(arr)); } }
上述代碼執行結果如下:
排序前的數組:[15, 32, 14, 86, 54, 78, 36] 第1次排序:[14, 15, 32, 86, 54, 78, 36] 第2次排序:[14, 15, 32, 36, 54, 78, 86] 排序后的數組:[14, 15, 32, 36, 54, 78, 86]
最后,在時間復雜度方面,快速排序的最糟糕狀況是O(n²) ,平均時間復雜度為O(n*logn)。