我终于弄明白什么是归并排序了

一、概念:

归并排序(Merge Sort)是建立在归并操作上的一种有效,稳定的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。-----摘自于百度百科

二、具体场景:

一看概念说的什么玩意儿,还是举一个例子比较形象:

现有一个无序数组[3,1,4,5,5,2],要将其实现有序排列。有各种排序算法可以实现,快排、冒泡、选择等等都可以,我们今天主要是讲归并排序,并好好剖析一下里面的递归思想。

归并排序是什么流程呢?首先我们选取数组的中间位置的数作为分界线,分别用递归的方式对分界线左右两边的子数组进行有序排列,然后最后将两个有序的子数组进行合并即可。过程及其容易理解。但是它的递归到底是如何实现排序的?很多人都弄不清楚。接下来我们通过一段代码来剖析这个执行过程。

三、代码实现:

# include<iostream>
#include<vector>
using namespace std;
void Sort(vector<int>&, int, int, int);
void Process(vector<int>&, int, int);
int main()
{
	//初始化data数组
	vector<int> data;
	for (int i = 1; i <= 8; i++)
		data.push_back(8 - i);
	cout << "the initial order:";
	for (int i = 0; i < 8; i++)
		cout << data[i] << " ";
	cout << endl;
	//开始运行
	cout << "strat sort..." << endl;
	Process(data, 0, 7);
	//结果输出
	cout << "the sorted order:";
	for (int i = 0; i < 8; i++)
		cout << data[i] << " ";
	cout << endl;

	system("pause");
	return 0;
}
void Process(vector<int>&data, int l, int r) { //使用递归进程对数组data从索引i到索引j之间的元素排序。
	cout << "the process of digui:" << l << "," << r << endl;
	//base case:当只有一个元素的时候,结束递归
	if (l == r) return;
	int mid = l + ((r - l) >> 1);	//取中间点
	Process(data, l, mid);//左半边递归(递)
	Process(data, mid+1, r);//右半边递归(递)
	Sort(data, l, mid, r);//合并(归)
}
void Sort(vector<int>&data, int l, int mid, int r)//将两个有序数组合并成一个有序数组,这是归并排序的前提
{
	cout << "此时调用Sort进行排序" << endl;
	vector<int> copy(r - l + 1);//定义一个和当前的data数组一样大小的拷贝数组
    cout << "此时的data数组的大小:"<<(int)copy.size() << endl;
	int cindex = 0;//指向copy数组的第一个元素
	int p1 = l;//指向左半边子数组的第一个元素
	int p2 = mid + 1;//指向右半边数组的第一个元素
	while (p1 <= mid && p2 <= r) {//越界条件
		//比较p1和p2指向元素的大小,小的放进copy数组,默认相等的时候copy左半边的数,然后相应的指针往后指一个
		copy[cindex++] = data[p1] <= data[p2] ? data[p1++] : data[p2++];
	}
	//若出现越界情况,跳出上面的while循环,但是我们不知道哪个越界,因此,我们要进行讨论,
	//因为两个指针是一次只能走一个,因此,只可能出现一个越界,先越界的说明,自己已经比完了,所以只要将未越界的后面的数
	//加到copy数组即可
	//(1)p2越界,p1没有越界:
	while (p1 <= mid) {
		copy[cindex++] = data[p1++];//将当前p1后面的数依次加入到copy
	}
	//(1)p1越界,p2没有越界:
	while (p2 <= r) {
		copy[cindex++] = data[p2++];//将当前p2后面的数依次加入到copy
	}
	//程序执行到这,copy已经是有序了,因此,我们将copy中的序列,再刷新到原来的data数组中
	for (int i = 0; i < r - l + 1; i++) {
		data[l + i] = copy[i];//这句代码至关重要,为什么data[]不从i开始????后面就知道了
	}

}

上述代码我相信大家了解归并排序的都能看懂,看注释应该也很容易读懂了。接下来有两件事情:

1.剖析程序中递归是如何进行排序的

2.为什么data[]不从i开始拷贝?

上输出:大家其实可以将代码复制到vs里进行断点调试,会更清晰,我这里直接将输出给出,然后口述一遍流程。

the initial order:7 6 5 4 3 2 1 0
strat sort...
the process of digui:0,7
the process of digui:0,3
the process of digui:0,1
the process of digui:0,0
the process of digui:1,1
此时调用Sort进行排序
此时的data数组的大小:2
the process of digui:2,3
the process of digui:2,2
the process of digui:3,3
此时调用Sort进行排序
此时的data数组的大小:2
此时调用Sort进行排序
此时的data数组的大小:4
the process of digui:4,7
the process of digui:4,5
the process of digui:4,4
the process of digui:5,5
此时调用Sort进行排序
此时的data数组的大小:2
the process of digui:6,7
the process of digui:6,6
the process of digui:7,7
此时调用Sort进行排序
此时的data数组的大小:2
此时调用Sort进行排序
此时的data数组的大小:4
此时调用Sort进行排序
此时的data数组的大小:8
the sorted order:0 1 2 3 4 5 6 7
请按任意键继续. . .

 大家将输出的语句和递归的结构图一一对应起来看会豁然开朗。我们从输出的start sorting开始,到第一次调用sort函数之前输出的分别是:

the process of digui:0,7
the process of digui:0,3
the process of digui:0,1
the process of digui:0,0
the process of digui:1,1

这段程序告诉我们什么?是不是对应于递归结构图的最左边的一条分支,就是直接递归到一个数的时候就结束,然后接下来的输出是:

此时调用Sort进行排序
此时的data数组的大小:2

说明这个时候开始"归的过程",将7和6 Sort,即[6,7],然后return到上一级。而且此时的data数组大小是2!!这个很重要。

继续:

the process of digui:2,3
the process of digui:2,2
the process of digui:3,3
此时调用Sort进行排序
此时的data数组的大小:2

这一段是到了左边的第二个分支,将5和4sort的过程。

继续:

此时调用Sort进行排序
此时的data数组的大小:4

这一段怎么跟之前不一样了?是因为这段代码是对[6,7]和[4,5]进行sort!!变成[4,5,6,7]。所以这时data数组大小为4。

这时左边的分支已经结束,右边同理。不再赘述。

然后左右两边都结束,到了最后一步,也就是最后一步整合的过程:

此时调用Sort进行排序
此时的data数组的大小:8
the sorted order:0 1 2 3 4 5 6 7

将[4,5,6,7]和[0,1,2,3]进行sort排序,此时data数组大小是8。

所以可以看出每一次的data数组是不一样的,但是每一次它的开始都是从l开始,而copy永远是从i=0开始,

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值