快速排序及其优化

一、特点
当划分均匀时快速排序的时间复杂度是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);  
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
选择排序算法准则: 每种排序算法都各有优缺点。因此,在实用时需根据不同情况适当选用,甚至可以将多种方法结合起来使用。 选择排序算法的依据 影响排序的因素有很多,平均时间复杂度低的算法并不一定就是最优的。相反,有时平均时间复杂度高的算法可能更适合某些特殊情况。同时,选择算法时还得考虑它的可读性,以利于软件的维护。一般而言,需要考虑的因素有以下四点: 1.待排序的记录数目n的大小; 2.记录本身数据量的大小,也就是记录中除关键字外的其他信息量的大小; 3.关键字的结构及其分布情况; 4.对排序稳定性的要求。 设待排序元素的个数为n. 1)当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。 快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短; 堆排序 : 如果内存空间允许且要求稳定性的, 归并排序:它有一定数量的数据移动,所以我们可能过与插入排序组合,先获得一定长度的序列,然后再合并,在效率上将有所提高。 2) 当n较大,内存空间允许,且要求稳定性 =》归并排序 3)当n较小,可采用直接插入或直接选择排序。 直接插入排序:当元素分布有序,直接插入排序将大大减少比较次数和移动记录的次数。 直接选择排序 :元素分布有序,如果不要求稳定性,选择直接选择排序 5)一般不使用或不直接使用传统的冒泡排序。 6)基数排序 它是一种稳定的排序算法,但有一定的局限性:   1、关键字可分解。   2、记录的关键字位数较少,如果密集更好   3、如果是数字时,最好是无符号的,否则将增加相应的映射复杂度,可先将其正负分开排序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值