快速排序(Quicksort)是對冒泡排序的一種改進。(分治法策略)html
實現方法兩種方法:挖坑法和指針交換法git
基準的選擇三種方法:1.選擇最左邊記錄 2.隨機選擇 3.選擇平均值github
【3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到爲止。找到符合條件的值,進行交換的時候i, j指針位置不變。另外,i==j這一過程必定正好是i+或j-完成的時候,此時令循環結束】。 pivot算法
基準的選擇對於快速排序算法來講是很是重要的,雖然選擇任何一個元素做爲基準均可以完成排序,可是好的基準的選擇能最大發揮快速排序算法的性能,而很差的基準選擇則會讓快速排序的性能大打折扣,甚至變成」慢速」排序。所以在選擇基準上,最好的狀況就是儘可能選擇能均勻劃分子序列的基準(即儘可能使子序列相等)。數組
快速排序的時間性能取決於快速排序遞歸的深度,遞歸過程當中,子序列越平衡,則性能越好
(1)最優狀況下時,遞歸過程造成的遞歸樹是一課平衡樹,快速排序算法的時間複雜度爲O(nlogn)。
(2)最壞狀況下時,遞歸過程造成的遞歸樹是一課斜樹,快速排序算法的時間複雜度爲O(n2)。
(3)平均狀況下,時間複雜度爲O(nlogn)。數據結構
就空間複雜度來講,主要是遞歸形成的棧空間的使用。
(1)最好狀況,遞歸樹的深度爲log2n,其空間複雜度也就爲O(logn)。
(2)最壞狀況,須要進行n‐1遞歸調用,其空間複雜度爲O(n)。
(3)平均狀況,空間複雜度也爲O(logn)。post
快速排序在排序過程當中,因爲基準數的比較和交換是跳躍進行的,所以,快速排序是一種不穩定的排序算法。性能
能歸位,每一趟排序有一個元素歸位。學習
在有序或者接近有序的時候上面的方法效率會很是低(摘錄自百度百科)動畫
根據分區大小調整算法:在遞歸子問題的時候在區間內的數據比較少的時候咱們能夠再也不劃分區間,直接用直接插入排序或者堆排序等算法效率會更高,由於接着劃分又要建立棧楨,沒有必要。在遞歸排序子分區的時候,老是選擇優先排序那個最小的分區。這個選擇可以更加有效的利用存儲空間從而從總體上加速算法的執行。
不一樣的分區方案考慮:相同元素不少的狀況下,將分區分爲三塊而不是原來的兩塊:一塊是小於中軸值的全部元素,一塊是等於中軸值的全部元素,另外一塊是大於中軸值的全部元素。另外一種簡單的改進方法是,當分區完成後,若是發現最左和最右兩個元素值相等的話就避免遞歸調用而採用其餘的排序算法來完成。
並行的快速排序:因爲快速排序算法是採用分治技術來進行實現的,這就使得它很容易可以在多臺處理機上並行處理。
在大多數狀況下,建立一個線程所須要的時間要遠遠大於兩個元素比較和交換的時間,所以,快速排序的並行算法不可能爲每一個分區都建立一個新的線程。通常來講,會在實現代碼中設定一個閥值,若是分區的元素數目多於該閥值的話,就建立一個新的線程來處理這個分區的排序,不然的話就進行遞歸調用來排序。
對於這一併行快速排序算法也有其改進。該算法的主要問題在於,分區的這一步驟老是要在子序列並行處理以前完成,這就限制了整個算法的並行程度。解決方法就是將分區這一步驟也並行處理。改進後的並行快速排序算法使用2n個指針來並行處理分區這一步驟,從而增長算法的並行程度。
public class QuickSort { /** * 快排遞歸 * @param arr 待排序的數組 * @param left 數組的左邊界(例如,從起始位置開始排序,則l=0) * @param right 數組的右邊界(例如,排序截至到數組末尾,則right=arr.length-1) */ public static void quickSort(int[] arr, int left, int right) { if (left< right) { int index = getPartitionIndex2(arr, left, right);/* 獲取分區索引 */ quickSort(arr, left, index-1); /* 遞歸調用 */ quickSort(arr, index+1, right); /* 遞歸調用 */ } } //快速排序--挖坑法 獲取分區索引的 public static int getPartitionIndex1(int[] arr, int left, int right) { int low,high,pivot; low = left; high = right; dealPivot(arr, left, right);//三數中值 pivot = arr[left]; //最左基準 while (low < high) { while(low < high && arr[high] > pivot) high--; // 從右向左找第一個小於pivot的數 if(low < high) arr[low++] = arr[high]; while(low < high && arr[low] < pivot) low++; // 從左向右找第一個大於pivot的數 if(low < high) arr[high--] = arr[low]; } arr[low] = pivot; return low; } //快速排序--指針交換法 獲取分區索引的 public static int getPartitionIndex2(int[] arr, int left, int right) { int low,high,pivot; low = left; high = right; dealPivot(arr, left, right);//三數中值 pivot = arr[left];//最左基準 while (low < high) { while(low < high && arr[high] > pivot) high--; // 從右向左找第一個小於pivot的數 while(low < high && arr[low] <= pivot) low++; // 從左向右找第一個大於等於pivot的數 if(low < high) swap(arr, low, high); } // 此時right和left值是相同的,將基準元素與重合位置元素交換 arr[left] = arr[low]; arr[low] = pivot; return low; } //三數取中法,也就是取左端、中間、右端三個數,而後進行排序,將中間數做爲樞紐值。將中間數放在了left位置上 public static void dealPivot(int[] arr, int left, int right) { int mid = (left + right) / 2; if (arr[left] > arr[mid]) {//左端小於中間 swap(arr, left, mid); } if (arr[left] > arr[right]) {//左端小於右邊 swap(arr, left, right); } if (arr[right] < arr[mid]) {//右邊大於中間 swap(arr, right, mid); } swap(arr, left, mid);//左邊《中間《右邊 將中間的值換到左邊 } // 交換數組中的兩個數 public static void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } public static void main(String[] args){ int[] arr = { 49, 38, 65, 97, 76, 13, 27, 50 }; quickSort(arr,0,arr.length-1); System.out.println("排好序的數組:"); for (int e : arr) System.out.print(e+" "); } }