数据结构—排序算法篇二

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

快排(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)。

相关推荐
ShineWinsu2 小时前
对于数据结构:堆的超详细保姆级解析—上
数据结构·c++·算法·计算机·二叉树·顺序表·
im_AMBER3 小时前
Leetcode 46
c语言·c++·笔记·学习·算法·leetcode
努力学算法的蒟蒻3 小时前
day09(11.6)——leetcode面试经典150
算法·leetcode·职场和发展
2301_796512523 小时前
Rust编程学习 - 内存分配机制,如何动态大小类型和 `Sized` trait
学习·算法·rust
卿言卿语4 小时前
CC23-最长的连续元素序列长度
java·算法·哈希算法
天选之女wow5 小时前
【代码随想录算法训练营——Day60】图论——94.城市间货物运输I、95.城市间货物运输II、96.城市间货物运输III
android·算法·图论
Blossom.1185 小时前
大模型在边缘计算中的部署挑战与优化策略
人工智能·python·算法·机器学习·边缘计算·pygame·tornado
时间醉酒5 小时前
数据结构:双向链表-从原理到实战完整指南
c语言·数据结构·算法
京东零售技术5 小时前
当搜索遇见 AIGC:京东零售的“千人千面”素材生成实践
算法