快速排序思路
以排序數組int numbers = {4,8,2,3,8,9,5,2,3,6,5,8,7,3,6} 爲例
整體思路:
典型的分治思想
1、分解
以數組的第一個元素爲基準進行排序,大於基準元素的 放在右邊,小於基準元素的放在左邊,這樣就將數組分成了三部分
- 左邊數組(小於等於基準數組)
- 基準元素本身
- 右邊數組(大於等於基準數組)
2、遞歸求解
- 分別在第一步基礎上 遞歸地對左邊數組以及右邊數組排序
3、合併
- 就是將數組合並,由於第一步的所有左邊的數組元素 <= 基準元素 <=右邊數組,所以不需要進行任何額外的計算數組元素,合併後的數組就是有序的。
快速排序與歸併排序的不同點也在此處:
快速排序的排序操作發生在第一步分解時;
歸併排序的排序操作發生在第三部合併時;關於歸併排序大家可以看看之前的文章。
上面是整體的思路,我們也可以感受到上述三個步驟最困難的就是第一步,我們如何將數組劃分爲三份,這個比較不好理解,下面我用圖的方式來敘述:
下面請看詳細的圖解:
導出個excel還給我弄個"非會員水印",我窮我忍了。。。
下面咱們擼一下代碼
/**
* 快排 遞歸寫法
* @param arr 排序數組
* @param left 起始腳標
* @param right 結束腳標
* @return 結果
*/
public static int[] quickSort(int[] arr,int left,int right){
if(left < right){
//第一步 分解
int mid = partition(arr,left,right);
//第二步 遞歸求解
quickSort(arr,left,mid-1);
quickSort(arr,mid+1,right);
//第三步 不需要合併 因爲數組本身已經有序
}
return arr;
}
/**
* 分解 將數組 分爲三份 對應上圖中的步驟
*/
public static int partition(int[] arr,int left,int right){
int i = left;
int j = right+1;
//選出一個基準值
int mid = arr[left];
while (true){
//從右向左 找尋比mid小的位置
while (mid < arr[--j] && j >= left );
//從左向右找到比mid大的位置
while (mid > arr[++i] && i < right );
if(i >= j){
break;
}
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
arr[left] = arr[j];
arr[j] = mid;
return j;
}
時間複雜度 T(n) = O(nlogn)。
就地快速排序使用的空間是 O(1) 的 ,是在原數組中排序
而真正消耗空間的就是遞歸調用了,因爲每次遞歸就要保持一些數據;
最優的情況下空間複雜度爲:O(logn) ;每一次都平分數組的情況
最差的情況下空間複雜度爲:O( n ) ;退化爲冒泡排序的情況