算法:快速排序
快速排序
其實我第一次聽到快速排序的時候,我就很納悶,因爲不像冒泡、插入、歸併、選擇排序等等,名稱即原理!這個直接把特性用作名稱的,我就很震驚!
咱還是直接瞅一眼百科的解釋吧!
快速排序是冒泡排序的改進。
快速排序由C. A. R. Hoare在1960年提出。它的基本思想是:通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
百科中解釋了快排的一趟操作是對無序數組進行分割,使其分爲左右兩半部分,左邊的都小於某個數,右邊的都大於每個數。那某個數是哪個數呢?這個就是無所謂的,隨便取一個數就好,要不就以第1個數作爲那個數吧!
就像上圖這樣,一趟排序使得小於4的處於左邊,大於4的處於右邊!
一趟排序
那一趟排序到底做了什麼呢?它具體是怎麼使得元素位置進行如圖變換的呢!
其實也很簡單,我們之前有講過雙指針的應用,我們兩邊分別定義指針i,j。i從左到右走,負責找大於4的,j從右往左走,負責找小於4的。
到找到之後,他倆所在位置的元素一交換,就使得大的值處於右邊,小的值處於左邊!
接着呢,i和j繼續移動,直到他倆相遇!此時我們將當前位置與1號位置交換,就把4放在中間了!講是這麼講,但是這個過程還是有很多需要注意的地方!!!
接着
一趟排序完之後呢?其實我們只是把一個元素的最終位置確定了,即4!也是就說4肯定是在這裏放,那我們還需要處理其他的,所以仍然需要對4左邊的一趟排序,對4的右邊的一趟排序!那當然直觀的看,4右邊就一個元素5所以肯定是有序的!
那左邊132,經過一趟排序後!其實1的位置還是沒有變,1左邊沒有了,我們需要對1右邊的元素進行一趟排序!
好了,對右邊進行一趟排序後,結果如下:
因爲我們是分別處理的,所以我們畫的都是部分圖,最終效果就是整體有序!
這個就是快速排序的核心思想了,我們可以簡單總結一下,我們將序列根據某個值分爲兩段,這個成爲一趟排序,此時該值的最終位置就確定了,接着再對兩段進行一趟排序,來確定兩側元素的的最終位置。每一段都要進行一趟操作,所以着很明顯是遞歸操作!我們在代碼中可以很明顯的看到!
分析一下
快速排序的一次劃分算法從兩頭交替搜索,直到i和j重合,因此其時間複雜度是O(n);而整個快速排序算法的時間複雜度與劃分的趟數有關!
理想的情況是,每次劃分所選擇的中間數恰好將當前序列幾乎等分,經過log2n趟劃分,便可得到長度爲1的子表。這樣,整個算法的時間複雜度爲O(nlog2n)。
最壞的情況是,每次所選的中間數是當前序列中的最大或最小元素,這使得每次劃分所得的子表中一個爲空表,另一子表的長度爲原表的長度-1。這樣,長度爲n的數據表的快速排序需要經過n趟劃分,使得整個排序算法的時間複雜度爲O(n^2)。
當然快排也不穩定!!位於後面的元素很有可能會拍到前面,這個發生在一趟排序過程中,仔細想想!
Java實現
理解思路後,再看代碼其實還是蠻簡單的!
package sort; import java.util.Arrays; /** * 快速排序 * @author mrsaber */ public class QuickSort { public static void main(String[] args) { int[] arr = new int[]{4,3,2,1,5,6,7,8,3,1,3,111,22,9}; new QuickSort().sort(arr,0,arr.length-1); System.out.println(Arrays.toString(arr)); } public void sort(int[] nums,int start,int end){ if(end-start<1) return; int i = start; int j = end; while (j>i){ while (j>=0&&j>i&&nums[j]>nums[start]){j--;} while (i<end&&i<j&&nums[i]<=nums[start]){i++;} if(i<j){ swap(nums,i,j); } } swap(nums,i,start); sort(nums,start,i-1); sort(nums,i+1,end); } /** * 數組元素交換 * @param nums * @param i * @param j */ public void swap(int[] nums,int i,int j){ int temp = nums[i]; nums[i] = nums[j]; nums[j] =temp; } }
參考資料
- 《百度百科》