八大排序之快速排序的优化

快速排序也是有缺点的,它的缺点就是,数据越有序,快速排序的效率就越差。
比如当我们拿到这样的一组关键字序列
[1 2 3 4 5 6 7 8]
根据上一篇快速排序的思想,还是会先将1当关键字然后将序列划分成两部分,然后接着排序接着划分。。。如下图所示:
在这里插入图片描述
这样下来,就进行了很多没有必要的递归,递归的层次就会变得非常多。基于这样的情况我们给出优化方案
优化一:随机取基准点法
这个方法优化最坏的是情况,也就是数据最有序的情况。这样的话我们就不取第一个数为基准点,而是随机取基准点,这样的话就大大提高了算法的效率。

void Swap(int arr[],int first, int sencond)
{
	int tmp;
	tmp = arr[frist];
	arr[frist] = arr[sencond];
	arr[sencond] = tmp;
}
int parition(int arr[],int start,int end)//找到基准点后的基准点下标
{
	int boundkey = arr[start];
	while(start < end)
	{
		while(start < end && arr[end])
			end--;
		arr[start] = arr[end];
		while(start < end && arr[start] <= boundkey)
			start++;
		arr[end] = arr[start]
	}
	arr[start] = boundkey;
	return start;
}
void Quick(int arr[],int startindex,int endindex)
{
	int k;
	if(startindex < endindex)
	{
     	swap(arr.startindex,rand()%(endindex - startindex + 1) + startindex)//改变随机值。随机在这个数据里取一个元素与其实位置交换
		k = parition(arr,startindex,endindex);
		Quick(arr,startindex,k - 1);
		Quick(k + 1,endindex);
	}
}
void QuickSort(int arr[],int len)
{
	Quick(arr, 0, len-1)}

优化二:三数取中法(第一个数据,中间数据,最后一个数据)
数据本身是随机的,当数据是随机的时候,随机取基准点的优化效率并不大。这个方法可以优化快排最坏的情况,普通情况也可以稍作优化。

int parition(int arr[],int start,int end)//找到基准点后的基准点下标
{
	int boundkey = arr[start];
	while(start < end)
	{
		while(start < end && arr[end])
			end--;
		arr[start] = arr[end];
		while(start < end && arr[start] <= boundkey)
			start++;
		arr[end] = arr[start]
	}
	arr[start] = boundkey;
	return start;
}
void getMiddleMaxNum(int arr[], int start, int mid, int end)
{
	if(arr[mid] > arr[end])
	{
		Swap(arr, mid, end);//mid和right中较大的数据
	}
	if(arr[start] < arr[end])//end中间大
	{
		Swap(arr,start,end);
	}
	if(arr[start] > arr[mid])//mid中间大
	{
		Swap(arr.start,mid);
	}
}
void Quick(int arr[],int startindex,int endindex)
{
	int k;
	if(startindex < endindex)
	{
     	//swap(arr.startindex,rand()%(endindex - startindex + 1) + startindex)//改变随机值。随机在这个数据里取一个元素与其实位置交换
     	int midindex = (endindex + startindex) / 2;
     	getMiddleMaxNum(arr, startindex,midindex,endindex);
		k = parition(arr,startindex,endindex);
		Quick(arr,startindex,k - 1);
		Quick(k + 1,endindex);
	}
}
void QuickSort(int arr[],int len)
{
	Quick(arr, 0, len-1)}

优化三:数据量小
这种情况下就没有必要进行快速排序了,这个时候我们可以进行插入排序来提高s算法的效率

//核心代码
if(startindex < endindex0)
{
	if((endindex - startindex ) < 20)
	{
		InsertSort(arr,startindex,endindex);
	}
}

具体实现

int parition(int arr[],int start,int end)//找到基准点后的基准点下标
{
	int boundkey = arr[start];
	while(start < end)
	{
		while(start < end && arr[end])
			end--;
		arr[start] = arr[end];
		while(start < end && arr[start] <= boundkey)
			start++;
		arr[end] = arr[start]
	}
	arr[start] = boundkey;
	return start;
}
void InsertSort(int arr[],int start,int end)
{
	int i = start + 1;
	int j = i - 1;
	int tmp;
	for(i;i <= end;i++)
	{
		tmp = arr[i];
		for(j = i - 1;j >= start && arr[j] > tmp;j--)
		{
			arr[j + 1] = arr[j];
		}
	arr[j + 1] = tmp;
	}
}
void Quick(int arr[],int startindex,int endindex)
{
	int k;
	if(startindex < endindex)
	{
	if((endindex - startindex ) < 20)
	{
		InsertSort(arr,startindex,endindex);
		return;
	}
    //swap(arr.startindex,rand()%(endindex - startindex + 1) + startindex)//改变随机值。随机在这个数据里取一个元素与其实位置交换
    int midindex = (endindex + startindex) / 2;
    getMiddleMaxNum(arr, startindex,midindex,endindex);
	k = parition(arr,startindex,endindex);
	Quick(arr,startindex,k - 1);
	Quick(k + 1,endindex);
	}
}

优化四:聚集优化
针对于重复率高,数据值少的情况。比如这样的待排序序列:
[19 18 19 20 20 20 19 19 19 20]
这样的话如果我们不做聚集优化的话,它会将每个数字都当成基准点,会提高递归的次数。如果我们将所有重复的数字只当一次基准点,这样就大大减少了递归的次数,提高了算法的效率。
方法是:找到基准点,在它左边的区间,把和基准点相同的数据向右侧移动和基准点聚集。在它右边的区间,把和基准点相同的数据向左侧移动和基准点聚集。

void Gather(int arr[],int s,int e,int k,int newleft,int newright)
{
	int newleft = k - 1;
	int newright = k + 1;
        // 寻找并聚集基准左边与基准相同的元素
	for (int i = k - 1; i >= start; i--) 
	{
		if (arr[i] == arr[k])
		{
			if (i != newleft) 
			{
   				int tmp = arr[newleft];
				arr[newleft] = arr[i];
				arr[i] = tmp;
 				newleft--;  
			}
			else
			{
				newleft--;
 			}
		}
	}
// 寻找并聚集基准右边与基准相同的元素
	for (int j = k + 1; j <= end; j++)
  	{
		if (arr[j] == arr[k])
 		{
 			if (j != newright)
  			{
   // 依次遍历比较,相同就交换位置
 				int tmp = arr[newright];
 				arr[newright] = arr[j];
				arr[j] = tmp;
 				newright++;
 			}
	 		else 
 			{
				newright++;
			}
		}
	}
}
void Quick(int arr[],int startindex,int endindex)
{
	int k;
	if(startindex < endindex)
	{
	if((endindex - startindex ) < 20)
	{
		InsertSort(arr,startindex,endindex);
		return;
	}
    int midindex = (endindex + startindex) / 2;
    getMiddleMaxNum(arr, startindex,midindex,endindex);
    int left;
    int right;
    Gather(arr,startindex,endindex,k,&left,&right)
	k = parition(arr,startindex,endindex);
	Quick(arr,startindex,k - 1);
	Quick(k + 1,endindex);
	}
}

优化五:非递归优化
将递归处理的划分的区间采用非递归的方法。

int parition(int arr[],int start,int end)//找到基准点后的基准点下标
{
	int boundkey = arr[start];
	while(start < end)
	{
		while(start < end && arr[end])
			end--;
		arr[start] = arr[end];
		while(start < end && arr[start] <= boundkey)
			start++;
		arr[end] = arr[start]
	}
	arr[start] = boundkey;
	return start;
}
void Quick(int arr[], int startindex,int endindex)
{
	int * st = (int *)malloc(sizeof(int)*(endindex - startindex + 1))
	int top = 0;//栈顶指针
	int left = startindex;
	int right = endindex;
	st[top++] = left;
	st[top++] = right;
	while(top != 0)
	{
		right = st[top - 1]; top--;
		left = st[top - 1]; top--;
		k = parition(arr,left,right);
		if(k + 1 < right)//右区间至少有两个元素
		{
			st[top++] = k + 1;
			st[top++] = right;
		}
		if(left < k - 1)//右区间至少有两个元素
		{
			st[top++] = left;
			st[top++] = k - 1;
		}
	}
	free(st);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
快速排序是一种常见的排序算法,其时间复杂度为 O(nlogn),但是在处理大量数据的时候,它的效率可能不够高。以下是一些优化快速排序的方法: 1. 随机化选择基准值:快速排序的效率取决于基准值的选择,如果选择的基准值恰好是最小值或最大值,那么排序的效率将会很慢。随机选择基准值可以减少出现这种情况的概率,提高排序的效率。 2. 三数取中法选择基准值:在数组的左端、右端和中间位置分别选取一个数,取它们的中位数作为基准值,可以减少基准值选择不当的情况。 3. 插入排序:当待排序的数组长度小于一定阈值时,使用插入排序可以提高排序的效率。 4. 尾递归优化:将递归过程改为尾递归形式,可以减少递归调用的栈空间,从而减少程序的内存占用。 下面给出一个优化后的快速排序的 Python 实现: ```python import random def quick_sort(nums): if len(nums) <= 5: return sorted(nums) pivot = median_of_three(nums) left = [] right = [] center = [] for num in nums: if num < pivot: left.append(num) elif num > pivot: right.append(num) else: center.append(num) return quick_sort(left) + center + quick_sort(right) def median_of_three(nums): a = nums[0] b = nums[len(nums) // 2] c = nums[-1] if a < b: if b < c: return b elif a < c: return c else: return a else: if a < c: return a elif b < c: return c else: return b ``` 在上面的代码中,使用了三数取中法选择基准值,并且使用了插入排序来处理长度小于等于 5 的数组。如果数组长度大于 5,就递归地对左右两个子数组进行排序。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值