快速排序法(原理详细分析)

快速排序

时间复杂度:平均为O(nlog(n)) 最差情况为O(n2)
空间复杂度:O(log(2
n)) ~ O(n)
稳定性:不稳定

算法原理

首先给出几个定义

(1)子数组的头指针尾指针:在一个数组中,假设有一个子数组。这个子数组的第一个值在原数组的下标,我们定义为子数组的头指针。而子数组的最后一个值在原数组的下标,我们定义为子数组的尾指针。
例子:
原序列

{2 3 7 4 3 8 7}

在其中随便取出一个子序列

{2 3 (7 4 3) 8 7}

得到

{7 4 3}

则子数组{7 4 3}的头指针为2(数组首地址为0),尾指针为4。
(2)参考值参考指针:对于任意一个子数组,首先选定子数组的第一个元素为参考值,参考值在原数组中的位置被称为初始参考指针。
例子:
原序列

{2 3 7 4 3 8 7}

取出一个子序列

{7 4 3}

初始参考值是子数组的第一个元素7,参考指针为2。此时的参考指针和子序列的头指针是一样的(但注意,我们是要排序的,排序后就不一定一样了)。

快速排序都干了啥?

从小到大排序为例。
快速排序本质是对参考值与数组的其他值进行一系列交换操作,使得交换后子序列满足如下条件
(1)位于参考值前的数据都小于参考值
(2)位于参考值后的数据都大于参考值
(3)对参考值前的所有数据(即小于参考值的数据)没有顺序要求
(4)对参考值后的所有数据(即大于参考值的数据)没有顺序要求
先不要管一系列交换操作都干了什么,先来理解一下快速排序的大局思路。
按照如下操作:
(1)先对整个数组做上述交换操作
(2)取参考值前的所有数据作为一个子数组,我们给他起个名字叫左数组
(3)取参考值后的所有数据作为一个子数组,我们给他起个名字叫右数组
(4)分别对左数组和右数组重复上述交换操作
那么什么时候停止计算呢?
答曰:当子数组为空或者只有一个元素时,停上述操作
于是按照上述步骤就实现了排序。以数组{4 3 7 2 5 1 6}为例,排序过程如下图所示。注意:我们在操作子数组的时候都直接在原数组中操作,这样操作结束后原数组就是有序的了。
快速排序法
接下来看看如何实现上述的一系列交换操作的。
这里就需要用到头指针尾指针参考指针

具体思路为:

首先进行尾指针和参考指针的相关操作
(1)判断尾指针里的数据是否小于参考值。
(2)如果小于,交换参考值和尾指针里的数值(尾指针不变),参考指针移动到尾指针处,结束尾指针的操作。
(3)如果大于或者等于,尾指针向前移动一位。
(4)判断尾指针是否和头指针重合,不重合时重复上述操作,否则结束尾指针的操作。
之后进行头指针和参考指针的相关操作
因为有尾指针的操作,这时参考指针已经和头指针脱离开了。
(1)判断头指针里的数据是否大于参考值。
(2)如果大于,交换参考值和头指针里的数值(头指针不变),参考指针移动到头指针处,结束头指针的操作。
(3)如果小于或者等于,头指针向前移动一位。
(4)判断尾指针是否和头指针重合,不重合时重复上述操作,否则结束尾指针的操作。
进入头指针操作时尾指针和参考指针是重合的,但经过头指针操作二者又脱离开了。
最后判断头指针和尾指针是否重合
如果没重合,重新进行尾指针操作和头指针操作,一直到头尾指针重合。

关于上述操作的理解

这样操作可以保证,头指针左侧的数都一定小于参考数,尾指针右侧的数都一定大于参考数!!!
因为:尾指针中的数大于参考数时才会向左移动,等于或者小于的时候则不会动;头指针中的数小于参考数时才会向右移动,等于或者大于的时候则不会动。

从小到大排序的代码如下:

#include <iostream>
#include <vector>
using namespace std;
//快速排序
//从小到大
void fastSort(vector<int> &numVec, int left, int right)
{
	//如果为空数组或者数组长度为1,则不需要排序了,直接返回
	//这个判断十分重要,它可以使递归过程结束。不加这个逻辑会进入递归的死循环。
	if (!(left < right))
	{
		return;
	}
	//参考指针(里面存着参考数)
	int rPtr = left;

	//头指针
	int sPtr = left;

	//尾指针
	int ePtr = right;

	//排序部分
	//若头指针和尾指针没有重合
	while (sPtr < ePtr)
	{
		//尾指针操作部分
		//尾指针不可以超过头指针
		while (sPtr < ePtr)
		{
			//判断尾指针里的数据是否大于参考数据
			if (numVec[rPtr] <= numVec[ePtr])//从大到小改为>=
			{
				//如果大于:
				//尾指针向前移动一位
				ePtr--;
			}
			else
			{
				//如果小于:
				//(1)交换参考指针与尾指针的数值
				//(2)参考指针位置改变到尾指针处
				//(3)跳出循环
				swap(numVec[rPtr], numVec[ePtr]);
				rPtr = ePtr;
				break;
			}
		}

		//头指针操作部分
		//头指针不可以超过尾指针
		while (sPtr < ePtr)
		{
			//判断头指针里的数据是否小于参考数据
			if (numVec[rPtr] >= numVec[sPtr])//从大到小改为<=
			{
				//如果小于:头指针向后移动一位
				sPtr++;
			}
			else
			{
				//如果大于:
				//(1)交换参考指针与头指针的数值
				//(2)参考指针位置改变到尾指针处
				//(3)跳出循环
				swap(numVec[rPtr], numVec[sPtr]);
				rPtr = sPtr;
				break;
			}
		}
	}
	//排序结束
	//排序结果为:
	//参考数位于数组的最中间
	//参考数前的所有数都小于参考数
	//参考数后的所有数都大于参考数

	//递归部分:
	//对参考数前的所有数再次执快速排序
	fastSort(numVec, left, rPtr - 1);
	//对参考数后的所有数再次执快速排序
	fastSort(numVec, rPtr + 1, right);
}

int main()
{
	vector<int> numVec{ 4,3,7,2,5,1,6 };

	//numVec             待排序数组
	//0                  排序下标范围的起始点
	//numVec.size() - 1  排序下标范围的结束点
	//首先取整个数组为子数组
	fastSort(numVec, 0, numVec.size() - 1);

	//打印排序结果
	for (int i = 0; i < numVec.size(); i++)
	{
		cout << numVec[i] << " ";
	}
	cout<<endl;
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值