当前位置: 首页 > news >正文

【每日算法】Day 11-1:分治算法精讲——从归并排序到最近点对问题(C++实现)

掌握“分而治之”的算法哲学!今日系统解析分治算法的核心思想与实战应用,覆盖排序优化、数学计算、几何问题等高频场景,彻底理解“分解-解决-合并”的算法范式。


一、分治算法核心思想

分治算法(Divide and Conquer) 是一种将复杂问题分解为相似子问题的算法范式,核心步骤:

  1. 分解(Divide):将原问题划分为多个子问题

  2. 解决(Conquer):递归解决子问题(若子问题足够小则直接求解)

  3. 合并(Combine):将子问题的解合并为原问题的解

适用场景:

  • 子问题相互独立且与原问题形式相同

  • 合并操作的复杂度低于直接求解原问题

  • 典型应用:归并排序、快速排序、矩阵乘法优化


二、分治算法模板(C++)

通用模板结构
Result divideConquer(Problem problem) {
    if (problem is trivial) return solveDirectly(problem);
    
    // 分解为子问题
    SubProblem sub1 = split(problem);
    SubProblem sub2 = split(problem);
    
    // 递归解决子问题
    Result res1 = divideConquer(sub1);
    Result res2 = divideConquer(sub2);
    
    // 合并结果
    return merge(res1, res2);
}

关键特性:
  • 递归终止条件:定义最小子问题的处理方式

  • 分解策略:决定如何分割问题(均匀分割/按特征分割)

  • 合并逻辑:影响最终时间复杂度的重要因素


三、四大经典应用场景

场景1:归并排序(时间复杂度O(n log n))
void mergeSort(vector<int>& nums, int l, int r) {
    if (l >= r) return;
    int mid = l + (r - l)/2;
    mergeSort(nums, l, mid);    // 分解左半
    mergeSort(nums, mid+1, r);  // 分解右半
    merge(nums, l, mid, r);     // 合并有序数组
}

void merge(vector<int>& nums, int l, int mid, int r) {
    vector<int> tmp(r-l+1);
    int i = l, j = mid+1, k = 0;
    while (i <= mid && j <= r) {
        tmp[k++] = nums[i] < nums[j] ? nums[i++] : nums[j++];
    }
    while (i <= mid) tmp[k++] = nums[i++];
    while (j <= r) tmp[k++] = nums[j++];
    for (int m = 0; m < tmp.size(); ++m) {
        nums[l + m] = tmp[m];
    }
}
场景2:快速幂算法(LeetCode 50)
double myPow(double x, int n) {
    if (n == 0) return 1.0;
    long long N = n;
    if (N < 0) { x = 1/x; N = -N; }
    return fastPow(x, N);
}

double fastPow(double x, long long n) {
    if (n == 0) return 1.0;
    double half = fastPow(x, n/2);
    if (n % 2 == 0) return half * half;
    else return half * half * x;
}
场景3:多数元素(LeetCode 169)
int majorityElement(vector<int>& nums) {
    return divide(nums, 0, nums.size()-1);
}

int divide(vector<int>& nums, int l, int r) {
    if (l == r) return nums[l];
    int mid = l + (r-l)/2;
    int left = divide(nums, l, mid);
    int right = divide(nums, mid+1, r);
    
    if (left == right) return left;
    
    int cntLeft = count(nums, l, r, left);
    int cntRight = count(nums, l, r, right);
    return cntLeft > cntRight ? left : right;
}

int count(vector<int>& nums, int l, int r, int target) {
    int cnt = 0;
    for (int i=l; i<=r; ++i) {
        if (nums[i] == target) cnt++;
    }
    return cnt;
}
场景4:最近点对问题(进阶几何问题)
struct Point { double x, y; };

bool compareX(const Point& a, const Point& b) { return a.x < b.x; }
bool compareY(const Point& a, const Point& b) { return a.y < b.y; }

double closestPair(vector<Point>& points) {
    sort(points.begin(), points.end(), compareX);
    return divide(points, 0, points.size()-1);
}

double divide(vector<Point>& points, int l, int r) {
    if (r - l <= 3) return bruteForce(points, l, r);
    
    int mid = l + (r-l)/2;
    double dl = divide(points, l, mid);
    double dr = divide(points, mid+1, r);
    double d = min(dl, dr);
    
    vector<Point> strip;
    for (int i=l; i<=r; ++i) {
        if (abs(points[i].x - points[mid].x) < d)
            strip.push_back(points[i]);
    }
    sort(strip.begin(), strip.end(), compareY);
    
    for (int i=0; i<strip.size(); ++i) {
        for (int j=i+1; j<strip.size() && (strip[j].y - strip[i].y) < d; ++j) {
            d = min(d, distance(strip[i], strip[j]));
        }
    }
    return d;
}

四、分治算法复杂度分析

问题类型递推公式时间复杂度示例
归并排序T(n) = 2T(n/2) + O(n)O(n log n)排序、逆序对计数
快速幂T(n) = T(n/2) + O(1)O(log n)幂运算、斐波那契
最近点对T(n) = 2T(n/2) + O(n)O(n log n)几何计算
二分搜索T(n) = T(n/2) + O(1)O(log n)有序数组查找

五、大厂真题实战

真题1:数组中的逆序对(剑指 Offer 51)

分治解法(归并排序优化):
 

int reversePairs(vector<int>& nums) {
    vector<int> tmp(nums.size());
    return mergeSort(nums, tmp, 0, nums.size()-1);
}

int mergeSort(vector<int>& nums, vector<int>& tmp, int l, int r) {
    if (l >= r) return 0;
    int mid = l + (r-l)/2;
    int count = mergeSort(nums, tmp, l, mid) 
              + mergeSort(nums, tmp, mid+1, r);
    
    int i = l, j = mid+1, pos = l;
    while (i <= mid && j <= r) {
        if (nums[i] <= nums[j]) {
            tmp[pos++] = nums[i++];
        } else {
            count += mid - i + 1; // 统计逆序对
            tmp[pos++] = nums[j++];
        }
    }
    while (i <= mid) tmp[pos++] = nums[i++];
    while (j <= r) tmp[pos++] = nums[j++];
    copy(tmp.begin()+l, tmp.begin()+r+1, nums.begin()+l);
    return count;
}
真题2:最大子序和(LeetCode 53)

分治解法:

int maxSubArray(vector<int>& nums) {
    return divide(nums, 0, nums.size()-1).maxSum;
}

struct Status {
    int lSum, rSum, mSum, tSum;
};

Status divide(vector<int>& nums, int l, int r) {
    if (l == r) return {nums[l], nums[l], nums[l], nums[l]};
    int mid = l + (r-l)/2;
    Status left = divide(nums, l, mid);
    Status right = divide(nums, mid+1, r);
    
    int tSum = left.tSum + right.tSum;
    int lSum = max(left.lSum, left.tSum + right.lSum);
    int rSum = max(right.rSum, right.tSum + left.rSum);
    int mSum = max({left.mSum, right.mSum, left.rSum + right.lSum});
    return {lSum, rSum, mSum, tSum};
}

六、分治算法优化策略

优化方法应用场景优化效果
记忆化重复子问题减少重复计算
阈值切换小子问题直接求解降低递归开销
并行计算独立子问题提升多核利用率
剪枝策略无效分支提前终止减少计算量

七、常见误区与调试技巧

  1. 分解不平衡:导致递归深度过大(如快速排序最坏情况)

  2. 合并逻辑错误:未正确处理边界条件(如逆序对计数中的区间索引)

  3. 终止条件缺失:导致无限递归

  4. 调试技巧

    • 打印递归树层级与当前处理范围

    • 可视化中间结果(如归并排序的合并过程)

    • 添加断言检查子问题分解的正确性


LeetCode真题训练:

  • 493. 翻转对

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

  • 327. 区间和的个数

相关文章:

  • [运维]Linux系统扩容磁盘空间-将未分配的空间进行整合分配
  • 规范Unity工程目录和脚本结构能有效提升开发效率、降低维护成本
  • FastBlock是一个专为全闪存场景设计的高性能分布式块存储系统
  • Pytroch搭建全连接神经网络识别MNIST手写数字数据集
  • 在MFC中使用Qt(四):使用属性表(Property Sheet)实现自动化Qt编译流程
  • idea设置全局maven配置 对新建项目生效
  • 前端 - ts - - declare声明类型
  • 【斯坦福】【ICLR】RAPTOR:基于树结构的检索增强技术详解
  • RHCE 第一次作业 25-3-28
  • 火山dts迁移工具使用
  • linux》》docker 、containerd 保存镜像、打包tar、加载tar镜像
  • Android OTA升级中SettingsProvider数据库升级的深度解析与完美解决方案
  • Android R adb remount 调用流程
  • okhttp3网络请求
  • 【Apache Hive】
  • springboot3 整合 Log4j2
  • python3面试题(元类、内存管理、函数)
  • Maven工具学习使用(六)——聚合与继承
  • 24、web前端开发之CSS3(一)
  • java对pdf文件分页拆分
  • 美国将与阿联酋合作建立海外最大的人工智能数据中心
  • 中国—美国经贸合作对接交流会在华盛顿成功举行
  • 人民日报:从“轻微免罚”看涉企执法方式转变
  • 陕西宁强县委书记李宽任汉中市副市长
  • 古巴外长谴责美国再次将古列为“反恐行动不合作国家”
  • 陕西河南山西等地将现“干热风”灾害,小麦产区如何防范?