快速排序 三种版本

数据结构 专栏收录该内容
19 篇文章 1 订阅

快速排序

是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。
将区间按照基准值划分为左右两半部分的常见方式有:
1. 基础版本
2. 三数取中法
3. 挖坑法
4. 前后指针版本

基础版本
思路:
1.直接选第一个元素做基准值
2.比较一趟 从最左边先开始,right 找小的,比基准值大的就往回走, left找大的,比基准值小了就往前走 两者停下,就交换一次,
3.再入循环 在重复以上步骤,
5. 最后一步,把基准值换到最后停下的下标,返回此下标索引

//普通版本
int PartSort(int *a, int left, int right)
{
	int key = a[left];
	int start = left;
	while (left < right)
	{
		while (left < right && a[right]>=key)
		{
			right--;
		}
		while (left < right && a[left] <= key)
		{
			left++;
		}
		swap(&a[left], &a[right]);
	}
	swap(&a[left], &a[start]);
	return  left;

}

三数取中法优化
思路:
1.先求left mid right这三下标的数据的中位数,return此下标 ,
2. 之后 放到第一个元素做基准值再套用基础模板

// 快速排序   三数取中优化法
int GitMidindex(int *a, int left, int right)//取三个数的中位数放到第一位做基准值
{
	int mid = (left + right) / 2;
	//int mid=left+ ((right-left)>>1) 可能整数溢出的处理方法
	if (a[left] > a[mid])
	{
		if (a[mid] > a[right])
			return mid;
		else  if (a[right] > a[left])
			return left;
		else
			return right;
	}
	else    //a[left] <a[mid]
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[left]>a[right])
			return left;
		else
			return right;
	}
	//返回中位数的索引
}
int PartSort1(int* a, int left, int right)
{
	int mid = GitMidindex(a, left, right);//中位数的下标索引
	swap(&a[mid], &a[left]);//与开头的那个值交换,确保最开始时是用中位数做基准值的
	int key = a[left];
	int start = left;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		while (left < right && a[left] <= key)
		{
			left++;
		}
		swap(&a[left], &a[right]);
	}
	swap(&a[left], &a[start]);//比较完后把基准值换到停下的地方,一趟就排完了
	return  left;
	
}

挖坑法优化
1.把选一个基准值先保存,再假想成一个坑,比较后满足条件的就把自己(覆盖)填过去,自己就又是坑了,再继续判断
2.直到填完只剩下最后一个空坑,把基准值放进去
过程:
在这里插入图片描述

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
	int key = a[left];    //把自己变成坑 a[left]就可以看成一个坑// 选左右都可以
	while (left < right)
	{
		while (left<right && a[right]>=key)
		{
			right--;
		}
		a[left] = a[right];//小的就填进去  right就是新的坑了
		while (left < right  && a[left] <= key)
		{
			left++;
		}
		a[right] = a[left];//大的填进去 left就是新的坑
	}
	a[left] = key;//最后再把基准值填到最后一个坑里
	return left;//把中间的基准值的位置保留下来
}

前后指针优化
prev指向开头,cur遍历找小,遇到小的便与之前prev保留的小的交换,遇到大的就继续遍历,这样小的一开始就交换到前面去了,大的不动直到最后被堆到后面。这样操作也就完成了划分以基准值为界的两段区间。
思路:
在这里插入图片描述

// 快速排序前后指针法  双指针prev cur
int PartSort3(int* a, int left, int right)
{
	int mid = GitMidindex(a, left, right);//选出中间的那个数
	swap(&a[mid], &a[left]);//与开头的那个基准值交换,确保最开始时是用中位数做基准值的
	int key = a[left];
	int prev = left;
	int cur = left + 1;
	while (cur <= right)
	{
		if(a[cur] < key)
		{
			prev++;
			swap(&a[cur], &a[prev]);
			cur++;
		}
		else
		{
			cur++;
		}
	}
	swap(&a[left], &a[prev]);
	return prev;

}

对不同的版本进行调用

void QuickSort(int* a, int left, int right)
{
	//if (left >= right)//特殊情况  1 3 5 4, 当排第一趟时1为基准,end没找到比它小的一直减减,直到它本身了,此时left=right,返回keyindex为没动的left; 在递归下一趟时left为0,大于keyindex-1的-1,就不做左边无意义的递归趟数了
	//	return;   如果区域不存在或只有一个数据则不递归排序
	if (left < right)
	{
		if (left-right +1 < 10)//小区间优化:   区间内数据小于一定值了,就没必要用快排的递归下去了,小数据还是可以用插入排序的
		{
			InsertSort(a+left, right-left + 1); //相当于也是传区间
		}
		else
		{//对不同版本调用即可
			//int keyindex = PartSort(a, left, right);//基础版本
			//int keyindex = PartSort1(a, left, right);//三数取中
			//int keyindex = PartSort2(a, left, right);//挖坑法
			int keyindex = PartSort3(a, left, right);//前后指针版本
			// [left,keyindex-1]    keyindex   [keyindex+1,right]   二叉树结构  左区间 基准值 右区间  继续递归
			QuickSort(a, left, keyindex - 1);//基准值左边(大于它的)继续划分
			QuickSort(a, keyindex + 1, right);//基准值右边(小于它的)也继续
			//直到最小的都已经有序(划分好了)
		}
	}
}
  • 5
    点赞
  • 0
    评论
  • 11
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值