数据结构—排序算法篇二

本期是对排序的深入讲解,排序能让我对循环和递归的第二次理解,让我们重新感受到循环和递归的强大!学完排序感觉脑子在燃烧,这可能就是对编程的热爱吧。

快排(Quicksort)

快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本思想为:任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

快排的思想:其核心思想是 "分治法"------ 通过 "选基准、分区、递归排序" 三步,将大问题拆解为小问题解决。

如下图:

先设立一个对比关键,先让右边先走,找到比key小的值停下来,然后再让左边走,找到比key大的值停下来,然后让小值和大值进行交换(直到他们相遇),最后肯定是碰到小于key的值停下来(因为是右边先找小),相遇之后在与key交换位置。最后在重新设定key。

代码实现

c 复制代码
// 快速排序hoare版本
void QuickSort(int* a, int left, int right)
{
	int ki = left;
	int begin=left, end=right;
	while (begin < end)
	{
		while (begin < end&&a[end] >= a[ki])
		{
			end--;
		}
		while (begin < end && a[begin] <= a[ki]) {

			begin++;
		}
		Swap(&a[end], &a[begin]);
	}
	Swap(&a[ki], &a[right]);
	ki = begin;
	QuickSort(a, left, ki-1);
	QuickSort(a, ki+1, right)
}

这是霍尔版本的快排

快排前后指针法

这是用两个指针实现的快排,其中cur找到比key小的 找到比key小的值就让prev指针++(往后走),cur指针++(也往后走)。cur遇到比key大的也往后走,直到遇到比key小的(prev++),然后再让prev(此时它指向的是大于key的值)和cur(指向的是小于key的值),并进行交换。

前后指针法实现

c 复制代码
void QuickSort(int* a, int left, int right)
{
int keyi = left;
int perv = left;
int cur = perv + 1;
while (cur <= right)
{
	if (a[cur] < a[keyi]&&++perv!=cur)
	Swap(&a[cur], &a[perv]);

	cur++;
}
 Swap(&a[perv], &a[keyi]);
 int ki=perv;
 QuickSort(a, left, ki - 1);
 QuickSort(a, ki + 1, right);
}

快速排序非递归版本

非递归版本想起来比较繁琐,最好要用栈(数据结构)实现,当然也可以用队列(但是比较麻烦)区别就是一个深度优先遍历( DFS),一个广度优先遍历(BFS)。

首先,我用栈保存所用区间。通过向栈中添加数据(所用区间),取数据。利用循环的方式,来实现非递归的实现原理。

快速排序非递归版本代码实现

c 复制代码
void QuickSortNonR(int* a, int left, int right)
{
	Stack sl;
	StackInit(&sl);

	StackPush(&sl, right);
	StackPush(&sl, left);
	while (!StackEmpty(&sl))
	{
		int begin = StackTop(&sl);
		StackPop(&sl);

		int end = StackTop(&sl);
		StackPop(&sl);
		int keyi = PartSort3(a, begin, end);

		if (keyi + 1 < end)
		{
			StackPush(&sl, end);
			StackPush(&sl, keyi+1);
		}
		if (keyi - 1 > begin)
		{
			StackPush(&sl, keyi - 1);
			StackPush(&sl, begin);
		}
	}

}

快速排序挖坑法

这是关于快速排序的另外一种写法,就是根据为什么左边设关键字,右边先走而不是左边先走设计而来的。

核心思想:就是如果左边是坑,就让右边先走,如果右边是坑就让左边先走,同时将符合的数据移到坑中,在更换坑的位置。

如下图:

就比如说先把6给key,左边为坑,右边先走,找小,找到之后放到坑里,同时更换坑的位置在右边,让左边找大, 找到之后放到坑里,更换坑的位置,在依次下去;

挖坑法代码实现

c 复制代码
// 快速排序挖坑法
int PartSort2(int* a, int left, int right)
{
	int ki = a[left];
	int begin = left;//坑位
	while (left<right)
	{
		while (a[right] >= ki&& left < right)
		{
			right--;
		}
		a[begin] = a[right];
		begin = right;
		while (a[left] <= ki && left < right)
		{
			left++;
		}
		a[begin] = a[left];
		begin = left;
	}
	a[begin] = ki;
	return begin;
}

void QuickSortPartSort2(int *a,int left,int right)
{
	if (left >= right)
		return;
	int pi = PartSort2(a, left, right);

	QuickSortPartSort2(a, left, pi - 1);
	QuickSortPartSort2(a, pi + 1, right);
}

快排优化

最后快排还可以优化

选出key不大不小的值 这是三数取中法,能更快确定中间值

还可以经行小区间优化,当剩下10个数的时候,就不要去递归了,直接用插入排序排完返回即可。

快排优化代码

c 复制代码
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
	return;
	//三数选一
	int midi = GetMidi(a,left,right);
	Swap(&a[left], &a[midi]);
	//小区间优化
	if ((right - left + 1) < 10)
	{
		InsertSort(a+left, (right - left + 1));
	}
	else
	{
		/*int ki = left;
		int begin = left, end = right;
		while (begin < end)
		{
			while (begin < end && a[end] >= a[ki])
			{
				end--;
			}
			while (begin < end && a[begin] <= a[ki])
			{

				begin++;
			}
			Swap(&a[end], &a[begin]);
		}*/
		//Swap(&a[ki], &a[begin]);
		int ki = PartSort1(a,left,right);
		QuickSort(a, left, ki - 1);
		QuickSort(a, ki + 1, right);
	}
}

最后快排的时间复杂度是O(N*logN)。

相关推荐
小年糕是糕手1 天前
【C++】string类(二)
开发语言·数据结构·c++·程序人生·算法·leetcode·数字货币
Tisfy1 天前
LeetCode 3573.买卖股票的最佳时机 V:深度优先搜索
算法·leetcode·深度优先
TimelessHaze1 天前
算法复杂度分析与优化:从理论到实战
前端·javascript·算法
李玮豪Jimmy1 天前
Day42:单调栈part2(42.接雨水、84.柱状图中最大的矩形)
java·算法
yaoh.wang1 天前
力扣(LeetCode) 58: 最后一个单词的长度 - 解法思路
python·程序人生·算法·leetcode·面试·职场和发展·跳槽
LYFlied1 天前
【每日算法】LeetCode239. 滑动窗口最大值
数据结构·算法·leetcode·面试
XiaoHu02071 天前
C++ 数据结构关于二叉搜索树
数据结构·算法
CoovallyAIHub1 天前
下一代驾驶员监测系统如何工作?视觉AI接管驾驶舱
深度学习·算法·计算机视觉
C雨后彩虹1 天前
事件推送问题
java·数据结构·算法·华为·面试
明洞日记1 天前
【设计模式手册018】访问者模式 - 分离数据结构与操作
数据结构·设计模式·访问者模式