目录
归并排序
基本思路
归并排序是采用分治法的典型应用,通过将无序数组分割并按原路一步步合并的过程最终将无序数组合并为有序数组
1、将无序数组分割成每个区间只有单个数据的小区间
2、将分割好的数据依照原路归并到一起,使之有序,最终归并成一个有序数组
代码演示
例题 取自个人作业【模板题】归并排序
代码为个人提交作业所写,仅供参考
#include <iostream>
using namespace std;
void merge(int sz[], int tempsz[], int left, int right);
void merge_sort(int sz[], int tempsz[], int left, int mid, int right);
void merge_join(int sz[], int left, int right) {
int tempsz[right];
merge(sz, tempsz, left, right - 1);
}
//进入程序,创建一个临时数组用于存储合并数据并最终复制回原始数组
void merge(int sz[], int tempsz[], int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
merge(sz, tempsz, left, mid);
merge(sz, tempsz, mid + 1, right);
merge_sort(sz, tempsz, left, mid, right);
}
}
//归并排序,用于分割和归并数组
void merge_sort(int sz[], int tempsz[], int left, int mid, int right) {
int l = left;
int r = mid + 1;
int p = left;
while (l <= mid && r <= right) {
if (sz[l] < sz[r]) {
tempsz[p] = sz[l];
l++;
p++;
} else {
tempsz[p] = sz[r];
r++;
p++;
}
}
while (l <= mid) {
tempsz[p] = sz[l];
p++;
l++;
}
while (r <= right) {
tempsz[p] = sz[r];
r++;
p++;
}
for (int i = left; i <= right; i++) {
sz[i] = tempsz[i];
}
}
//合并函数。将分割好的数据进行有序合并
int main() {
int n;
cin >> n;
int sz[n];
for (int i = 0; i < n; i++) {
cin >> sz[i];
}
merge_join(sz, 0, n);
for (int i = 0; i < n; i++) {
cout << sz[i];
if (i != n - 1) {
cout << " ";
}
}
//显示排好序的数组
return 0;
}
对代码中部分函数以及递归的具体理解
上述代码使用两段函数对数组进行分割归
其中数组首先进入merge函数
void merge(int sz[], int tempsz[], int left, int right) {
if (left < right) {
int mid = (left + right) / 2;
merge(sz, tempsz, left, mid);
merge(sz, tempsz, mid + 1, right);
merge_sort(sz, tempsz, left, mid, right);
}
}
通过递归将数组分为无法继续分割的单个数据
mid = (left+right)/2是思路图片中的分割点,将数组下标mid往左和mid+1往右进行分割
通过函数中再次使用
merge(sz, tempsz, left, mid);
merge(sz, tempsz, mid + 1, right);
进行递归将数组分割
递归理解:
拿思路数组进行举例
1 8 15 4 128 9 10 16 5
因为代码为从上至下依次运行,所以在数组进入后,首先会不断进入merge,即分割每一个左半边数组1、8、15、4、28 → 1、8、15 → 1、8 (此步mid为0,下一个函数分别为left=0,mid=0和mid+1=1,rigfht=1,left==right,merge函数不进行任何操作)
而1、8归并有序后回到上一层与15归并为1、8、15,此时该层仅执行至第四行
merge(sz, tempsz, left, mid);
进入下一步继续执行至merge(sz, tempsz, mid + 1, right);将1、8、15、4、128的右半段进行归并排序,并最后进入merge_sort进行合并为1、4、8、15、128。
所以,merge函数内部的merge_sort进入时,该层的下一层的两边数组已经完成了归并排序
再通过merge_sort函数进行合并
合并函数:
void merge_sort(int sz[], int tempsz[], int left, int mid, int right) {
int l = left;
int r = mid + 1;
int p = left;
while (l <= mid && r <= right) {
if (sz[l] < sz[r]) {
tempsz[p] = sz[l];
l++;
p++;
} else {
tempsz[p] = sz[r];
r++;
p++;
}
}
while (l <= mid) {
tempsz[p] = sz[l];
p++;
l++;
}
while (r <= right) {
tempsz[p] = sz[r];
r++;
p++;
}
for (int i = left; i <= right; i++) {
sz[i] = tempsz[i];
}
}
根据上面的递归我们知道,每一次进入合并函数时,数组left到mid和数组mid+1到right是两个归并好的有序数组(或是最终的两个单个数据,单个数据视为只有一个数据的有序数组),merge_sort即是对两个有序数组再次进行合并并使之有序。
为了使两个数组合并后有序,我们使用l,r两个下标将数组从小到大依次放入临时数组中
l和r依次设置为两个数组中的最小下标,因为两个数组皆为从小到大的有序数组,所以sz[l]和sz[r]皆为两个数组中的最小数据,将两者进行比较,较小值即为所有数据中的最小值,再将该数据放入临时数组的第一位,排除不看,将剩下未排序的两个数组中的最小值进行比较,放入临时数组的下一位,并以此类推。
代码中,tempsz为临时数组,用p和每一段代码中的p++将数据依次放入临时数组
l与r为两段数组的下标,在左边数组放入一个数据后进行l++将下标移入下一个该数组未合并的最小数据中,右半边r同理
在左右比较完后,当左右数组有一方已经全部放置完毕,易知另一方数组剩余数据全部大于已放置数据并且有序,所以将剩余数据依次放入临时数组即可
while (l <= mid) {
tempsz[p] = sz[l];
p++;
l++;
}
while (r <= right) {
tempsz[p] = sz[r];
r++;
p++;
}
最后将临时数组中的归并完成的数据复制回原数组,即可完成本轮归并。
总结
归并排序是采取分治法,将数据依次拆分成更小的数组,再按照原路依次合并的方法
时间复杂度O(nlogn),是一种稳定的排序算法。
学习归并排序以掌握思路为主,
对学习,理解分治思想、递归、基础算法有很大帮助。