快速排序(单趟排序算法,递归和非递归实现)

一.快速排序简介      

        快速排序是由东尼·霍尔所发展的一种排序算法。其本质是二分思想,假设一维数组,存储数据为n个,快排每次单层排时间复杂度为O(N)(注意是一层排),每次排好一个数的位置然后二分,将递归深度降为接近完全二叉树的数高:logN,总的时间复杂度为O(N*logN)。

二.快速排序核心逻辑

        快速排序是通过每次选择基准值 key ,然后排 key 的位置,使左边小于等于key,右边大于等于key,通过key分割出左右区间,[left,keyi-1 ]和[keyi+1,right];再用这种方式对左右区间进行分割。

关键:先对排好一个数,分割出左右区间,如果左区间有序,右区间有序,那么整体有序;

左右区间如何有序?

         对左右区间进行快速排序(即递归调用),终止条件:区间长度为1,或区间不存在,即 left>=right。

三.快速排序单趟排序算法

(1)东尼·霍尔方法

        假设一维数组a, 对其区间 [begin,end] 排升序,选择 a[begin] 为 key,end 找比 key 小的值,begin找比 key 大的值,然后 a[begin] 和 a[end] 交换;重复上述步骤,直至 begin 和 end 相遇。

逻辑代码如下:

// 快速排序hoare版本
int PartSort1(int* a, int left, int right)
{
	int keyi = left;
	int begin = left, end = right;
	while (left < right)
	{		
		//找小
		while (left<right && a[right]>=a[keyi])
		{
			right--;
		}
		//找大
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[right], &a[left]);
	}
	Swap(&a[left], &a[keyi]);
    //返回相遇位置的下标
	return left;
}

 (2)挖坑法

        假设一维数组a, 假设一维数组a, 对其区间 [begin,end] 排升序,选择 a[begin] 为 key,begin位置形成坑,end 找比 key 小的值,begin找比 key 大的值。从end(非坑)开始找,找到比 key 小的值,填到 begin(坑) 中,此时,end 形成坑,begin(非坑)找小;循环往复,直至 begin 和 end 相遇,相遇位置为 坑,将 key 放进去,返回相遇位置的下标。

逻辑代码如下

// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
	int key = a[left];
	int begin = left, end = right;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			right--;
		}
		a[left] = a[right];
		while (left<right && a[left]<=key)
		{
			left++;
		}
		a[right] = a[left];
	}
	a[left] = key;
    //返回相遇位置的下标
	return left;
}

 (3)前后指针法

选择 left 位置值为key,prev 从left 开始,cur 从 left+1 开始(保证区间存在),cur 找比key 小的,找到了后,先++prev,然后交换;结束条件,cur 超出了区间范围,此时prev 的位置就是key的最终位置,将 prev 的位置与初始 key 的位置交换。

逻辑代码如下:

// 快速排序前后指针法
int PartSort3(int* a, int left, int right)
{
	int key = a[left];
	int prev = left, cur = left+1;
	while (cur<=right)
	{
		if (a[cur] < key) 
		{
			++prev;
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[left], &a[prev]);
	return prev;
}

四.整体方法(递归)

逻辑代码如下:

void QuickSort(int* a, int left, int right) 
{
	//区间不存在 或 区间长度为1,返回
	if (left >= right)
		return;
	int pos = PartSort1(a, left, right);
	QuickSort(a, left, pos - 1);
	QuickSort(a, pos + 1, right);
}

 五.快速排序(非递归)

如何写非递归,首先来分析一下,快速排序的核心是什么?

答:核心思想:单趟排序和二分。核心数据:区间

那么非递归就很明确了,通过数据结构栈,模拟函数栈帧的压栈和出栈,存的值是区间。

// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right) 
{
	Stack st;
	StackInit(&st);
	//右先入栈,取的时候左先出
	StackPush(&st, right);
	StackPush(&st, left);
	while (!StackEmpty(&st))
	{
		int begin = StackTop(&st);
		StackPop(&st);
		int end = StackTop(&st);
		StackPop(&st);
		//区间不存在 或 只剩一个值已经有序
		if (begin >= end)
			continue;
		//区间[begin,pos-1] pos [pos+1,end]
		int pos = PartSort1(a, begin, end);
		//压入[pos+1,end]区间
		StackPush(&st, end);
		StackPush(&st, pos + 1);
		//压入[begin,pos-1]区间
		StackPush(&st, pos - 1);
		StackPush(&st, begin);
	}
	StackDestroy(&st);
}

其中 Stack 为手动实现的栈

 源文件链接:挺6的还/数据结构初阶 (gitee.com)

以上代码为完全模拟递归的过程,实现中可以优化,如:区间不存在 或 区间长度等于1不入栈。

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

挺6的还

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值