• 归并分治 归并排序的应用 + 图解 + 笔记


     归并分治 前置知识:讲解021-归并排序

    归并排序 图解 递归 + 非递归 + 笔记-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/weixin_41987016/article/details/134338789?spm=1001.2014.3001.5501原理:

    • (1)思考一个问题在大范围上的答案,是否等于,左部分的答案 + 右部分的答案 + 跨越左右产生的答案
    • (2)计算“跨越左右产生的答案”时,如果加上左、右各自有序这个设定,会不会获得计算的便利性
    • (3)如果以上两点都成立,那么该问题很可能被归并分治解决(话不说满,因为总有很毒的出题人)
    • (4)求解答案的过程中只需要加入归并排序的过程即可,因为要让左、右各自有序,来获得计算的便利性

    补充:

    • (1)一些用归并分治解决的问题,往往也可以用线段树,树状数组等解法。时间复杂度也都是最优解,这些数据结构都会在【扩展】
    • (2)本书讲述的题目都是归并分治的常规题,难度不大。归并分治不仅可以解决简单问题,还可以解决很多较难的问题,只要符合上面说的特征。比如二维空间里任何两点间的最短距离问题,这个内容会在【挺难】课程阶段里讲述。顶级公司考这个问题的也很少,因为很难,但是这个问题本身并不冷门,来自《算法导论》原题
    • (3)还有一个常考的算法:“整块分治”。会在【必备】课程阶段讲到

    举个栗子】假设数组 s = [1,3,5,2,4,6],给定一个数组arr,实现函数返回arr的“小和”

        在s[0]的左边所有 <= s[0]的数的总和为0
        在s[1]的左边所有 <= s[1]的数的总和为1
        在s[2]的左边所有 <= s[2]的数的总和为4
        在s[3]的左边所有 <= s[3]的数的总和为1
        在s[4]的左边所有 <= s[4]的数的总和为6
        在s[5]的左边所有 <= s[5]的数的总和为15
    所以s数组的 “小和” 为:0 + 1 + 4 + 1 + 6 + 15 = 27

    • "小和"问题是,当我 j 来到一个位置的时候,你的左侧 i 去给我划答案  

    C++代码:

    1. #include
    2. using namespace std;
    3. #include
    4. // 返回跨左右产生的小和累加和,左侧有序、右侧有序,让左右两侧整体有序
    5. // arr[left...mid] arr[mid+1...right]
    6. long merge(vector<int>& arr,int left, int mid, int right) {
    7. int n = right - left + 1;
    8. vector<int> help(n, 0);
    9. // 统计部分
    10. long ans = 0;
    11. for (int j = mid + 1, i = left, sum = 0; j <= right; j++) {
    12. while (i <= mid && arr[i] <= arr[j]) {
    13. sum += arr[i++];
    14. }
    15. ans += sum;
    16. }
    17. // 正常merge
    18. int i = 0;
    19. int a = left;
    20. int b = mid + 1;
    21. while (a <= mid && b <= right) {
    22. help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
    23. }
    24. while (a <= mid) {
    25. help[i++] = arr[a++];
    26. }
    27. while (b <= right) {
    28. help[i++] = arr[b++];
    29. }
    30. for (i = 0; i < n; i++) {
    31. arr[i+left] = help[i];
    32. }
    33. return ans;
    34. }
    35. // 结果比较大,用 int 会溢出的,所以返回 long 类型
    36. // 特别注意溢出这个点,笔试常见坑
    37. // 返回arr[left...right]范围上,小和的累加和,同时把arr[left...right]变有序
    38. long smallSum(vector<int>&arr, int left, int right) {
    39. if (left == right) return 0;
    40. //int mid = (left + right) / 2;
    41. int mid = left + ((right - left) >> 1);
    42. return smallSum(arr,left, mid) + smallSum(arr,mid + 1, right) + merge(arr,left, mid, right);
    43. }
    44. int main() {
    45. vector<int>arr = { 7,7,6,2,6,5,4,9 };
    46. //vectorarr = { 1, 3, 5, 2, 4, 6 };
    47. int n = arr.size();
    48. cout<<"该数组的小和为:"<<smallSum(arr,0, n - 1)<
    49. system("pause");
    50. return 0;
    51. }

    计算数组的小和__牛客网 (nowcoder.com)icon-default.png?t=N7T8https://www.nowcoder.com/questionTerminal/edfe05a1d45c4ea89101d936cac32469?f=discussion

    •  实战牛客网[编程题]计算数组的小和
    1. #include
    2. #include
    3. using namespace std;
    4. // 返回跨左右产生的小和累加和,左侧有序、右侧有序,让左右两侧整体有序
    5. // arr[left...mid] arr[mid+1...right]
    6. long merge(vector<int>& arr, int left, int mid, int right) {
    7. int n = right - left + 1;
    8. vector<int> help(n, 0);
    9. // 统计部分
    10. long ans = 0;
    11. for (int j = mid + 1, i = left, sum = 0; j <= right; j++) {
    12. while (i <= mid && arr[i] <= arr[j]) {
    13. sum += arr[i++];
    14. }
    15. ans += sum;
    16. }
    17. // 正常merge
    18. int i = 0;
    19. int a = left;
    20. int b = mid + 1;
    21. while (a <= mid && b <= right) {
    22. help[i++] = arr[a] <= arr[b] ? arr[a++] : arr[b++];
    23. }
    24. while (a <= mid) {
    25. help[i++] = arr[a++];
    26. }
    27. while (b <= right) {
    28. help[i++] = arr[b++];
    29. }
    30. for (i = 0; i < n; i++) {
    31. arr[i + left] = help[i];
    32. }
    33. return ans;
    34. }
    35. // 结果比较大,用 int 会溢出的,所以返回 long 类型
    36. // 特别注意溢出这个点,笔试常见坑
    37. // 返回arr[left...right]范围上,小和的累加和,同时把arr[left...right]变有序
    38. long smallSum(vector<int>& arr, int left, int right) {
    39. if (left == right) return 0;
    40. //int mid = (left + right) / 2;
    41. int mid = left + ((right - left) >> 1);
    42. return smallSum(arr, left, mid) + smallSum(arr, mid + 1, right) + merge(arr, left, mid, right);
    43. }
    44. int main() {
    45. int n;
    46. cin >> n;
    47. vector<int> arr(n, 0);
    48. for (int i = 0; i < n; i++) cin >> arr[i];
    49. cout << smallSum(arr, 0, n - 1) << endl;
    50. return 0;
    51. }

    参考和推荐视频:

    算法讲解022【必备】归并分治_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1L14y1B7ef/?spm_id_from=333.788.recommend_more_video.-1&vd_source=a934d7fc6f47698a29dac90a922ba5a3

  • 相关阅读:
    滑动窗口与前缀和算法总结和例题
    vulfocus——drupal1代码执行(CVE-2018-7600)
    [python 刷题] 刷题常用函数
    y85.第四章 Prometheus大厂监控体系及实战 -- prometheus告警机制进阶、pushgateway和prometheus存储(十六)
    leetcode背包问题总结
    Map和Set知识点
    LeetCode:709.转换成小写字母
    IDL学习:语法基础-结构体
    备份服务器数据的重要
    基于PSO粒子群优化的汽车刹车稳定性数据matlab仿真与分析
  • 原文地址:https://blog.csdn.net/weixin_41987016/article/details/134340208