22计算机408考研—数据结构—二路归并排序

193 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

2022计算机考研408—数据结构—排序 手把手教学考研大纲范围内的排序 22考研大纲数据结构要求的是C/C++,笔者以前使用的都是Java,对于C++还很欠缺, 如有什么建议或者不足欢迎大佬评论区或者私信指出

Talk is cheap. Show me the code. 理论到处都有,代码加例题自己练习才能真的学会

二路归并排序

思路二路归并,就是把一个数组,先分开,然后在合并的过程中逐步排序 二路就是分开的时候每次分成两个,分成两种路 归并就是把他分开在合上

二路分割数组,一直把数组分割到一路只有一个数为止 然后再把他们两路合一路,两路合一路,在合路的过程中把无序的路合成有序的路,慢慢的合成后的路都为有序的路,大致原理如下图

在这里插入图片描述

分路没有什么说的,一分为,对半分即可 合路的时候,AB两个路合成C路,分别两个指针i,j,k对应着A,B,C数组的下标 循环AB两个数组,每次都比较A[i]与B[j] 取两个数组中对应值小的放到C[k] 然后k下标右移,i或者j小的下标右移,完全循环后,C==A+B的有序路 在这里插入图片描述

#include <iostream>
#include <vector>

using namespace std;

void mergeSort(vector<int> &num, int left, int right);

int main() {
    int n;    //n为将要输入的数组长度
    cin >> n;   //输入n   cin方法需要上面使用std
    vector<int> num;    //定义vector  记得上面导入vector
    int temp;   //temp为输入vector时的中间变量
    for (int i = 0; i < n; i++) {
        cin >> temp;            //输入
        num.push_back(temp);
    }
    mergeSort(num, 0, num.size() - 1);    //调用自定义的排序方法
    cout << "\n\n排序后" << endl;
    for (int i = 0; i < num.size(); i++) {
        cout << num[i] << " ";     //输出
    }
    return 0;
}

void mergeSort(vector<int> &num, int left, int right) {
    if (left == right) return;      //如果左节点和右节点相等,说明就分到底了,一路只剩下一个元素
    int m = (left + right) / 2;     //左节点和右节点的中间节点,从中间节点分路,左面一路,右面一路
    mergeSort(num, left, m);            //先分左面->左面进入后还会从上面再来,接着分,一直分到一路只有一个元素
    mergeSort(num, m + 1, right);   //再分右面
    //走到这里说明已经分完了,接下来就是合并
    int temp [right - left + 1];    //定义一个数组,用来存两路合并后的有序数组
    int l = left;                   //左路起始点下标
    int r = m + 1;                  //右路起始点下标
    int k = 0;                      //合路的起始点下标
    while (l <= m && r <= right) {  //每次放到时候比较对应的值,小的值放到前面,l要在左路的范围内,r要在右路的范围内
        if (num[l] <= num[r]) {     //谁小把谁放到合路
            temp[k++] = num[l++];
        } else {
            temp[k++] = num[r++];
        }
    }
    //下面两个while循环处理的是一路全部放进合路了,另一路没有完全放进,循环的作用就是把两路上面没放进去的全部放进去
    while (l <= m) {
        temp[k++] = num[l++];
    }
    while (r <= right) {
        temp[k++] = num[r++];
    }
    //temp就是我们合路,然后我们把合好的路放回到num原数组中
    for (int i = left, k = 0; i <= right; i++, k++) {
        num[i] = temp[k];
    }

    //每次循环都把数组的变动输出出来
    cout << "此次合并的是下标:" << left << "-" << right << "\n";
    for (int j = 0; j < num.size(); j++) {
        cout << num[j] << " ";
    }
    cout << "\n";
}

在这里插入图片描述

Acwing,这个题就是归并排序
788. 逆序对的数量
  

给定一个长度为 n 的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i<j 且 a[i]>a[j],则其为一个逆序对;否则不是。

输入格式
第一行包含整数 n,表示数列的长度。

第二行包含 n 个整数,表示整个数列。

输出格式
输出一个整数,表示逆序对的个数。

数据范围
1≤n≤100000,
数列中的元素的取值范围 [1,109]。

输入样例:
6
2 3 4 5 6 1
输出样例:
5





#include <iostream>
#include <vector>

using namespace std;

void mergeSort(vector<int> &num, int left, int right);

int res = 0;
int main() {
    int n;    //n为将要输入的数组长度
    cin >> n;   //输入n   cin方法需要上面使用std
    vector<int> num;    //定义vector  记得上面导入vector
    int temp;   //temp为输入vector时的中间变量
    for (int i = 0; i < n; i++) {
        cin >> temp;            //输入
        num.push_back(temp);
    }
    mergeSort(num, 0, num.size() - 1);    //调用自定义的排序方法
     cout << res;
    return 0;
}

void mergeSort(vector<int> &num, int left, int right) {
    if (left == right) return;      //如果左节点和右节点相等,说明就分到底了,一路只剩下一个元素
    int m = (left + right) / 2;     //左节点和右节点的中间节点,从中间节点分路,左面一路,右面一路
    mergeSort(num, left, m);            //先分左面->左面进入后还会从上面再来,接着分,一直分到一路只有一个元素
    mergeSort(num, m + 1, right);   //再分右面
    //走到这里说明已经分完了,接下来就是合并
    int temp [right - left + 1];    //定义一个数组,用来存两路合并后的有序数组
    int l = left;                   //左路起始点下标
    int r = m + 1;                  //右路起始点下标
    int k = 0;                      //合路的起始点下标
    while (l <= m && r <= right) {  //每次放到时候比较对应的值,小的值放到前面,l要在左路的范围内,r要在右路的范围内
        if (num[l] <= num[r]) {     //谁小把谁放到合路
            temp[k++] = num[l++];
        } else {
            temp[k++] = num[r++];
            res += m - l + 1;   //当前l大于r证明l到m的数都大于r
                                //归并就是一段一段的排好序的数组合并起来的
        }
    }
    //下面两个while循环处理的是一路全部放进合路了,另一路没有完全放进,循环的作用就是把两路上面没放进去的全部放进去
    while (l <= m) {
        temp[k++] = num[l++];
    }
    while (r <= right) {
        temp[k++] = num[r++];
    }
    //temp就是我们合路,然后我们把合好的路放回到num原数组中
    for (int i = left, k = 0; i <= right; i++, k++) {
        num[i] = temp[k];
    }

}