【分治】合并排序、自然合并排序(C++)

一、合并排序(归并排序)概念 / 思想

定义:(引用自"百度百科")

  • 合并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
  • 合并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
  • 将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。合并排序也叫归并排序。

算法步骤:(引用自"菜鸟教程")

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置;
  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
  4. 重复步骤 3 直到某一指针达到序列尾;
  5. 将另一序列剩下的所有元素直接复制到合并序列尾。

示例图:
图-归并排序示例图

二、分治算法实现

1. 设计递归方程

  1. 我们需要将序列拆分,将当前区间一分为二,即求中间点;

  2. 递归地对两个子区间 L[left…mid] 和 R[mid+1…right] 进行归并排序(即再次执行第一步);

  3. 当拆分后的子序列只含有一个元素时,将已排序的两个子区间 L[left…mid] 和 R[mid+1…right] 合并为一个有序的区间[left…right]。

2. 编写程序代码

// 合并排序
#include<iostream>

using namespace std;

void mergeSort(int array[], int left, int right);
void merge(int array[], int left, int mid, int right);

int main()
{
	// 待排序数组,也可进行改写,获取键盘输入进行排序
	int array[] = { 10,5,8,6,3,9,4,1,2,7 };
	int len = sizeof(array) / sizeof(int); // 数组长度
	
	mergeSort(array, 0, len-1);
	cout << "合并排序:";
	for (int i = 0;i < len;i++)
		cout << array[i] << " ";
	cout << endl;
	
	return 0;
}

void mergeSort(int array[], int left, int right)
{
	// 当子序列就只有一个元素的时候结束递归调用
	if (left == right)
		return;
	else {
		// 分治
		// 判断中间位置,进行拆分
		int mid = (left + right) / 2;
		mergeSort(array, left, mid);
		mergeSort(array, mid + 1, right);
		// 合并
		merge(array, left, mid, right);
	}
}

void merge(int array[], int left, int mid, int right)
{
	// 声明一个指针,指向临时数组,临时存放排序后的元素
	int* tempArray = new int[right - left + 1];

	int left_1 = left;		// 指向 左待排序区域 第一个元素
	int left_2 = mid + 1;	// 指向 右待排序区域 第一个元素
	int k = 0;				// 指向 临时数组 第一个元素

	// 顺序选取两个待排序区的较小元素,存储到tempArr数组中
	while (left_1 <= mid && left_2 <= right) {
		// 将较小的元素存入tempArr数组中,并将指针递增
		if (array[left_1] <= array[left_2])
			tempArray[k++] = array[left_1++];
		else
			tempArray[k++] = array[left_2++];
	}
	// 若比较完之后,有序区仍有剩余元素,则直接复制到tempArray数组中
	while (left_1 <= mid)
		tempArray[k++] = array[left_1++];
	while (left_2 <= right)
		tempArray[k++] = array[left_2++];

	// 将临时数组中排序后的元素取出
	for (int i = left, j = 0;i <= right;i++,j++)
		array[i] = tempArray[j];

	// 删除指针
	delete[] tempArray;
}

3. 运行结果展示

运行截图1

4. 程序改进

程序只是举例排序{ 10,5,8,6,3,9,4,1,2,7 },
可以使用cin获取用户输入替代此数组,对用户输入的数据进行排序。

三、自然合并排序

1. 概念 / 基本思想

  • 自然合并排序是合并排序算法的一种改进;

  • 对于初始给定的数组,通常存在多个长度大于1的已排好序的子数组段。因此用一次线性扫描就可以找出所有这些排好序的子数组段,然后将相邻的排好序的子数组段两两合并。

  • 注:通常情况下, 按此方式进行合并排序所需的合并次数较少。

  • 举个栗子

    • 数组a中元素为{2, 5, 8, 3, 6, 10, 4, 9};
    • 用一次对数组a的线性扫描,找出自然排好序的子数组段:{2, 5, 8},{3, 6},{10},{4, 9};
    • 然后将相邻的排好序的子数组段两两合并:{2, 3, 5, 6, 8},{4, 9, 10};
    • 继续合并直至整个数组排好序{2, 3, 4, 5, 6, 8, 9, 10}。

2. 编写程序代码

// 待更新。。。

3. 运行结果展示

运行截图(待更新。。。)

三、友情链接~


最后,非常欢迎大家来讨论指正哦!

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值