快速排序算法

快速排序

基本思想

快速排序是基于分治法的,其思路如下:

  • 在待排序序列中,选取一个数值作为 基准 pivot ,通过一趟排序,将序列以 pivot 划分为了 左右两个子序列
  • 左子序列元素值都比 pivot 小,右子序列元素值都比 pivot 大,相当于一趟排序后,确定了 pivot 在序列中的最终位置
  • 分别递归对左右子序列重复操作,直至确定所有元素的最终位置

❓ 其难点主要在于,在某一趟排序(实则为某次递归中),如何划分左右两个子序列

思路一

在之前学习javascript的时候,看了阮一峰大神的博客,他给出一种浅显易懂的思路:

快速排序(Quicksort)的Javascript实现 - 阮一峰的网络日志 (ruanyifeng.com)

  • 直接开辟两个辅助空间,用来存储左右两个子序列
  • 遍历待排列表,与基准 pivot 比较,或小或大,分别存储在两个辅助空间中
  • 然后分别对这两个辅助列表元素重复上述操作

虽然方法易理解,易实现,但是每轮都要开辟额外的存储空间,空间复杂度是大于 O(n)

思路二

严蔚敏 老师的教材《数据结构》上,划分操作如下:

  • 假设每次以待排表中第一个元素为基准 pivot ,则将比 pivot 小的元素向左移动,比 pivot 大的元素向右移动
  • 其移动 通过 交换 实现,可以理解为因为 pivot 记录了表头(即第一个元素),所以表中无论怎么交换,总有一个位置为 “

思路三

在刷题时看到的一种思路,只利用一次循环,操作如下:

  • 序列末元素 为基准 pivot ,从首到尾遍历序列,以 i 记录首部下标
  • 对于比基准大或相等的元素,不做处理
  • 对于比基准小的元素,与 i 位置 做交换,i++ 后移一位
  • 遍历完成全部交换后,最后将 基准 pivoti 位置 交换,返回 i,确定 i 的最终位置

代码CPP实现

参考思路二:

划分操作(思路二)

// 划分操作,low为数组头部,high为尾部,按升序排列
int partition(int arr[], int low, int high) {
	int pivot = arr[low];		// 以数组第一个数为基准
	while (low < high) {		// 当low = high时,该躺排序(该次递归)完成
		while (low < high && arr[high] >= pivot)
			--high;				// 因为表头为“空”,所以从右到左比较,一直到出现比 pivot 小的数
		arr[low] = arr[high];	// 将该数存入“空”的位置(即low),high变为新的“空”的位置
		while (low < high && arr[low] <= pivot)
			++low;				// 因为空的位置已经更改在右侧,所以从左到右比较,一直到出现比 pivot 大的数
		arr[high] = arr[low];	// 将该数存入“空”的位置(即high),low 变为新的“空”的位置
	}							// 当结束遍历时,已经划分左右子表
	arr[low] = pivot;			// 确定 pivot 的最终位置 low(此时low = high)
	return low;					// 返回 pivot 的位置下标
}

操作步骤可以跟随注释过一遍,这也解释了 两个子表的划分以及元素的移动 是通过 交换 实现的。

快速排序并不产生有序子序列,每趟排序后会确定 pivot 的最终位置

划分操作(思路三)

// 划分操作,low为数组头部,high为尾部,按升序排列
int partition(vector<int> &arr, int low, int high) {
	int pivot = arr[high];				// 以数组末元素为基准
	int i = low;						// i 为交换位置
    for(int j = low; j < high; j++){
        if(arr[j] < pivot)
            swap(arr[i++],arr[j]);		// 先交换位置,i再后移一位
    }
    swap(arr[i],arr[high]);				// 确定 pivot 的最终位置
    return i;
}

递归的程序结构

void quickSort(int arr[], int low, int high) {
	if (low < high) {			// 递归跳出条件
		int pivotpos = partition(arr, low, high);	// 先划分子表,后递归,由上向下确定每个基准的最终位置
		quickSort(arr, low, pivotpos - 1);
		//quickSort(arr, pivotpos, high);			//wrong,基准元素位置已经确定为 pivotpos,不必再参与排序
		quickSort(arr, pivotpos + 1, high);
	}
}

性能分析与改进

  • 平均空间复杂度 O(logn) ,借助了递归工作栈,平均情况下的递归栈深度为 O(logn)

  • 平均时间复杂度 O(nlogn)

  • 稳定性:不稳定,元素值相同的元素相对位置会发生改变

  • 提高效率:尽量选择贴近中值的元素作为基准,那么形成的递归树就会贴近 完全二叉树,从而递归深度最小

更多排序算法以及动画演示可以参考:

十大经典排序算法(动图演示) - 一像素 - 博客园 (cnblogs.com)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值