归并排序(merge sort)

组别:提高级
难度:5

洛谷:P2804, P1309

归并排序是基于分治算法的一种排序,我们把一个问题分解成子问题,每个子问题再分别解决,最后再把子问题的解合并成最终结果。

如下图数列{6,5,12,10,9,1},首先将数列分成两分{6,5,12}和{10,9,1},再把这俩个子数列继续分解成{6},{5,12},{10},{9,1} ,当就子数列就剩两个元素时比较大小,现把子数列逐级合并成一个有序数列。

Merge Sort (With Code)

代码实现:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
/**************************************************************** 
 * Description: 用C++实现归并算法,Merge sort in C++
 * Author: Alex Li
 * Date: 2022-02-10 19:55:32
 * LastEditTime: 2024-12-02 17:50:17
****************************************************************/
#include <iostream>
#include <vector>
using namespace std;
int arr[100];
// 合并两个子数组Left_arr和Right_arr到主数组中
void merge(int arr[], int left, int middle, int right) {
    // 确定左右子数组的大小
    int left_size = middle - left + 1;
    int right_size = right - middle;

    // 临时数组用于存储左右子数组的值
    vector<int> left_array(left_size);
    vector<int> right_array(right_size);

    // 将数据从主数组复制到子数组中
    for (int i = 0; i < left_size; i++) {
        left_array[i] = arr[left + i];
    }
    for (int j = 0; j < right_size; j++) {
        right_array[j] = arr[middle + 1 + j];
    }

    // 维护子数组和主数组的索引
    int left_index = 0, right_index = 0;
    int merged_index = left;

    // 按顺序将两个子数组中的元素合并到主数组中
    while (left_index < left_size && right_index < right_size) {
        if (left_array[left_index] <= right_array[right_index]) {
            arr[merged_index] = left_array[left_index];
            left_index++;
        } else {
            arr[merged_index] = right_array[right_index];
            right_index++;
        }
        merged_index++;
    }

    // 将剩余的元素合并到主数组中
    while (left_index < left_size) {
        arr[merged_index] = left_array[left_index];
        left_index++;
        merged_index++;
    }

    while (right_index < right_size) {
        arr[merged_index] = right_array[right_index];
        right_index++;
        merged_index++;
    }
}

// 递归地将主数组分解成两个子数组,再合并子数组
void mergeSort(int arr[], int begin, int end) {
    if (begin < end) {
        // middle是把主数组分成两个子数组的分界点
        int middle = begin + (end - begin) / 2;

        // 对左半部分进行排序
        mergeSort(arr, begin, middle);
        // 对右半部分进行排序
        mergeSort(arr, middle + 1, end);

        // 合并已经排好序的子数组
        merge(arr, begin, middle, end);
    }
}

// 打印排好序的数组
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;
}

// 主程序
int main() {
    int size;
    cout << "请输入数组大小: ";
    cin >> size;
    

    cout << "请输入 " << size << " 个数组元素: ";
    for (int i = 0; i < size; i++) {
        cin >> arr[i];
    }

    mergeSort(arr, 0, size - 1);

    cout << "排好序的数组: " << endl;
    printArray(arr, size);
    return 0;
}

逆序对问题:

要在 Merge Sort 基础上解决逆序对问题,可以通过在合并阶段计算出逆序对的数量。在逆序对问题中,如果在数组中 i < j 但是 arr[i] > arr[j],那么我们就称 (i, j) 是一个逆序对。

下面是一个基于 Merge Sort 的方法,通过在合并的过程中统计逆序对的数量来解决这个问题。

思路:

  • 在每次合并子数组的过程中,如果左子数组的元素大于右子数组的元素,那么这些左子数组的元素与当前右子数组的元素构成逆序对,因为左子数组元素的索引比右子数组元素的索引小。
while (left_index < left_size && right_index < right_size) {
        if (left_array[left_index] <= right_array[right_index]) {
            arr[merged_index] = left_array[left_index];
            left_index++;
        } else {
            // 如果左子数组的当前元素大于右子数组的当前元素,则构成逆序对
            arr[merged_index] = right_array[right_index];
            right_index++;
            // 左子数组中剩余的所有元素都与右子数组当前元素构成逆序对
            reverse_count += (left_size - left_index);
        }
        merged_index++;
    }
Scroll to Top