快速排序及其優化

一、特點
當劃分均勻時快速排序的時間複雜度是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);  
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章