归并排序
归并排序: 归并排序采用的是分治算法的思想,先把这个要排序的数组划分为两份,然后再把子数组再次二分,直到数组的大小为1;然后从低端开始向上合并,合并的过程对每一个子数组进行排序。可以通过下面的分解图观察归并算法的流程
归并排序是一种稳定的排序算法,相对其他排序算法而言,是一种速度比较快的算法,时间复杂度为
O
(
n
×
l
o
g
(
n
)
)
O(n \times log(n))
O(n×log(n)),但是需要损耗空间,其空间复杂度为
O
(
n
)
O(n)
O(n) ,即需要一个额外的数组进行对子数组进行排序;
对于子数组排序的过程可以在这里描述一下:如上图子数组 arr1 = {4,5,7,8}
,子数组 arr2 = {1,2,3,6}
,需要合并成arr,这个过程我们需要定义一个辅助数组help, 空间大小为 arr1.size() + arr2.size()
。从下面的动画中可以更清楚的看明白合并和排序的过程,定义两个指针分别指向arr1和arr2,不断的比较,每次比较小的数放进help数组中。
根据上面的动图对应的合并代码如下
void merge(vector<int>& vec, int left, int mid, int right) {
vector<int> helper(right - left + 1, 0); // 定义辅助数组
// 定义两个指针,分别指向第一个子数组和第二个子数组的开始(这里mid+1就相等于第二个子数组开始)
int p1 = left;
int p2 = mid + 1;
int index = 0; // 定义辅助数组移动的下标
// 向helper中添加数字,任何一个子数组指向末尾的时候结束此while
while (p1 <= mid && p2 <= right) {
helper[index++] = (vec[p1] < vec[p2] ? vec[p1++] : vec[p2++]);
}
// 这里的两个while是为了把剩下的一个子数组中的数字添加到helper中
while (p1 <= mid) {
helper[index++] = vec[p1++];
}
while (p2 <= right) {
helper[index++] = vec[p2++];
}
// 把辅助数组中的数更新到目标数组中,这里需要主要目标数组更新的位置是left开始不是0开始。
for (int i = 0; i < helper.size(); i++) {
vec[left + i] = helper[i];
}
}
然后需要定义一个排序函数去不断的产生子数组,并合并(也就是定义一个递归的函数),代码如下:
void mergeSort(vector<int>& vec, int left, int right) {
if(left >= right){
return ;
}
int mid = left + ((right - left) >> 1); // 计算中间位置
mergeSort(vec, left, mid); // 左侧子数组
mergeSort(vec, mid + 1, right); // 右侧子数组
merge(vec, left, mid, right); // 合并子数组
}
这里定还需要定义一个函数来调用递归函数,定义一个名字同样为mergeSort的函数,把它重载一下
void mergeSort(vector<int>& vec) {
if (vec.size() < 2) { // 如果元素的个数小于2个,就表示不需要排序,直接返回
return;
}
mergeSort(vec, 0, vec.size() - 1); // 排序
}
动图展示归并算法
整体代码
#include <iostream>
#include <vector>
using namespace std;
void merge(vector<int>& vec, int left, int mid, int right) {
vector<int> helper(right - left + 1, 0);
int p1 = left;
int p2 = mid + 1;
int index = 0;
while (p1 <= mid && p2 <= right) {
helper[index++] = (vec[p1] < vec[p2] ? vec[p1++] : vec[p2++]);
}
while (p1 <= mid) {
helper[index++] = vec[p1++];
}
while (p2 <= right) {
helper[index++] = vec[p2++];
}
for (int i = 0; i < helper.size(); i++) {
vec[left + i] = helper[i];
}
}
void mergeSort(vector<int>& vec, int left, int right) {
if (left >= right) {
return;
}
int mid = left + ((right - left) >> 1);
mergeSort(vec, left, mid);
mergeSort(vec, mid + 1, right);
merge(vec, left, mid, right);
}
void mergeSort(vector<int>& vec) {
if (vec.size() < 2) {
return;
}
mergeSort(vec, 0, vec.size() - 1);
}
int main() {
vector<int> arr = { 1,4,32,23,4,6,7,1,3,9,7,0,-1 };
mergeSort(arr);
for (int i = 0; i < arr.size(); i++) {
cout << arr[i] << endl;
}
return 0;
}
总结:
- 归并排序是一种稳定的排序算法
- 时间复杂度为 O ( n × l o g ( n ) ) O(n \times log(n)) O(n×log(n))
- 空间复杂度 O ( n ) O(n) O(n)
欢迎大家关注我的个人公众号,同样的也是和该博客账号一样,专注分享技术问题,我们一起学习进步