首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【数据结构实战】一起探索快速排序和归并排序的奥秘

【数据结构实战】一起探索快速排序和归并排序的奥秘

作者头像
f狐o狸x
发布2024-12-24 09:14:52
发布2024-12-24 09:14:52
11900
代码可运行
举报
运行总次数:0
代码可运行

上一篇我们讲了各大排序之间的差距,以及他们实现的思路和代码,本期我们将详细的研究一下快速排序和归并排序的奥秘

一、快速排序优化

1.1 选取关键字

上一期我们直到,快速排序的主要逻辑是先将一个关键字排到合适的位置上,在通过递归依次排后面的数字

但是如果这串数字本来就接近于有序了或者他本来就是有序的,我们选的关键字都是最左边的那个,那么快排的优势有没有了,他甚至没有插入排序快,因此我们可以在选择关键字的时候先比较一下最左边、最右边和中间的,选出他们中间的那个数值作为关键字,就能让快速排序的效率条一些

代码语言:javascript
代码运行次数:0
运行
复制
int FindMid(int a, int b, int c)
{
	if (a > b)
	{
		if (b > c)
			return b;
		else
			if (a > c)
				return c;
			else
				return a;
	}
	else //a < b
	{
		if (a > c)
			return a;
		else
			if (b > c)
				return c;
			else
				return b;
	}
}

int PartSort1(int* a, int left, int right)
{
	int mid = FindMid(a[left], a[right], a[(left + right) / 2]);
	if (mid != left)
	{
		swap(&a[mid], &a[left]);
	}

	int key = left;
	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[left]);
	return left;
}

1.2 小区间优化

在快排递归的时候,当他的区间只有五个的时候,可能还需要递归调用十次才能排序好有点大材小用了,因此我们也可以将快排区间划小之后再用插入排序搞定,这样可以减少很多递归的次数,效率也能提升很多

代码语言:javascript
代码运行次数:0
运行
复制
void QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int begin = left, end = right;
	//小区间优化
	if (end - begin + 1 <= 10)
	{
		InsertSort(a + begin, begin - end + 1);
		return;
	}

	//int mid = PartSort1(a, begin, end);
	//int mid = PartSort2(a, begin, end);
	int mid = PartSort1(a, begin, end);

	QuickSort(a, begin, mid - 1);
	QuickSort(a, mid + 1, end);
}

1.3 快速排序非递归实现

在有些情况下,我们也需要用到快速排序的非递归实现,这里我们就需要一个栈来模拟递归的情况,每出一个大区间,就入他的两个小区间,直到区间不存在

代码语言:javascript
代码运行次数:0
运行
复制
void QuickSortNonR(int* a, int left, int right)
{
	int begin = left, end = right;
	ST st;
	InitStack(&st);
	STPush(&st, end);
	STPush(&st, begin);
	while (!STEmpty(&st))
	{
		begin = STTop(&st);
		STPop(&st);
		end = STTop(&st);
		STPop(&st);
		if (begin < end)
		{
			int key = PartSort3(a, begin, end);

			STPush(&st, end);
			STPush(&st, key + 1);

			STPush(&st, key - 1);
			STPush(&st, begin);
		}
	}

	DestoryStack(&st);
}

二、归并排序

2.1 归并排序的思想

归并排序和快速排序类似,都是需要把大区间分为小区间排序,不同的是快速排序是先将数字排好再去划分小区间(先序遍历),而归并排序是需要先递归到最小区间,再依次归并上来(后序遍历)

2.2 归并排序代码实现

代码语言:javascript
代码运行次数:0
运行
复制
void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)
		return;
	int mid = (left + right) / 2;
	_MergeSort(a, left, mid, tmp);
	_MergeSort(a, mid + 1, right, tmp);

	int begin1 = left, begin2 = mid + 1;
	int end1 = mid, end2 = right;
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
		else
			tmp[i++] = a[begin2++];
	}
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	memcpy(a + left, tmp + left, sizeof(int) * (right - left + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));
	if (tmp == NULL)
	{
		perror("MergeSort::malloc");
		return;
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
}

2.3 归并排序非递归实现

上面代码是用递归实现的,就有可能存在栈溢出的情况,因此我们还需要想想如何用非递归来完成它,递归是先把大区间全部化为小区间,在依次归并上去,所以我们可以直接从小区间开始归并上去直到归并到最大的区间

代码语言:javascript
代码运行次数:0
运行
复制
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(n * sizeof(int));
	if (tmp == NULL)
	{
		perror("MergeSort::malloc");
		return;
	}
	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2*gap)
		{
			int begin1 = i, end1 = begin1 + gap - 1;
			int begin2 = begin1 + gap, end2 = begin1 + 2 * gap - 1;
			int j = i;
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int)*(end2 - i + 1));
		}

		gap *= 2;
	}
	free(tmp);
}

至此,数据结构的所有内容已全部更完啦!(鼓掌)(欢呼)(尖叫)

从下一期我们将开始c++部分和Linux部分的学习~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、快速排序优化
    • 1.1 选取关键字
    • 1.2 小区间优化
    • 1.3 快速排序非递归实现
  • 二、归并排序
    • 2.1 归并排序的思想
    • 2.2 归并排序代码实现
    • 2.3 归并排序非递归实现
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档