【数据结构】-排序-快速排序


快速排序是Hoare于1962年提出的一种二叉树结构的交换排序方法。

基本思想

任取待排序元素序列中的某元素作为基准值,按照该排序码将待排序集合分割成两子序列,左子序列中所有元素均小于基准值,右子序列中所有元素均大于基准值,然后最左右子序列重复该过程,直到所有元素都排列在相应位置上为止。

 将区间按照基准值划分为左右两半部分的常见方式: Hoare法、挖坑法、前后指针法


1. hoare版本

动图理解:

 选择方式:

1.当选择最左做key时,右边先走。-->左右相遇时比key小。

2.当选择最右做key时,左边先走。-->左右相遇时比key大。

核心思路:

选择左边值为key,right先走,找比key小的值停下(跳过大的),left再走,找到比key大的值停下(跳过小的)。交换 left 与 right 的值,直到left,right相遇。相遇点的值和key值交换。

扫描二维码关注公众号,回复: 13758140 查看本文章


int Partion1(int* a, int left, int right)
{
	// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
	int midi = GetMidIndex(a, left, right);
	Swap(&a[midi], &a[left]);

	int keyi = left;
	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[left], &a[keyi]);

	return left;
}

注意:

 快速排序优化:三数取中法选key:三数取中位数不会使最大或最小做key

 


2.挖坑法

动图理解:

选择方式:

1.当选择最左做key时,右边先走。

2.当选择最右做key时,左边先走。

核心思路:

右边找小,把比key小的值放进坑里,右边形成新的坑;左边找大扔到坑里,左边形成新的坑。

// 挖坑法
int Partion2(int* a, int left, int right)
{
	// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
	int midi = GetMidIndex(a, left, right);
	Swap(&a[midi], &a[left]);

	int key = a[left];
	//坑
	int pivot = left;
	while (left < right)
	{
		// 右边找小, 放到左边的坑里面
		while (left < right && a[right] >= key)
		{
			--right;
		}

		a[pivot] = a[right];
		pivot = right;

		// 左边找大,放到右边的坑里面
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[pivot] = a[left];
		pivot = left;
	}

	a[pivot] = key;
	return pivot;
}

注意:

先往坑里填值,再改变坑的位置。

 


3. 前后指针法:

动图理解:

 选择方式:

核心思路:

cur找到比key小的就停下来,++prev,再交换prev和cur位置的值。

prev要么紧跟着cur,要么紧跟着比key大的序列。

// 推荐掌握这个 -- 思想三种大家都要掌握
int Partion3(int* a, int left, int right)
{
	// 三数取中 -- 面对有序最坏情况,变成选中位数做key,变成最好情况
	int midi = GetMidIndex(a, left, right);
	Swap(&a[midi], &a[left]);

	int keyi = left;
	int prev = left;
	int cur = prev + 1;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && ++prev != cur)
		{
			Swap(&a[cur], &a[prev]);
		}

		++cur;
	}

	Swap(&a[prev], &a[keyi]);
	return prev;
}

快速排序优化
1. 三数取中法选key
2. 递归到小的子区间时,可以考虑使用插入排序

void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;

	// 小区间优化,当分割到小区间时,不再用递归分割思路让这段子区间有序
	// 对于递归快排,减少递归次数
	if (right - left + 1 < 10)
	{
		InsertSort(a + left, right - left + 1);
	}
	else
	{
		//类似二叉树遍历,根左右
		int keyi = Partion(a, left, right);
		// [left, keyi-1] keyi [keyi+1, right]
		QuickSort(a, left, keyi - 1);
		QuickSort(a, keyi + 1, right);
	}
}

非递归方法:

利用栈的结构处理区间。

// 递归深度太深的程序,只能考虑改非递归
void QuickSortNonR(int* a, int left, int right)
{
	ST st;
	StackInit(&st);
	StackPush(&st, left);
	StackPush(&st, right);

	while (!StackEmpty(&st))
	{
		int end = StackTop(&st);
		StackPop(&st);

		int begin = StackTop(&st);
		StackPop(&st);

		int keyi = Partion3(a, begin, end);
		// [begin, keyi-1] keyi [keyi+1, end]
		if (keyi + 1 < end)
		{
			StackPush(&st, keyi + 1);
			StackPush(&st, end);
		}

		if (begin < keyi - 1)
		{
			StackPush(&st, begin);
			StackPush(&st, keyi - 1);
		}
	}

	StackDestroy(&st);
}

猜你喜欢

转载自blog.csdn.net/m0_51866180/article/details/121985605