数据结构 - 快排 | C

目录

  • 快速排序
    • ①hoare版本
      • 思路分析
      • 代码实现
      • 时间复杂度
      • <整体优化>
      • <局部优化>
    • ②挖坑法
        • 思路分析
      • 代码实现
    • ③前后指针法
      • 思路分析
      • 代码实现
    • ④非递归快排
      • 思路分析
      • 代码实现
  • 以上代码汇总

快速排序

①hoare版本

思路分析

数据结构 - 快排 | C_第1张图片
以上图为例:
指定一个数为a[key] = 6,a[right] 从尾出发找比 6 小的数,找到后停下,接着a[left] 从头出发找比 6 大的数,找到后停下 → Swap(&a[left], &a[right]) ,最终 a[right] 和 a[left] 相遇 → Swap(&a[key], &a[right]);//left==right

  • 这样就分割出了左右区间,左区间的数都比 6 小,右区间的数都比 6 大
  • 6 已经落到正确的位置
  • 解下来就是以左右区间为新数列,重复上述操作
    在这里插入图片描述
    Swap(&a[key], &a[right]);//left==right 分析
  1. right 停住,left 往后走 与 right 相遇 ⇨ 相遇的位置是right 停住的位置 right 找到要找的数才会停下(始终在 left < right 的约束下),此时相遇点就是 right 找到的比 a[key] 小的数
  2. left 停住,right 往前走 与 left 相遇 ⇨ 相遇的位置是 left 停住的位置 right 比 left 先走,right 找到了 ,left 才会动,left停住之后,则会Swap(&a[left], &a[right]) ,Swap 之后此时a[left] < a[key] ,left 停住,right 往前走(如果一直未找到要找的数)与 left 相遇,此时相遇点就是比 a[key] 小的数

⭐左边做 key,右边先走;
⭐右边做 key,左边先走;
数据结构 - 快排 | C_第2张图片

  • 一些bug代码分析:
while (left < right)
{
	while (a[right] > a[key])
	{
		--right;
	}
	while (a[left] < a[key])
	{
		++left;
	}
	Swap(&a[left], &a[right]);
}
Swap(&a[key], &a[right]);//left==right
  1. 相等的情况:
6 6 6
a[key] a[left] a[right]

Swap之后:

6 6 6
a[key] a[left] a[right]

这样循环就会卡住

while (left < right)
{
	while (a[right] > a[key]){}//循环不进入
	while (a[left] < a[key]){}//循环不进入
	
	Swap(&a[left], &a[right]);//一直无限循环Swap
}
  1. 越界
while (left < right)
{
	while (a[right] >= a[key]){
		--right;
	}
	while (a[left] <= a[key]){
		++left;
	}
	Swap(&a[left], &a[right]);
}

right 飞出去的情况:

6 7 8 9 6 10 6 13 14
a[key]

left 飞出去的情况:

6 1 6 4 5 3 6 2 3
a[key]

因此,right 和 left 每次改变之后都需要判断是否 left < right

代码实现

int hoareSort1(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;
	int key = left, begin = left, end = right;
	while (left < right)
	{
		while (left < right && a[right] >= a[key])
		{
			--right;
		}
		while (left < right && a[left] <= a[key])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[key], &a[right]);//left==right

	//[begin,left-1] [right+1,end]
	hoareSort1(a, begin, left - 1);
	hoareSort1(a, right + 1, end);

	return right;
}

时间复杂度

  • 最好的情况(像二叉树一样):如下图 时间复杂度为O(N*logN)
    数据结构 - 快排 | C_第3张图片
  • 比较一般的情况:
    数据结构 - 快排 | C_第4张图片
  • 最坏的情况:有序
    数据结构 - 快排 | C_第5张图片
    Σ = n + (n-1) + (n-2) + …… + 2 + 1 = (n+1)*n/2
    时间复杂度为 O(N²)

<整体优化>

  • 三数取中(begin、middle、end)
    • 例如:
1 2 3 4 5 6 7 8 9
[begin] [middle] [end]

∵ 1 < 6 < 9
∴ 选择 a[middle] 作为 a[key]

// 三数取中
int GetMidIndex(int* a, int left, int right)
{
	int begin = left, end = right;
	int middle = (left + right) / 2;

	if (a[begin] < a[end])
	{
		if (a[middle] < a[begin])
			return begin;
		else if (a[middle] > a[end])
			return end;
		else
			return middle;
	}
	else
	{
		if (a[middle] < a[end])
			return end;
		else if (a[middle] > a[begin])
			return begin;
		else
			return middle;
	}

}
// 快速排序hoare版本
int hoareSort1(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;
	int key = left, begin = left, end = right;

	int mid = GetMidIndex(a, left, right);

	Swap(&a[mid], &a[key]);
	while (left < right)
	{
		while (left < right && a[right] >= a[key])
		{
			--right;
		}
		while (left < right && a[left] <= a[key])
		{
			++left;
		}
		Swap(&a[left], &a[right]);
	}
	Swap(&a[key], &a[right]);//left==right

	//[begin,left-1] [right+1,end]
	hoareSort1(a, begin, left - 1);
	hoareSort1(a, right + 1, end);

	return right;
}

int mid = GetMidIndex(a, left, right);
Swap(&a[mid], &a[key]);

  • 为什么要Swap? 如果不Swap↓
    数据结构 - 快排 | C_第6张图片
    因为,最后 a[key] 会和 left 与 right 相遇的数交换,所以 [key] 必须在头部位置。

时间复杂度分析:针对局部有序或有序的情况效率明显提升
数据结构 - 快排 | C_第7张图片
时间复杂度为 O(N) = N*logN

<局部优化>

递归到小区间的时候用插入排序
数据结构 - 快排 | C_第8张图片

int hoareSort1(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;

	if ((right - left + 1) < 5)
	{
		InsertSort(a + left, right - left + 1);
	}
	else
	{
		int key = left, begin = left, end = right;

		int mid = GetMidIndex(a, left, right);

		Swap(&a[mid], &a[key]);
		while (left < right)
		{
			while (left < right && a[right] >= a[key])
			{
				--right;
			}
			while (left < right && a[left] <= a[key])
			{
				++left;
			}
			Swap(&a[left], &a[right]);
		}
		Swap(&a[key], &a[right]);//left==right

		//[begin,left-1] [right+1,end]
		hoareSort1(a, begin, left - 1);
		hoareSort1(a, right + 1, end);
	}
	return right;
}

②挖坑法

思路分析

把选定做 key 的数存储在新变量中。left 和 right 找到 数 填坑里,原位置变新坑。
数据结构 - 快排 | C_第9张图片

代码实现

// 快速排序挖坑法
int HoleSort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	int key = a[left];
	int hole = left, begin = left, end = right;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[right] = key;

	//[begin,left-1] [right+1,end]
	HoleSort2(a, begin, left - 1);
	HoleSort2(a, right + 1, end);

	return right;
}

③前后指针法

思路分析

  1. cur 找比 key所指向 小的
  2. 找到后,++prev,交换 cur 和 prev 指向的内容
  3. cur 走到尽头后,最后交换 key 和 prev
  • 小的数往前←,大的数往后→

cur 找到 比 data 的数时的情况:
数据结构 - 快排 | C_第10张图片
循环结束的情况:
数据结构 - 快排 | C_第11张图片

  • 示例:
    数据结构 - 快排 | C_第12张图片

代码实现

// 快速排序前后指针法
int PointSort3(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;
	int keyi = left;
	int prev = left, cur = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && cur != prev)
			Swap(&a[++prev], &a[cur]);

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

	//[left,prev-1][prev+1,right]
	PointSort3(a, left, prev - 1);
	PointSort3(a, prev + 1, right);

	return prev;
}

④非递归快排

思路分析

利用数据结构栈 → 广度遍历
递归→ 深度遍历

  • 将左右区间[begin,end] 存入栈中
  • 循环:
    • 1.取出栈中的左右区间[begin,end],在这个区间内进行快排
    • 2.一次快排之后,得到两个新的左右区间[begin,end] → [begin,keyi-1] keyi [keyi+1,end],将其存入栈中(注意顺序
    • 1.取出栈中的左右区间[keyi+1,end],在在这个区间内进行快排
    • 2.一次快排之后,得到两个新的左右区间……
    • ……

代码实现

// 快速排序 非递归实现
#include "Stack.h"
void QuickSort(int* a, int left, int right)
{
	assert(a);
	Stack 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 = hoareSort1(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);
}

以上代码汇总

// 快速排序递归实现
// 三数取中
int GetMidIndex(int* a, int left, int right)
{
	int begin = left, end = right;
	int middle = (left + right) / 2;

	if (a[begin] < a[end])
	{
		if (a[middle] < a[begin])
			return begin;
		else if (a[middle] > a[end])
			return end;
		else
			return middle;
	}
	else
	{
		if (a[middle] < a[end])
			return end;
		else if (a[middle] > a[begin])
			return begin;
		else
			return middle;
	}

}
// 快速排序hoare版本
int hoareSort1(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;

	if ((right - left + 1) < 5)
	{
		InsertSort(a + left, right - left + 1);
	}
	else
	{
		int key = left, begin = left, end = right;

		int mid = GetMidIndex(a, left, right);

		Swap(&a[mid], &a[key]);
		while (left < right)
		{
			while (left < right && a[right] >= a[key])
			{
				--right;
			}
			while (left < right && a[left] <= a[key])
			{
				++left;
			}
			Swap(&a[left], &a[right]);
		}
		Swap(&a[key], &a[right]);//left==right

		//[begin,left-1] [right+1,end]
		hoareSort1(a, begin, left - 1);
		hoareSort1(a, right + 1, end);
	}
	return right;

}


// 快速排序挖坑法
int HoleSort2(int* a, int left, int right)
{
	if (left >= right)
		return;
	int key = a[left];
	int hole = left, begin = left, end = right;
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[right] = key;

	//[begin,left-1] [right+1,end]
	HoleSort2(a, begin, left - 1);
	HoleSort2(a, right + 1, end);

	return right;
}

// 快速排序前后指针法
int PointSort3(int* a, int left, int right)
{
	assert(a);
	if (left >= right)
		return;
	int keyi = left;
	int prev = left, cur = left;
	while (cur <= right)
	{
		if (a[cur] < a[keyi] && cur != prev)
			Swap(&a[++prev], &a[cur]);

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

	//[left,prev-1][prev+1,right]
	PointSort3(a, left, prev - 1);
	PointSort3(a, prev + 1, right);

	return prev;
}

// 快速排序 非递归实现
void QuickSort(int* a, int left, int right)
{
	assert(a);
	Stack 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 = hoareSort1(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);
}

你可能感兴趣的:(数据结构初阶,c语言,数据结构)