快速排序的的基本思想:
設數組a中存放了n個數據元素,low爲數組的低端下標,high爲數組的高端下標,從數組a中任取一個 元素(一般選取a[ow])做爲標準,調整數組a中各個元素的位置,使排在標準元素前面的元素的關鍵字均小於標準元素的關鍵字,排在標準元素後面元素的關鍵字均大於或等於標準元素的關鍵字。一次結束後,把標準元素放在適合的位置,而且把以標準元素爲中心劃分了兩個子數組,前面數組的數都小於標準元素,後面的數組都大於標準元素。而後分別對這兩個自數組進行相同的遞歸快速排序。遞歸算法的出口是high>lowios
快速排序至關與冒泡排序的升級版
1.選擇基準:在待排序列中,按照某種方式挑出一個元素,做爲 "基準"(pivot)算法
2.分割操做:以該基準在序列中的實際位置,把序列分紅兩個子序列。此時,在基準左邊的元素都比該基準小,在基準右邊的元素都比基準大數組
3.遞歸地對兩個序列進行快速排序,直到序列爲空或者只有一個元素。dom
挖坑填數,進行快速排序:
1.i =L; j = R; 將基準數挖出造成第一個坑a[i]。函數
2.j--由後向前找比它小的數,找到後挖出此數填前一個坑a[i]中。優化
3.i++由前向後找比它大的數,找到後也挖出此數填到前一個坑a[j]中。ui
4.再重複執行2,3二步,直到i==j,將基準數填入a[i]中spa
快速排序的過程圖:code
快速排序的完整代碼(遞歸):blog
#include<iostream> using namespace std; int partition(int *arr,int left,int right) { int temp=arr[left]; while(left<right)//直達left和right重合的時候,才找到合適的位置 { //先從後往前找比基準小的 while(left<right && arr[right]>=temp)//當right的值大於temp的值的時候才執行 //等號必定得寫,由於可能會出現,保存的temp元素和數據中的元素同樣的,不寫會出現死循環的現象 { right--; } arr[left]=arr[right];//當right的值小於temp的值的時候執行 //從前日後找,找比基準大的 while(left<right && arr[left] <=temp)//當left的值小於temp的值的時候執行 { left++; } arr[right]=arr[left];//當left的值大於temp的時候執行 } arr[left]=temp;//此時的left和right在同一個位置,此時爲合適的位置,把temp的值給left return left;//此時返回的值是temp合適的位置,即小於它的在它的左邊,大於它的在它的右邊 } void quick(int *arr,int left,int right) { if(left<right) { int pivot=partition(arr,left,right); quick(arr,left,pivot-1); quick(arr,pivot+1,right); } } void quick_sort(int *arr,int len) { quick(arr,0,len-1); } int main() { int arr[]={9,5,7,10,45,12}; int len=sizeof(arr)/sizeof(arr[0]); quick_sort(arr,len); for(int k=0;k<len;++k) { cout<<arr[k]<<" "; } cout<<endl; }
快速排序的完整代碼(非遞歸):
#include<stack> #include<iostream> using namespace std; int partition(int *arr,int left,int right) { int temp=arr[left];//基準 while(left<right) { //先從後往前找比基準小的 while(left<right && temp<=arr[right]) { right--; } arr[left]=arr[right]; //從前日後找比基準大的 while(left<right && temp>=arr[left]) { left++; } arr[right]=arr[left]; } arr[left]=temp; return left; } //其實就是用棧保存每個待排序子串的首尾元素的下標 void q_sort(int *arr,int left,int right ) { stack<int> st; int pos=0; st.push (left); st.push (right); while(!st.empty()) { right=st.top(); st.pop(); left=st.top(); st.pop ();//5,6,8,2,9,4,1,3,45,89,65 pos=partition(arr,left,right); if(pos+1<right)//先入基準右邊的,若是基準右邊只有一個元素的時候,就不用入了 { st.push (pos+1); st.push (right); } if(pos-1>left)//再入基準左邊的,若是基準左邊只有一個元素的時候,就不用入了 { st.push (left); st.push (pos-1); } } } void quick_sort(int *arr,int len) { q_sort(arr,0,len-1); } int main() { int arr[]={5,6,8,2,9,4,1,3,45,89,65}; int len=sizeof(arr)/sizeof(arr[0]); quick_sort(arr,len); for(int i=0;i<len;i++) { cout<<arr[i]<<" "; } cout<<endl; }
快速排序的各類優化:
優化一:三數取中法,解決數據基本有序的(就是找到數組中最小下標,最大下標,中間下標的數字,進行比較,把中間大的數組放在最左邊)
代碼:
//************************************************************************* void swap(int *arr,int left,int right) { int temp; temp=arr[left]; arr[left]=arr[right]; arr[right]=temp; } //*************************************************************************** int partition(int *arr,int left,int right) { //*************************** //e.g:9,1,5,8,3,7,4,6,2 int m=left+(right-left)/2;//找到中間的數字的下標 if(arr[left]>arr[right])//最左大於最右的時候,交換左右 { swap(arr,left,right); } if(arr[m]>arr[right])//若是中間的>right ,交換 { swap(arr,m,right); } if(arr[m]>arr[left])//若是中間的>left,交換 { swap(arr,m,right); } //通過交換以後low變爲3 //**************************** int temp=arr[left];//基準 while(left<right)//知道left和right重合的時候,才找到合適的位置 { //從後向前找到比小的數字 while(left<right && arr[right]>=temp)//當right的值大於temp的值的時候才執行 { right--; } arr[left]=arr[right];//當right的值小於temp的值的時候執行 while(left<right && arr[left] <= temp)//從前日後找到比基準大的數字 { left++; } arr[right]=arr[left];//當left的值大於temp的時候執行 } arr[left]=temp;//此時的left和right在同一個位置,此時爲合適的位置,把temp的值給left return left;//此時返回的值是temp合適的位置,即小於它的在它的左邊,大於它的在它的右邊 }
優化二:
隨機選取基準
引入的緣由:在待排序列是部分有序時,固定選取樞軸使快排效率底下,要緩解這種狀況,就引入了隨機選取樞軸
思想:取待排序列中任意一個元素做爲基準
/*隨機選擇樞軸的位置,區間在low和high之間*/ int SelectPivotRandom(int arr[],int low,int high) { //產生樞軸的位置 srand((unsigned)time(NULL)); int pivotPos = rand()%(high - low) + low; //把樞軸位置的元素和low位置元素互換,此時能夠和普通的快排同樣調用劃分函數 swap(arr[pivotPos],arr[low]); return arr[low]; }
優化三:優化小數組的交換,就是爲了解決大才小用問題
緣由:對於很小和部分有序的數組,快排不如插排好。當待排序序列的長度分割到必定大小後,繼續分割的效率比插入排序要差,此時可使用插排而不是快排
截止範圍:待排序序列長度N = 10,雖然在5~20之間任一截止範圍都有可能產生相似的結果,這種作法也避免了一些有害的退化情形。
#define max_len 10 void quick(int *arr,int left,int right) { int length=right-left; if(length>max_len ) { int pivot=partition(arr,left,right); quick(arr,left,pivot-1); quick(arr,pivot+1,right); } else { //用插入排序 } }
優化四:
在一次分割結束後,能夠把與Key相等的元素聚在一塊兒,繼續下次分割時,不用再對與key相等元素分割
舉例:
待排序序列 1 4 6 7 6 6 7 6 8 6
三數取中選取樞軸:下標爲4的數6
轉換後,待分割序列:6 4 6 7 1 6 7 6 8 6
樞軸key:6
本次劃分後,未對與key元素相等處理的結果:1 4 6 6 7 6 7 6 8 6
下次的兩個子序列爲:1 4 6 和 7 6 7 6 8 6
本次劃分後,對與key元素相等處理的結果:1 4 6 6 6 6 6 7 8 7
下次的兩個子序列爲:1 4 和 7 8 7
通過對比,咱們能夠看出,在一次劃分後,把與key相等的元素聚在一塊兒,能減小迭代次數,效率會提升很多
具體過程:在處理過程當中,會有兩個步驟
第一步,在劃分過程當中,把與key相等元素放入數組的兩端
第二步,劃分結束後,把與key相等的元素移到樞軸周圍
舉例:
待排序序列 1 4 6 7 6 6 7 6 8 6
三數取中選取樞軸:下標爲4的數6
轉換後,待分割序列:6 4 6 7 1 6 7 6 8 6
樞軸key:6
第一步,在劃分過程當中,把與key相等元素放入數組的兩端
結果爲:6 4 1 6(樞軸) 7 8 7 6 6 6
此時,與6相等的元素全放入在兩端了
第二步,劃分結束後,把與key相等的元素移到樞軸周圍
結果爲:1 4 6 6(樞軸) 6 6 6 7 8 7
此時,與6相等的元素全移到樞軸周圍了
以後,在1 4 和 7 8 7兩個子序列進行快排
代碼:
void QSort(int arr[],int low,int high) { int first = low; int last = high; int left = low; int right = high; int leftLen = 0; //用來統計左邊與key相等的元素的個數 int rightLen = 0; //統計右邊與key相等的元素的個數 if (high - low + 1 < 10) { InsertSort(arr,low,high); //數據量少,就用插入排序 return; } //一次分割 int key = SelectPivotMedianOfThree(arr,low,high);//使用三數取中法選擇樞軸 while(low < high) { while(high > low && arr[high] >= key) { if (arr[high] == key)//處理相等元素 { swap(arr[right],arr[high]); //把右邊與key元素相等的彙集的右端 right--; rightLen++; } high--; } arr[low] = arr[high]; while(high > low && arr[low] <= key) { if (arr[low] == key) //把左邊與key元素相等的彙集數組的左端 { swap(arr[left],arr[low]); left++; leftLen++; } low++; } arr[high] = arr[low]; } arr[low] = key; //一次快排結束 //把與樞軸key相同的元素移到樞軸最終位置周圍 int i = low - 1; //軸的左邊 int j = first; while(j < left && arr[i] != key) { swap(arr[i],arr[j]); //此時,把數組左端與key相等的數據換到key的左邊 i--; j++; } i = low + 1; //軸的右邊 j = last; while(j > right && arr[i] != key) { swap(arr[i],arr[j]); //此時,把數組右端與key相等的數據換到,key右邊 i++; j--; } partition(arr,first,low - 1 - leftLen); partition(arr,low + 1 + rightLen,last); }