排序算法5----快速排序(前后指针法)(C语言)

        f78262d0e34a46b7a1ddac42b39c0b0c.png

        前边我们介绍了快速排序的两种方法:Hoare法和挖坑法,今天我们介绍第三种办法:前后指针法。

        快排时间复杂度:O(NlogN) ,快速排序的空间复杂度为O (log n),其中n为待排序数组的长度。 这是因为快速排序是一种原地排序算法,它通过在原始数组上进行交换和划分操作来实现排序,而不需要额外的空间来存储临时数据。 在每一次递归调用中,快速排序只需要使用O (log n)的额外空间来保存递归调用的栈空间。

        前后指针法:引入prev和cur指针,用来表示下标。prev指针初始为begin位置,cur指针初始为prev+1位置。然后全程由cur往后找小,当cur找到小时,prev就朝后挪动一位,然后交换cur位置和prev位置的值。交换后,cur继续往后找小,直到cur越界时结束。最后交换prev位置的值和基准值。

a859b984bb1344a6ab048fdd4f24af0e.gif

         前后指针法的妙处:cur往后找小,找到小后交换arr[cur]和arr[++prev],如图可以看出,交换二者之后,cur和prev指针之间间隔的数一定是比基准值大的数(因为cur是在找小!)。当最后cur越界之后,prev指针往后的数全都是比基准值大的数,最后交换prev和基准值,那么此时基准值就处于中间位置。

        14f5f0c83e324ddfbf656ace273544f7.png9705e394ec374d2bb8cd0a7a0d88c311.png

那么接下来我要上代码了:

 2191d2e74a924d2fa15e8ce712d20171.png

        同样,引出“三数取中”法来进行优化:

//定义一个三数取中函数,取首尾中三个元素的中间值
//这样可以有效的提高效率
//避免每次单趟排序时的begin元素都是最大或最小
int GetMidi(int* arr, int begin, int end)
{
	int midi = (begin + end) / 2;
	if (arr[begin] < arr[end])
	{
		if (arr[midi] >= arr[begin] && arr[midi] <= arr[end])
			return midi;
		if (arr[midi] >= arr[end])
			return end;
		if (arr[midi] <= arr[begin])
			return begin;
	}
	else
	{
		if (arr[midi] >= arr[end] && arr[midi] <= arr[begin])
			return midi;
		if (arr[midi] >= arr[begin])
			return begin;
		if (arr[midi] <= arr[end])
			return end;
	}
}

        然后我们进行单趟排序:

int SingleSort_Pointer(int* arr, int begin, int end)
{
	//prev指针定义在begin位置,cur定义在prev后面一个位置
	int prev = begin;
	int cur = prev + 1;

	//取定基准值
	int key = arr[begin];

	//当cur越界时结束循环
	while (cur <= end)
	{
		//cur找小,当cur找到小后,就和prev下一个位置互换
		//一定是和prev下一个位置换

		//当然这里可能出现cur和prev相同的情况,自身互换也没啥问题,可优化可不优化
		if (arr[cur] < key)
		{
			prev++;
			Swap(&arr[prev], &arr[cur]);
		}
		//cur继续往下找小
		cur++;
	}
	//最后交换prev的值和基准值,再返回此时基准值的下标
	Swap(&arr[prev], &arr[begin]);
	return prev;
}

         最后再进行左右区间的递归:

void QuickSort_Pointer_incline(int* arr, int begin, int end)
{
	if (begin >= end)
		return;

	int midi = GetMidi(arr, begin, end);
	Swap(&arr[begin], &arr[midi]);

	int keyi = SingleSort_Pointer(arr, begin, end);

	QuickSort_Pointer_incline(arr, begin, keyi - 1);
	QuickSort_Pointer_incline(arr, keyi + 1, end);
}

        总结:从前面三种快排方法来看,快排其实很类似于二叉树结构:每次单趟排序后,又递归左区间和右区间,(类似左子树和右子树)。其核心思想在于递归!当然我们写了这么多发现,递归的快排是不是好像也就那样,但是难的是非递归快排!既然如此,那等后面,我在来展示一波非递归快排,先提示一波:借用栈来实现类似递归!

        bf4763675fa3477ebb49f57682826c46.png

  • 21
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

夹心宝贝

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

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

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

打赏作者

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

抵扣说明:

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

余额充值