【408DS算法题】基础002-快速排序

题目

代码实现快速排序,将线性表a按照从小到大的顺序进行排序。
(*注意:此算法中用到了递归,关于实现递归的细节思想先不在此讨论)


初步分析实现

首先梳理快速排序算法的基本思想:

  1. 每趟选择一个元素作为枢轴(pivot)
  2. 每趟扫描的目标是将数组元素整理为 {<枢轴}{枢轴}{>枢轴} 这样的三部分,这样枢轴元素的位置就调整为了排序完成时的最终位置
  3. {<枢轴}{>枢轴} 这两部分递归进行同样的扫描
  4. 随着扫描的进行,每部分的元素个数不断减小,当元素个数减小为1时,表示已经到达递归的终点

本文中枢轴的选择策略——每次选择范围内最左侧元素。
另外,为了维持算法的稳定性,将=pivot的元素看作>pivot的元素,保持其一直待在枢轴元素的右侧,这样至少大小为pivot的元素是稳定的。

理想中一趟扫描前后对比:
在这里插入图片描述
[灰色格子为枢轴元素,此处元素的ab下标是为了区分相同大小的元素,用于检测算法的稳定性]

扫描具体过程(用双指针实现)
初始化:
在这里插入图片描述
双指针开始执行扫描操作:
在这里插入图片描述
双指针继续执行:
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
以上就是当前策略下一趟排序的过程,具体实现如下:

测试版实现

void quickSort(vector<int>& a, int left, int right){
	// 元素范围为[left, right],left>=right时无需调整
	if(left >= right)
		return ;
	// [left, right]区间内元素个数大于1时,进行调整
	int i = left, j = right;
	// 选择最左边的元素作为pivot
    int pivot = a[left]; 
	while (i < j){
		// 尝试尽量保持算法稳定性,使>pivot与=pivot等价
		while (i < j && a[j] >= pivot)
            j--;
		a[i++] = a[j];
		// <pivot作为第二种情况
		while (i < j && a[i] < pivot)
            i++;
		a[j--] = a[i];
	}
	// 将pivot归位
	a[i] = pivot;
	// 递归扫描区间[left, i)  (元素值<=pivot)
	quickSort(a, left, i - 1);
	// 递归扫描区间(i, right]  (元素值>=pivot)
	quickSort(a, i + 1, right);
}

分析改进

  1. 从上面的例子可以看到算法依旧是不稳定的(数组 ⟸ \Longleftarrow 调整时类似于数组逆置),因此可以放弃一开始为了保持算法稳定性的操作,追求尽可能快地进行一趟扫描——两指针都不对=pivot的元素进行移动操作
  2. 上面例子中出现了当i==j时对两元素进行交换的情况,可以添加条件避开(挖个坑,条件判断的时间消耗是不是真的<i==j时数组交换的时间消耗)

最终版

void quickSort(vector<int>& a, int left, int right){
	// 元素范围为[left, right],left>=right时无需调整
	if(left >= right)
		return ;
	// [left, right]区间内元素个数大于1时,进行调整
	int i = left, j = right;
	// 选择最左边的元素作为pivot
    int pivot = a[left]; 
	while (i < j){
		// 追求更快地完成一趟扫描,略过=pivot
		while (i < j && a[j] >= pivot)
            j--;
		// 对数组赋值添加前提条件
		if(i < j)
			a[i++] = a[j];
			
		// 同样略过=pivot
		while (i < j && a[i] <= pivot)
            i++;
		if(i < j)
			a[j--] = a[i];
	}
	a[i] = pivot;
	
	quickSort(a, left, i - 1);
	quickSort(a, i + 1, right);
}

总结分析

以上就是经典的快速排序,下面对比一下优化前后一趟扫描的过程(右侧为优化后):
在这里插入图片描述
再看一下排序的整体过程(优化版):
在这里插入图片描述
[红色代表元素已经处在最终位置。灰色表示此趟扫描选择的枢轴]

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值