快速排序三种实现和非递归实现及其优化

目录

一、快速排序实现思想:

二、快速排序的执行步骤:

三、实现方法:

四、代码实现:

五、优化


一、快速排序实现思想

是每次制定一个基准值,再一次排序后将除基准值之外的数据分为两部分,一边全部小于等于,另一边均大于等于

然后再对左右各部分重新寻找基准值排序。

二、快速排序的执行步骤

  1. 选取基准值
  2. 左右分别遍历,找出位置有误的元素后交换
  3. 重复1.2步骤,直至遍历完

三、实现方法

  • 基础实现:对递归进来数组,随机选一个基准值,我用的是第一个数据作为。再左右遍历,遇到不符合的数就退出交换,最后返回基准数的新坐标
  • 双指针法:对递归进来数组,随机选基准值,左右指针分别向中间遍历,左右分别遇到不符要求的数后在循环末尾将两数交换。
  • 三数取中法(也叫霍尔法):对递归进来数组,和上面的方法主要区别在于基准值得选取,在数组的头,尾和中间,三数中取中间值作为基准值。
  • 挖坑法(去掉不必要交换法):对递归进来数组,将基准值用一个临时变量存储起来,相当于在原来的基准值地方挖了一个坑,后面不符的值直接填进去,不用再交换。最后将这个临时变量(基准值)填会最后一个坑里。这样省去了不必要的交换!
  • 非递归实现:借助队列或者栈,对基准值左右排序,思想类似于树的层序遍历和前序遍历。将头结点和尾节点入队列,然后再对内部使用前面的算法进行调整,之后再将返回下标分成的左右数组分别入队列首尾节点,再将原来的首尾节点出队列,这样不断循环,上层首尾出队前,会将调整后的数组一分为二,首尾入队。直至首尾间隔为一个元素。

四、代码实现

基础实现:

int PointerWay(int* src, int start, int end)
{
	int point = src[start];
	while (start<end)
	{
		while (start< end && src[end] >= point)
		{
			end--;
		}
		SwapArgs(&src[start], &src[end]);
		while ( start<end && src[start] <= point)
		{
			start++;
		}
		SwapArgs(&src[start], &src[end]);
	}
	return start;
}

图解:

双指针法:

int doublePointerWay(int *src, int start, int end)
{
	int point = src[start];
	int* p = src+start;//指向操作数组最左处
	int* q = src + end;//指向操作数组最右处,即最后一个操作数据
	while (p < q)
	{
		while (p<q && *q >= point)
		{
			q--;
		}
		while (p<q && *p <= point)
		{
			p++;
		}
		SwapArgs(p, q);//p<q&&p指向左边大于point的数,q指向右边小于point的数,两者交换
	}
	//q指针只能停在*q<=point的数据上,除非q走到了p的指向,这时q,p的指向均指向p的指向,而*p<=point,
	//若从q的while循环跳出,则说明,p遇到了q,而q的while()执行在前,除非遇到*q<=point,否则q一直向前,直到遇到p,
	//因此无论这两种情况的哪一种,p,q的最终指向都小于等于point,故最后指向的数据只能放在左边,刚好和src[start]交换
	SwapArgs(&src[start], p);//将point换到p和q的交叉位置,左边
	return p - src;
}

图解:

 

三数取中法:

int HoareWay(int* src, int start, int end)
{
	int mid = start + (end - start) / 2;
	if (src[mid] > src[end])
	{
		SwapArgs(&src[mid], &src[end]);
	}
	if (src[start] > src[end])
	{
		SwapArgs(&src[start], &src[end]);
	}
	if (src[mid] > src[start])
	{
		SwapArgs(&src[start], &src[mid]);
	}//使得三个数中start下标为三个数中间大小的那个数
	int point = src[start];
	while (start<end)
	{
		while (start < end && src[end] >= point)
		{
			end--;
		}
		SwapArgs(&src[start], &src[end]);
		while (start < end && src[start] <= point)
		{
			start++;
		}
		SwapArgs(&src[start], &src[end]);
	}
	return start;
}

图解:

去掉不必要交换法:

int DigWay(int* src, int start, int end)//挖坑法减少不必要交换,结合霍尔法
{
	int mid = start + (end - start) / 2;
	if (src[mid] > src[end])
	{
		SwapArgs(&src[mid], &src[end]);
	}
	if (src[start] > src[end])
	{
		SwapArgs(&src[start], &src[end]);
	}
	if (src[mid] > src[start])
	{
		SwapArgs(&src[start], &src[mid]);
	}//使得三个数中start下标为三个数中间大小的那个数
	int tmp = src[start];//保存基准值
	while (start < end)
	{
		while (start < end &&src[end] >= tmp)
		{
			end--;
		}
		src[start] = src[end];
		while (start < end && src[start] <= tmp)
		{
			start++;
		}
		src[end] = src[start];
	}
	src[start] = tmp;
	return start;
}

画了第一层递归的草图理解一下,后续递归道理相同,来感受一下:

非递归实现:

//非递归实现
void QuickingNonR(int*src, int n)
{
	Queue qu;//定义一个队列
	int mid = n / 2;
	QueueInit(&qu);//初始化队列
	QueuePush(&qu, 0);//首下标入队
	QueuePush(&qu, n-1);//尾下标入队
	while (!QueueIsEmpty(&qu))
	{
		int start = QueueTop(&qu);//取队头元素(首元素下标),
		QueuePop(&qu);//出队,
		int end = QueuTop(&qu);//取队头元素,(尾元素下标)
		QueuePop(&qu);//出队,
		mid = DigWay(src, start, end);//调用函数,将mid的左右调整并mid接收下标值
		if (start < mid-1)//将mid分开的数组左部分,首尾入队
		{
			QueuePush(&qu, start);
			QueuePush(&qu, mid);
		}
		if (mid+1 < end)//将mid右半部分首尾下标入队
		{
			QueuePush(&qu, mid+1);
			QueuePush(&qu, end);
		}
	}
	QueueDestory(&qu);//队列销毁
}

非递归就是借助队列的特点,先将原数组的首尾下标入队,再从队头中拿出要操作数组的首尾下标,传入DigWay函数,其返回的基准值下标将一个数组分为左右两部分,然后再将左右两部分的数组首尾下标入队。循环直至整个数组有序。

 

 

五、优化

当数组基本趋于有序时使用插入排序较为有效,所以在快速排序的基础上,当待排序数组小于一定规模的时候使用插入排序效果更优。

在这里我定义为当数据规模小于8个的时候去调用直接排序调整。

void Quicking(int*src, int start, int end)
{
	int mid;//作为基准点
	if (start+8 < end)//满足条件,快速排序递归调用
	{
		mid = DigWay(src, start, end);
		Quicking(src, start, mid-1);
		Quicking(src, mid + 1, end);
	}
    else//数据规模小,使用插入排序
	{
		InsertSort(src + start, end - start + 1);
	}
}
void QuickSort(int* src, int n)//快速排序
{
	Quicking(src, 0, n - 1);
}

想看完整代码的可以点击我的gitHub链接:Sort完整代码

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值