分治排序的特点在于每次都将要排序的对象分成两部分,再考虑将这两部分进行排序,排序过程是自下而上的,即先排序分到最后只剩单个元素的两个对象,再逐渐向上,一旦理解了这一点,就可以写出如下的分治排序过程伪代码:
//MERGE-SORT
merge_sort(A,p,q)//p和q分别指A的下限和上限取值
{
if (q>p)//说明分治没有到最底层
{
r = (q+p)/2;
merge_sort(A,p,r);
merge_sort(A,r+1,q);
merge(A,p,q,r);//这里的第一层是指,假设p~r与r+1~q都排序好的情况下,对这两大块进行排序。实际运行中,第一个运行该函数的是最底层的单独两个元素,单个元素不存在排序好的情况,如此往上迭代
}
}
在这里本人纠结了很久merge_sort到底是什么含义的函数,其实实际上不用想那么多,它就相当于是一个方便我们将其拆分为两个部分,并且不断套娃的工具而已w。
在最后分解为只剩单个元素了,merge_sort函数停止套娃,因为if语句判断为false,因此不执行任何操作。此时看向上一级,需要执行merge函数,merge函数是用来将这两部分进行排序,如此往上。
以下是merge函数的伪代码:
merge(A,p,q,r)
{
定义两个与A相同类型的数组/向量 M,N;
将A下标为p~r的元素复制到M
将A下标为r+1~q的元素复制到N
//注意,M、N的实际大小要比上述大1,最后用来存储极限值(INT_MAX),称为哨兵牌
int m = 0, n =0;
for (int x = p ; x < q + 1 ; ++ x )
if (M[m] <= N[n])
{
A[x]=M[m];
++m;
}
else
{
A[x] = N[n];
++n;
}
//因为一共有p-q+1个数字要进行排序,因此进行这么多次for循环后,所有数字可以排序完毕
}
至此已经完成算法的逻辑描述。下面是应用案例:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
//MERGE-SORT 分治排序
//merge_sort函数负责将一个数组/向量分割成两部分,merge函数负责将两个数组/向量整合
//p和q指A的上下限,p为下限,q为上限
void merge(vector<int>& A, int p, int q, int r)
{
vector<int> M(r - p + 1 + 1);
int m=0, n = 0;
for (int i = p; i < r + 1; ++i)
{
M[m] = A[i];
++m;
}
M[r - p + 1] = INT_MAX;
vector<int> N(q - r + 1);
for (int i = r + 1; i < q + 1; ++i)
{
N[n] = A[i];
++n;
}
N[q - r] = INT_MAX;
int m_2 = 0, n_2 = 0;
for (int i = p; i < q + 1; ++i)
{
if (M[m_2] <= N[n_2])
{
A[i] = M[m_2];
++m_2;
}
else
{
A[i] = N[n_2];
++n_2;
}
}
}
void merge_sort(vector<int> &A,int p, int q)
{
if (q > p)
{
int r = (p + q) / 2;
merge_sort(A, p, r);
merge_sort(A, r + 1, p);
//对A中,p~r和r+1~q两大块进行排序,默认的条件是这两大块都已经排好了顺序,在程序中,这一行是最后一次执行的merge。
//当A向量分解到了最后一层,即只有一个元素,这个时候就是2个独立的元素合成一个包含2个元素的向量,如此向上——4个、8个...
merge(A, p, q, r);
}
}
int main()
{
vector<int> vector_test;
int num_of_vec = 0,input = 0;
cout << "输入vector的大小" << endl;
cin >> num_of_vec;
cout << "依次输入vector的每个值" << endl;
//也可以根据num_of_vec创造这个大小的vector
for (int size = 0; size < num_of_vec; ++size)
{
cin >> input;
vector_test.push_back(input);
}
merge_sort(vector_test, 0, num_of_vec-1);
for (int i = 0; i < num_of_vec; ++i)
{
cout << vector_test[i] << " ";
}
return 0;
}
个人想法:
1、由于对个人而言涉及到的局部变量较多,因此调试时出现了很多问题。在思考的时候要想清楚,局部变量要放在哪里合适,放在函数体内部的变量要尤其谨慎!
2、自从简单学习了vector之后感觉比数组好用多了好吧qwq,使用体验完全碾压数组哈哈哈
3、更进一步地,为了提高效率,有时候发现当有一端出现了哨兵牌(或不使用哨兵牌时发现所有数字都被拷贝走了)时,可以直接停止for循环。