归并排序与快速排序

发表于 2025-03-29 1016 字 6 min read

文章目录
归并排序与快速排序的实现与要点对比。

当时我还在背模板阶段,归并和快排算是第一批真正需要”理解”的排序。光看”递归”俩字看得头大,后来亲手把数组拆开再拼回去,才明白分治就是拆到最小再一步步合并。这篇把两个排序的实现和关键点记下来。

归并排序

// 归并排序 - 合并两个有序数组
void merge(vector<int>& arr, int l, int mid, int r) {
    // 创建临时数组存储合并结果
    vector<int> temp(r - l + 1);
    int i = l, j = mid + 1, k = 0;

    // 比较左右两部分的元素,按序放入临时数组
    while(i <= mid && j <= r) {
        if(arr[i] <= arr[j]) {
            temp[k++] = arr[i++];  // 左半部分元素较小
        } else {
            temp[k++] = arr[j++];  // 右半部分元素较小
        }
    }

    // 将左半部分剩余元素放入临时数组
    while(i <= mid) temp[k++] = arr[i++];
    // 将右半部分剩余元素放入临时数组
    while(j <= r) temp[k++] = arr[j++];

    // 将临时数组中的元素复制回原数组
    for(int i = 0; i < k; i++) {
        arr[l + i] = temp[i];
    }
}

// 归并排序的递归实现
void mergeSort(vector<int>& arr, int l, int r) {
    if(l >= r) return;  // 递归终止条件:区间长度为1

    int mid = l + (r - l >> 1);  // 计算中点,防止溢出
    mergeSort(arr, l, mid);      // 递归排序左半部分
    mergeSort(arr, mid + 1, r);  // 递归排序右半部分
    merge(arr, l, mid, r);       // 合并两个有序数组
}

快速排序

void quickSort(vector<int>& arr, int l, int r) {
    if(l >= r) return;
    int x = arr[l + (r - l >> 1)];
    int i = l, j = r;
    while(i < j) {
        while(arr[i] < x) i++;
        while(arr[j] > x) j--;
        if(i < j) swap(arr[i], arr[j]);
    }
    quickSort(arr, l, j);
    quickSort(arr, j + 1, r);
}
// 快速排序的分区函数
// 将数组按照基准值分为两部分,返回分区点位置
int partition(vector<int>& arr, int l, int r) {
    int x = arr[l + (r - l >> 1)];  // 选择中间位置的元素作为基准值
    int i = l, j = r;  // 双指针,从两端向中间移动
    while(i < j) {
        while(arr[i] < x) i++;  // 找到左边第一个大于等于基准值的元素
        while(arr[j] > x) j--;  // 找到右边第一个小于等于基准值的元素
        if(i < j) swap(arr[i], arr[j]);  // 交换这两个元素
    }
    return j;  // 返回分区点位置
}

// 快速排序的主函数
// 通过分治的方式递归排序数组
void quickSort(vector<int>& arr, int l, int r) {
    if(l >= r) return;  // 递归终止条件:区间长度为1
    int x = partition(arr, l, r);  // 获取分区点
    quickSort(arr, l, x - 1);  // 递归排序左半部分
    quickSort(arr, x + 1, r);  // 递归排序右半部分
}

题目

P1908 逆序对

思路

求逆序对的过程,说白了正好和归并排序对应:利用分治思想,逆序对数等于左半边逆序对数+右半边逆序对数+跨边逆序对数,正好对应”左半边排序+右半边排序+合并数组”这三步。当时我就是把这点想通了,代码才不再靠死背。

示例代码

#include<bits/stdc++.h>

using namespace std;

#define ll long long
//#define int ll
#define pii pair<int, int>
#define all(x) x.begin(),x.end()
#define fer(i, m, n) for(int i = m; i < n; ++i)
#define ferd(i, m, n) for(int i = m; i >= n; --i)
#define dbg(x) cout << #x << ' ' << char(61) << ' ' << x << '\n'

const int MOD = 1e9 + 7;
const int N = 2e5 + 2;
const int inf = 1e9;

ll merge(vector<int> &arr, int l, int mid, int r) {
    ll cnt = 0;
    vector<int> temp(r - l + 1);
    int i = l, j = mid + 1, k = 0;
    while(i <= mid && j <= r) {
        if(arr[i] <= arr[j]) temp[k++] = arr[i++];
        else temp[k++] = arr[j++], cnt += mid - i + 1;
    }
    while(i <= mid) temp[k++] = arr[i++];
    while(j <= r) temp[k++] = arr[j++];
    fer(i, 0, k) arr[l + i] = temp[i];
    return cnt;
}

ll mergeSort(vector<int> &arr, int l, int r) {
    if(l >= r) return 0;
    int mid = l + (r - l >> 1);
    ll res = 0;
    res += mergeSort(arr, l, mid);
    res += mergeSort(arr, mid + 1, r);
    res += merge(arr, l, mid, r);
    return res;
}

signed main() {
    ios::sync_with_stdio(false); cin.tie(nullptr);

    int n;
    cin >> n;
    vector<int> arr(n);
    fer(i, 0, n) cin >> arr[i];
    cout << mergeSort(arr, 0, n - 1) << '\n';
    return 0;
}