圖解快速排序


基本思想:將一組要排序的數列分成兩部分,其中一部分的值都比另一部分的小;然后按照這個方法分別對兩部分數據進行快速排序,整個過程可以用遞歸進行,從而實現整個數列的排序。

快速排序方法是基於分值策略的,排序在原地排序,不需要輔助的數組,但是分解困難。

 

快速排序分為三個過程:分解、治理、合並。

分解:先從數列num[low,high]中取出一個基准元素,將所有比基准元素小的數據存放在基准元素左側,將所有比基准元素大的數據存放在基准元素右側;基准元素此時已經位於正確的位置了,這個位置記為mid;

治理:基准元素左右兩個子數列num[low,mid-1]和num[mid+1,high]進行分解的操作,即進行快速排序的操作,這一步通過遞歸的方式實現;

合並:由於快速排序是在原地進行排序,因此不需要進行合並的操作。

 

快速排序中一個注意點的是基准元素該如何選擇?如果基准元素選取不當,有可能分解成規模為0和n-1的兩個子數列,快速排序會退化成冒泡排序。

一般來說,基准元素有以下幾種取法:

  • 取第一個元素
  • 取最后一個元素
  • 取中間元素
  • 取第一個、最后一個、中間位置元素三者的中位數
  • 取第一個和最后一個之間位置的隨機數k(low<=k<=high),取R[k]作為基准元素

 

快速排序的算法中,第二步是遞歸,關鍵的第一步分解如何實現呢?也就是說,我們選擇好了基准元素后,如何將數列中比基准元素小的數都移到基准元素左邊,而把數列中比基准元素大的數都移動到基准元素右邊呢?

下面選擇基准元素為第一個元素,說明如何實現這一步。

假設當前待排序的數列為num[low,high],其中low<=high。

步驟1:首先取數組的第一個元素作為基准元素pivot = num[low], 設置兩個指針i和j,i = low, j = high;

步驟2:從右向左掃描(即 j不斷向左移動),找到小於 pivot 的數,如果找到的話,num[i] 和 num[j] 交換, i++;(這一步其實就是將比基准元素小的數移動到基准元素左邊)

步驟3:從左向右掃描(即 i 不斷向右移動),找到大於 pivot 的數,如果找到的話,num[i] 和 num[j] 交換,j--;(這一步其實就是將比基准元素大的數移動到基准元素左右邊)

步驟4:重復步驟2~步驟3,直到指針 i 和 j 重合,返回該位置 mid = i,該位置的數正好是pivot

以數組{30,24,5,58,18,36,12,42,39}為例,上述步驟的演示過程如圖

 

實現代碼如下所示:

 1     public static void main(String[] args){
 2         int[] num = {30,24,5,58,18,36,12,42,39};
 3         QuickSort(num,0,num.length-1);
 4         for(int k:num)
 5             System.out.print(k+",");
 6     }
 7     public static void QuickSort(int[] num,int low,int high) {
 8         int mid;
 9         if(low<high) {
10             mid = Partition(num,low,high);
11             QuickSort(num,low,mid-1);
12             QuickSort(num,mid+1,high);
13         }
14     }
15     public static int Partition(int[] num, int low, int high) {
16         int i = low;
17         int j = high;
18         int pivot = num[low];
19         while(i < j) {
20             while(i < j && num[j] > pivot)
21                 j--;
22             if(i<j) {
23                 swap(num,i,j);
24                 i++;//注意,這一行不能在花括號外面
25             }
26             while(i<j && num[i]<pivot)
27                 i++;
28             if(i<j) {
29                 swap(num,i,j);
30                 j--;//注意,這一行不能在花括號外面
31             }
32         }
33         return i;
34     }
35     public static void swap(int[] num,int i,int j) {
36         int tmp = num[j];
37         num[j] = num[i];
38         num[i] = tmp;
39     }

 

需要注意的是,以上Partition()函數的實現只適用於基准元素是第一個元素的時候,當基准元素取最后一個元素時,需要將步驟2和步驟3調換。我們仔細看上述過程,會發現,指針i和j總有一個是指向基准元素,在做交換過程的時候,是基准元素和其它元素在做交換。如果我們將基准元素取最后一個元素是,可能會發生指針i和j指向的不是基准元素了。

 

在代碼中,我有兩行加了注釋,就是指針i++和指針j--這一過程必須在花括號里。這是因為只有在i<j的情況下,才能執行 i++和j--的操作。否則的話,當i==j了,此時需要換回i,如果i++不在i<j的約束下,肯定是要執行的,返回的就不是基准元素的位置,而是基准元素后一位了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM