cen's blog cen's blog
首页
  • 编程文章

    • Markdown使用
    • C语言程序的运行
  • 学习笔记

    • C++学习
    • C++数据结构
    • MySQL
    • Linux
    • 网络编程
算法
  • Git
  • ProtoBuf
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)

cen

十年饮冰,难凉热血
首页
  • 编程文章

    • Markdown使用
    • C语言程序的运行
  • 学习笔记

    • C++学习
    • C++数据结构
    • MySQL
    • Linux
    • 网络编程
算法
  • Git
  • ProtoBuf
  • 分类
  • 标签
  • 归档
关于
GitHub (opens new window)
  • 双指针算法
  • 二分算法
  • 常用数据结构
  • 动态规划
  • 递归、搜索和回溯
  • 分治和归并
    • 快速排序
    • 归并排序
  • 位运算
  • 算法
cen
2026-04-14
目录

分治和归并

# 快速排序

阶段 快速排序的做法
分解 选择一个基准值(pivot),通过分区操作将数组分成三部分:[ < pivot ]、[ = pivot ]、[ > pivot ]
解决 递归地对左右子区间(三路版中为 < pivot 和 > pivot 两部分)调用快速排序本身。等于 pivot 的元素已经就位,不再参与递归。
合并 无需显式合并。因为分区是原地完成的,当左右子区间都有序后,整个数组自然有序。这是快排与归并排序最大的区别。
// 三路快速排序
void qsort(vector<int>& nums, int left, int right) {
    if(left >= right)
        return;
    int i = left, l = left - 1, r = right + 1;
    int pivot = nums[rand() % (right - left + 1) + left];
    while(i < r) {
        if(nums[i] > pivot) swap(nums[--r], nums[i]);
        else if(nums[i] == pivot) i++;
        else swap(nums[++l], nums[i++]);
    }
    /*
      [left, l]: < pivot
      [l + 1, r - 1]: = pivot
      [r, right]: > pivot
    */
    qsort(nums, left, l);
    qsort(nums, r, right);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

例题:TopK 问题 215.数组中的第 K 个最大元素 (opens new window)

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        srand(time(NULL));
        return qselect(nums, 0, nums.size() - 1, k);
    }
    // 快速选择算法:
    int qselect(vector<int>& nums, int left, int right, int k) {
        if (left == right)
            return nums[left];
        int i = left, l = left - 1, r = right + 1;
        int pivot = nums[rand() % (right - left + 1) + left];
        while (i < r) {
            if (nums[i] > pivot)
                swap(nums[--r], nums[i]);
            else if (nums[i] == pivot)
                i++;
            else
                swap(nums[++l], nums[i++]);
        }
        int c = right - r + 1, b = r - l - 1;
        if (c >= k) {
            return qselect(nums, r, right, k);
        } else if (c + b >= k) {
            return pivot;
        } else {
            return qselect(nums, left, l, k - b - c);
        }
    }
};
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

# 归并排序

阶段 归并排序的做法
分解 将数组从中间位置一分为二,得到两个规模相等的子数组:[left, mid] 和 [mid+1, right]
解决 递归地对左右子数组分别调用归并排序,直到子数组长度为 1(天然有序)
合并 将两个已排序的子数组合并成一个完整的有序数组,这是归并排序的核心步骤,需要 O(n) 的辅助空间
void mergeSort(vector<int>& nums, int left, int right) {
    if (left >= right)
        return;
    // 1. 划分左右区间
    int mid = (right - left) / 2 + left;
    // 2. 左右区间排序
    mergeSort(nums, left, mid);
    mergeSort(nums, mid + 1, right);
    // 3. 还原数组
    int cur1 = left, cur2 = mid + 1, i = 0;
    while (cur1 <= mid && cur2 <= right)
        temp[i++] = nums[cur1] <= nums[cur2] ? nums[cur1++] : nums[cur2++];
    while (cur1 <= mid)
        temp[i++] = nums[cur1++];
    while (cur2 <= right)
        temp[i++] = nums[cur2++];
    for (int i = left; i <= right; ++i)
        nums[i] = temp[i - left];
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

利用归并排序思想,核心就是在合并两个有序子数组时,利用有序性来快速计算某种跨左右子数组的统计量。

例题:求逆序对个数 (opens new window)

class Solution {
public:
    vector<int> temp;
    int ret = 0;
    int reversePairs(vector<int>& record) {
        temp.resize(record.size());
        mergeSort(record, 0, record.size() - 1);
        return ret;
    }
    void mergeSort(vector<int>& nums, int left, int right) {
        if (left >= right)
            return;
        // 1. 划分左右区间
        int mid = (right - left) / 2 + left;
        // 2. 左右区间排序
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        // 3. 还原数组,计算逆序对:
        int cur1 = left, cur2 = mid + 1, i = 0;
        // 升序:
        // while (cur1 <= mid && cur2 <= right) {
        //     if (nums[cur1] <= nums[cur2]) {
        //         temp[i++] = nums[cur1++];
        //     } else {
        //         ret += mid - cur1 + 1;
        //         temp[i++] = nums[cur2++];
        //     }
        // }
        // 降序:
        // while (cur1 <= mid && cur2 <= right) {
        //     if (nums[cur1] <= nums[cur2]) {
        //         temp[i++] = nums[cur2++];
        //     } else {
        //         ret += right - cur2 + 1;
        //         temp[i++] = nums[cur1++];
        //     }
        // }
        while (cur1 <= mid)
            temp[i++] = nums[cur1++];
        while (cur2 <= right)
            temp[i++] = nums[cur2++];
        for (int i = left; i <= right; ++i)
            nums[i] = temp[i - left];
    }
};
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

LCR 170. 交易逆序对的总数

  1. 计算右侧小于当前元素的个数

todo

上次更新: 2026/04/29, 15:44:34
递归、搜索和回溯
位运算

← 递归、搜索和回溯 位运算→

最近更新
01
位运算
04-21
02
单例模式
04-14
03
事务特性
04-10
更多文章>
Theme by Vdoing | Copyright © 2024-2026 京ICP备2020044002号-3 京公网安备11010502056119号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式