文章目录
前言一、归并排序介绍(代码在大标题二)1.1 思路1.2 基本流程
二、代码总结
前言
本文继续排序算法的回顾。本文主要是针对归并排序进行分析,分别从递归的角度和分治的角度对归并进行认识,并附上c++的实现
一、归并排序介绍(代码在大标题二)
同样,这里我们以一个乱序数组为例。有数组 a[5]
a[0]a[1]a[2]a[3]a[4]
32710
len = 5 startIndex = 0 endIndex = n - 1 = 5 - 1 = 4
1.1 思路
归并排序的中心思路可以概括为从局部有序到整体有序。具体来说,就是可以将原数组进行分割(分治),分别进行运算(递归),实现局部有序,然后通过一个外排进而实现整体有序。
1.2 基本流程
结合a[5],就是先将数组切成两半 然后进行局部的排序,并借助两个指针实现外排(借助辅助数组,a和b中的较小值填入辅助数组,并且右移,直到其中一个指针遍历了其所在的子数组的全部元素,结束并将另外的数组的剩余元素全部拷贝到辅助数组),进而实现整体有序。(图片这里仅展示了第一次分治的过程,实际上伴随着递归,子数组也会被再一次分治,外排,直到无法进行分割,直观来说,就是辅助数组被构建了logN次) 最后将辅助数组数据拷贝到原数组即可。
时间复杂度分析:
子数组元素的数量是原数组元素的数量的一半 T(N) = 2T(N/2)此外,外派的复杂度为O(N),故有 T(N) = 2T(N/2) + O(N)根据Master公式有 log(b, a) > d 复杂度为O(N^log(b, a) ) log(b, a) = d 复杂度为O((N^d) * logN) log(b, a) < d 复杂度为O(N^d ) 所以有归并的时间复杂度为:O(N*logN)
二、代码
#include <iostream>
#include <vector>
using namespace std
;
void Merge(vector
<int> &v
, int start
, int mid
, int end
)
{
vector
<int> temp(end
- start
+ 1, 0);
int i
= 0;
int p1
= start
;
int p2
= mid
+ 1;
while (p1
<= mid
&& p2
<= end
)
{
temp
[i
++] = v
[p1
] < v
[p2
] ? v
[p1
++] : v
[p2
++];
}
while (p1
<= mid
)
{
temp
[i
++] = v
[p1
++];
}
while (p2
<= end
)
{
temp
[i
++] = v
[p2
++];
}
for (i
= 0; i
< temp
.size(); i
++)
{
v
[start
+ i
] = temp
[i
];
}
}
void SortProcess(vector
<int> &v
, int start
, int end
)
{
if (start
== end
)
{
return;
}
int mid
= start
+ ((end
- start
) >> 1);
SortProcess(v
, start
, mid
);
SortProcess(v
, mid
+ 1, end
);
Merge(v
, start
, mid
, end
);
}
void MergeSort(vector
<int> &v
)
{
if (v
.size() < 2)
{
return;
}
SortProcess(v
, 0, v
.size()-1);
}
int main()
{
vector
<int> v
= { 3,2,7,1,0 };
for (int i
= 0; i
< v
.size(); i
++)
{
cout
<< v
[i
] << "\t";
}
cout
<< endl
;
MergeSort(v
);
for (int i
= 0; i
< v
.size(); i
++)
{
cout
<< v
[i
] << "\t";
}
cout
<< endl
;
system("pause");
return 0;
}
总结
笔者在回顾的时候,这段编码还是错误挺多次的,主要问题出现在Merge函数部分,即辅助数组构建时候出了问题,究其原因,主要是对递归的理解不到位。总结来讲,归并排序的各个阶段不难理解,但是当这些内容串联起来是容易犯一些错误。