一、特點
當劃分均勻時快速排序的時間複雜度是O(nlogn),空間複雜度是O(logn)。
當劃分完全不均勻時時間複雜度是O(n²),空間複雜度是O(n)。
二、思想
快排是對冒泡的進一步改進,它的思想是分治和遞歸。通過一趟排序將要排序的數據分割成獨立的兩部分,其中一部分的所有數據都比另外一部分的所有數據都要小,然後再按此方法對這兩部分數據分別進行快速排序,整個排序過程可以遞歸進行,以此達到整個數據變成有序序列。
三、代碼及結果
#include<stdio.h>
//查找中軸,並把數據按中軸分爲兩部分,一邊比中軸大,一邊比中軸小
int findmid(int *arr,int left,int right)
{
int pivot=arr[left];//將數組第一個數定義爲中軸
while(left < right)//如果left=right時循環結束,這是的left或right便是中軸的位置,將pivot賦值給這個位置。並按這個位置的兩邊,分成兩個數組開始遞歸,直到left=right,完成排序。
{
while(left < right && arr[right]>=pivot)
{
right--;
}
if(left < right)
{
arr[left++]=arr[right];//注意這是把arr[right]的值賦給arr[left]然後left++,因爲arr[left]的值已經作爲中軸賦值給pivot,所以覆蓋掉沒有關係,並且只有覆蓋掉才能找到最後left=right時的位置也就是中軸的位置,這時再將pivot複製到這個位置。
}
while(left < right && arr[left]<=pivot)
{
left++;
}
if(left < right)
{
arr[right--]=arr[left];//
}
}
arr[left]=pivot;
return left;
}
//快排
void quickSort(int *arr,int left,int right)
{
if(left<right)
{
int mid = findmid(arr,left,right);
quickSort(arr,mid+1,right);//右遞歸
quickSort(arr,left,mid-1);//左遞歸
}
}
int main()
{
int arr[] = {5,3,2,5,4,7,6,8,2,2};
int len=sizeof(arr)/sizeof(arr[1]);
quickSort(arr,0,len-1);
for(int i=0;i<len;i++)
{
printf("%d",arr[i]);
}
printf("\n");
}
四、優化
1、兩種影響排序的因素
A.劃分不均勻
對於分治算法,當每次劃分時,算法若都能分成兩個等長的子序列時,那麼分治算法效率會達到最大。如果輸入序列有序(當然正常情況算法應該判斷當序列有序時直接返回),我們選最左邊元素做樞軸點,即劃分極度不均勻時,不僅時間複雜度變成O(n²),遞歸深度也變成了n,這樣很容易令棧內存溢出。所以要儘量避免這種情況。
B.當有大量重複元素時
每趟排序對每個子序列只產生一個有序位置, 所以對數組中相等元素的處理效率不是很高。如果有大量重複元素,實際上快排做了很多無用工作。由於劃分函數的特點,對於一個每個元素都完全相同的一個序列來講,快速排序也會退化到 O(n^2)。
2、三數取中
3、隨機選取基準
4、插入排序,對於很小和部分有序的數組,快排不如插排好
5、在一次分割結束後,可以把與Key相等的元素聚在一起,繼續下次分割時,不用再對與key相等元素分割
代碼
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);
}