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

Alt

🏝️专栏: 【数据结构实战篇】
🌅主页: f狐o狸x


目录

        一、快速排序优化

        1.1 选取关键字

        1.2 小区间优化

        1.3 快速排序非递归实现

二、归并排序

        2.1 归并排序的思想

        2.2 归并排序代码实现

        2.3 归并排序非递归实现


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

        一、快速排序优化

        1.1 选取关键字

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

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

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 小区间优化

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

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 快速排序非递归实现

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

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 归并排序代码实现

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 归并排序非递归实现

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

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部分的学习~

        感谢各位的陪伴,记得留个三连哟

评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值