数据结构系列-快速排序

🌈个人主页:羽晨同学

💫个人格言:"成为自己未来的主人~"

快速排序

递归hoare版本

首先,我们先来看一段示意图:

我们根据这份原理图就可以很清楚的看到快速排序的底层原理。

我们将key定义在初始位置。然后让右边先走,找到比key小的数字,然后左边再走,找到比key大的数字,然后两个发生交换,最后两者相遇,相遇位置就是key的位置的数字应该存在的位置。

首先,我们对快速排序进行声明:

cpp 复制代码
//Sort.h
// 快速排序递归实现
// 快速排序hoare版本
void PartSort1(int* a, int left, int right);

接下来,我们对快速排序进行定义:

cpp 复制代码
//Sort.c
// 快速排序递归实现
// 快速排序hoare版本
void PartSort1(int* a, int left, int right)
{
	int keyi = left;
	int begin = left;
	int end = right;
	if (left > right)
		return;
	while (left < right)
	{
		while (left < right && a[right] >= a[keyi])
		{
			right--;
		}
		while (left < right && a[left] <= a[keyi])
		{
			left++;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[keyi], &a[left]);
	keyi = left;
	PartSort1(a, begin, keyi - 1);
	PartSort1(a, keyi+1,end);
}

测试部分:

cpp 复制代码
//Test.c
#define _CRT_SECURE_NO_WARNINGS
#include"Sort.h"
int main()
{
	int a[] = { 4,5,8,9,2,1,0,5,8,5 };
	PartSort1(a, 0,sizeof(a)/sizeof(int)-1);
	for (int i = 0; i < sizeof(a) / sizeof(int); i++)
	{
		printf("%d ", a[i]);
	}
	return 0;
}

挖坑法

我们其实可以很明显的观察到,这个和前面的hoare是极其相似的。

那下面,我们就来实现一下挖坑法:

cpp 复制代码
//Sort.c
// 快速排序挖坑法
void PartSort2(int* a, int left, int right)
{
	int keyi = a[left];
	int begin = left;
	int end = right;
	if (left > right)
		return;
	while (left < right)
	{
		while (left<right && a[right]>=keyi)
			right--;
		Swap(&a[left], &a[right]);
		while (left < right && a[left] <= keyi)
			left++;
		Swap(&a[right], &a[left]);
	}
	a[left] = keyi;
	PartSort2(a, begin, left - 1);
	PartSort2(a, left + 1, end);
}

前后指针法

接下来,我们介绍一下快速排序的双指针的实现方法:

我们首先仍是一样的,将初始位置的值定义为key,定义prev和cur,然后在cur指向的值不超过key的前提下,cur走一步,prev走一步,在什么时候二者分开呢?那就是当cur指向的值超过key的时候,然后当cur的值小于key的时候,二者发生交换,并在最后当cur越界的时候,prev和key进行交换。

下面,我们具体说一下,前后指针法的实现逻辑。

cpp 复制代码
// 快速排序前后指针法
void PartSort3(int* a, int left, int right)
{
	int keyi = left;
	int begin = left;
	int end = right;
	int prev = left;
	int cur = left + 1;
	if (left >= right)
		return;
	while (cur<=end)
	{

		if ( a[cur] < a[keyi] && ++prev != cur)
			Swap(&a[prev], &a[cur]);
		
		cur++;
	}
	Swap(&a[prev], &a[keyi]);
	keyi = prev;
	PartSort3(a, begin, keyi - 1);
	PartSort3(a, keyi + 1, end);
}

其实前后指针法相对于前面的两种方法来说,真的可以算的上是代码简单。

快速排序无递归版本

大家可能不了解为什么会需要这种不需要递归的版本,这是因为,对于递归是需要开辟栈帧的,对于,快速排序而言,一旦递归次数过多,就会导致栈的溢出。

这种情况就是无递归版本的应用场景。

cpp 复制代码
// 快速排序 非递归实现
void QuickSortNonR(int* a, int left, int right)
{
	ST st;
	STInit(&st);
	STPush(&st, right);
	STPush(&st, left);
	while (!STEmpty(&st))
	{
		int begin = STTop(&st);
		STPop(&st);
		int end = STTop(&st);
		STPop(&st);
		// 单趟
		int keyi = begin;
		int prev = begin;
		int cur = begin + 1;

		while (cur <= end)
		{
			if (a[cur] < a[keyi] && ++prev != cur)
				Swap(&a[prev], &a[cur]);

			++cur;
		}

		Swap(&a[keyi], &a[prev]);
		keyi = prev;
		if (keyi + 1 < end)
		{
			STPush(&st, end);
			STPush(&st, keyi + 1);
		}
		if (begin < keyi - 1)
		{
			STPush(&st, keyi - 1);
			STPush(&st, begin);
		}
	}
	STDestroy(&st);
}

我们主要是利用的栈的性质模仿递归的过程,其最重要的部分就是left和right,我们就可以利用栈来得到新的left和right

改进

为了提高快速排序的效率,几位大佬创造了三数取中法,和随机值法,来改变keyi的值,来提高快速排序的效率。

小区间优化

我们可以肯定的是,当一个数组中的数值个数较少时,使用快速排序开辟新的栈帧是极其浪费的,这个时候最好的方式就是使用插入排序。

cpp 复制代码
	if (right - left + 1 < 10)
	{
		InsertSort(a + left, right - left + 1);
	}

随机数法

cpp 复制代码
		int randi = rand() % (right - left + 1);
		randi += left;
		Swap(&a[left], &a[randi]);

三数取中

cpp 复制代码
int GetMidi(int* a, int left, int right)
{
	int mid = (left + right) / 2;
	//printf("%d %d %d", left, mid, right);
	//printf("->%d %d %d\n", a[left], a[mid], a[right]);

	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
		{
			return mid;
		}
		else if (a[left] > a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
	else // a[left] > a[mid]
	{
		if (a[mid] > a[right])
		{
			return mid;
		}
		else if (a[left] < a[right])
		{
			return left;
		}
		else
		{
			return right;
		}
	}
}
cpp 复制代码
		int midi = GetMidi(a, left, right);
		//printf("%d\n", midi);
		Swap(&a[left], &a[midi]);
相关推荐
-qOVOp-34 分钟前
408第一季 - 数据结构 - 栈与队列
数据结构
Epiphany.55638 分钟前
堆排序code
数据结构·c++·算法
秋山落叶万岭花开ღ41 分钟前
树的基本概念与操作:构建数据结构的层级世界
数据结构·python·算法
追烽少年x1 小时前
数据结构---红黑树
数据结构
水水沝淼㵘2 小时前
嵌入式开发学习日志(数据库II && 网页制作)Day38
服务器·c语言·网络·数据结构·数据库·学习
心扬6 小时前
python数据结构和算法(5)
数据结构·python·算法
与己斗其乐无穷8 小时前
数据结构(9)排序
数据结构
NULL指向我9 小时前
C语言数据结构笔记6:使用宏和指针来设置和操作嵌套在结构体中的联合体数组的特定位
c语言·数据结构·笔记
焜昱错眩..10 小时前
代码随想录训练营二十六天| 654.最大二叉树 617.合并二叉树 700.二叉搜索树的搜索 98.验证二叉搜索树
数据结构·算法
笑口常开xpr10 小时前
线 性 数 据 结 构 双 雄:栈 与 队 列 的 原 理、实 现 与 应 用
数据结构··队列