当前位置: 首页 > news >正文

快速排序的详解

主要讲解快速排序的递归的三种实现方法和非递归的实现方法.

目录

递归版本

1.Hoare经典版

2.挖坑法

3.前后指针法

非递归版本:
​​​​​​​

递归版本

1.Hoare经典版

单趟排序思想:

1.选取最左边值为key,然后从右边开始向左找小于key的值.

2.找到之后,左边再走,找到大于key的值.

3.左边也找到之后,交换两个值.

4.右边继续向做找,找到之后,左边再继续向后找,找到之后再次进行交换,如此反复.

5.直到两者相遇,选最左边的值与key交换.

此时key就在正确的位置上.

看以下动图来理解单趟排序.

图片来源于网络. 

所以每次找到keyi值以后,再次找keyi剩下两侧的keyi值,然后再次划分,如此反复...

下面是代码实现:

int partion1(int* a, int left, int right)
{
	int keyi = left;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])//从右开始向左找,直到找到小于a[keyi]的值
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])//从左边开始向右找,直到找打大于a[keyi]的值
		{
			left++;
		}
		swap(a[left], a[right]);//交换两个值
	}
	swap(a[keyi], a[left]);//swap(a[keyi],a[right])也可以,因为此时left和right相遇了,left=right
	return left;//此时keyi值已经到了left(right)的位置,返回即可
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int keyi = partion1(a, left, right);
	//[left,keyi-1], keyi,[keyi+1.right]
	
	//开始排序[left,keyi-1]
	QuickSort(a, left, keyi - 1);
	//开始排序[keyi+1,right]
	QuickSort(a, keyi + 1, right);
}

2.挖坑法

这一步是hoare法的一种变形.

单趟排序思想:

1.先将最左边的数据存放在临时变量key中,然后就当此处形成一个坑位.

2.然后从右开始想左找,若小于key,则把这个值放到那个坑位中,然后本身的位置形成一个新的坑位.

3.再从左向右找,若大于key,则放到上一步形成的坑位中,本身的位置也形成一个坑位.

4.循环步骤123

5.两者相遇时,一定会有一个坑位,将key值放到其中即可.

动图如下:

图片来源于网络. 

每次返回坑位的的值下标(left,right均可),并且不断缩小范围,再划分,找key,直至全部排序完毕.

int partion2(int* a, int left, int right)
{
	int key = a[left];
	int piti = left;
	while (left < right)
	{
		while (left < right && a[right] > key)//从右向左找,直到找到小于key的值
		{
			right--;
		}
		//将a[right]的值放入到坑位中
		a[piti] = a[right];
		//并在这个位置重新形成坑位
		piti = right;
		
		while (left < right && a[left] < key)//从左向右找,直到找打大于key的值
		{
			left++;
		}
		a[piti] = a[left];
		piti = left;
	}
	//相遇之后,把key值放到坑位中,此时key已经到了正确位置
	a[piti] = key;
	//返回此时坑位下标
	return piti;
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int keyi = partion2(a, left, right);
	//[left,keyi-1], keyi,[keyi+1.right]
	
	//开始排序[left,keyi-1]
	QuickSort(a, left, keyi - 1);
	//开始排序[keyi+1,right]
	QuickSort(a, keyi + 1, right);
}

3.前后指针法

这是我们推荐的一种写法.

基本思想:

1.选取prev为序列的开始,cur为prev的下一个位置,同时选取最左边的下标值为keyi.

2.cur先走,找到小于key的值,停下来.

3.prev++

4.此时再交换a[prev]和a[cur]的值

5.一种重复2,3,4步,直到停止,交换key和prev的值

此时key值就在正确位置了.

在这个过程中,prev相当于是维护小于key的值,这样停下来时,prev前面的全是小于key的值,所以此时交换key和prev,prev就是正确位置了.

动图如下:

 

int partion3(int* a, int left, int right)
{
	int prev = left;
	int cur = left + 1;
	int keyi = left;

	while (cur <= right)
	{
		if (a[cur] < a[keyi])//如果a[cur]的值小于a[keyi]
		{
			prev++;//先prev++;
			swap(a[cur], a[prev]);//再交换prev和cur的值.
		}
		cur++;
	}
	swap(a[keyi], a[prev]);//最后交换a[keyi]和a[prev]的值
	keyi = prev;
	return keyi;
}
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	int keyi = partion3(a, left, right);
	//[left,keyi-1], keyi,[keyi+1.right]
	
	//开始排序[left,keyi-1]
	QuickSort(a, left, keyi - 1);
	//开始排序[keyi+1,right]
	QuickSort(a, keyi + 1, right);
}

非递归版本:

递归有个大问题就是当数据量过大时,在极端情况下,会对栈的开销太大,造成栈溢出.

所以这个时候有两种:

一种是改成循环

第二是用栈来模拟递归,因为栈本质是用数组,这个范围可是非常大的.所以这个题就用栈来模拟递归.

递归版本的主要过程就是在不断的划分子区间,所以如果非递归实现快排,就用栈来保存它的区间并不断划分保存

基本思想:

1.申请一个栈,用来保存数组的起始和终点位置.

2.将整个数组的起始位置和重点位置入栈(由于栈的特性是先进后出,后进先出,所以先入right,再入left)

3.定义begin来接受栈顶元素、进栈操作,end接受栈顶操作,出栈操作

4.对数组进行一次单趟排序,保存keyi值,即关键值下标.

5. 这时候需要排基准值key左边的序列。

把右区间的左右范围值压入栈中

压完之后,再把左区间的左右范围压入.

(反了也可以,只不过成了先遍历划分右区间,再划分左区间了)

6.判断栈是否为空,不为空,重复4,5步,若为空,说明所有元素已经排序完毕.

代码如下:

void QuickSortNonR(int* a, int begin, int end)
{
	stack<int> st;
	st.push(end);//先取整个数组的终点位置
	st.push(begin);//再去起始位置.
	while (!st.empty())
	{
		int left = st.top();//由于栈后进先出,所以取出的是begin
		st.pop();

		int right = st.top();//取出end
		st.pop();

		int keyi = partion3(a,left,right);//保存排序好的keyi值
        //递归模拟过程

		if (keyi + 1 < right)//如果右区间还存在,则继续划分,并把右区间左右范围存放到栈中
		{
			st.push(right);
			st.push(keyi + 1);
		}
		if (left < keyi - 1)/如果左区间还存在,则继续划分,并把左区间左右范围存放到栈中
		{
			st.push(keyi - 1);
			st.push(left);
		}
	}
}

这就是快速排序的全部内容啦,如果有疑问或错误的地方,欢迎提问或指正哦!

相关文章:

  • 建设信用卡银行积分兑换商城网站/营销咨询服务
  • wordpress最大发布大小/指数分布
  • 东莞网约车平台/百度seo排名规则
  • 无网站如何做淘宝客/网络营销策划书范文
  • 维护官网/seo课培训
  • 肇庆网站建设推广/搜索引擎营销的方法不包括
  • 【一年总结】我的大三
  • python装饰器
  • 【蓝桥杯04】:给定—个单词,请计算这个单词中有多少个元音字母,多少个辅音字母。元音字母包括a, e, i, o, u,共五个,其他均为辅音字母。
  • 【node.js从入门到精通】模块化+npm包详解
  • 【以太网硬件十四】以太网的MAC是干什么的?
  • 信号处理之声源定位
  • 前端进阶垫脚石-前端工程化
  • vs2022 命名空间“System”中不存在类型或命名空间名“Printing”
  • pytorch :OSError: [WinError 1455] 页面文件太小,无法完成操作。 Error loading 【已解决】
  • 算法概述——什么是算法、什么是数据结构以及关于时间复杂度的问题
  • Apache-Commons-FileIOUtils工具类常用方法使用
  • JavaScript-自制网页内弹窗[可移动][DOM][纯HTML]