• 算法分析与设计之分而治之篇(1)


    一、归并排序

    • 问题背景:杠铃增重问题,每位参赛运动员向组委会提交排好序得三次举重量,为了便于杠铃得拆卸,组委会需对所有试举重量递增排序。

    •  已学过的解决方案
      • 选择排序:从待排序元素中迭代选出最小值并排序
      • 插入排序:依次将每个元素插入到已排序序列中

    •  分析杠铃增重问题
      • 特点:局部有序
      • 快速合并:比较两个有序数组当前最小元素,将较小者逐一合入新数组
      • 后续策略:
        • 逐一合并
        • 两两合并
    • 杠铃增重问题代码实现
    1. package com.tiger.study;
    2. import java.util.ArrayList;
    3. import java.util.List;
    4. public class MergeSort {
    5. public static void main(String[] args) {
    6. // 杠铃增重问题
    7. int[] l1 = {4, 10, 19};
    8. int[] l2 = {13, 16, 18};
    9. int[] l3 = {5, 9, 12};
    10. int[] l4 = {11, 15, 17};
    11. int[] mergeList = merge(merge(l1, l2), merge(l3, l4));
    12. for (int i = 0; i < mergeList.length; i++) {
    13. System.out.println(mergeList[i]);
    14. }
    15. }
    16. private static int[] merge(int[] a, int[] b) {
    17. int[] tempList = new int[a.length + b.length];
    18. int a_head = 0, b_head = 0;
    19. int count = 0;
    20. while (a_head < a.length && b_head < b.length) {
    21. if (a[a_head] <= b[b_head]) {
    22. tempList[count] = a[a_head++];
    23. count++;
    24. } else {
    25. tempList[count] = b[b_head++];
    26. count++;
    27. }
    28. }
    29. if (a_head < a.length) {
    30. for (int i = a_head; i < a.length; i++) {
    31. tempList[count] = a[i];
    32. count++;
    33. }
    34. } else if (b_head < a.length) {
    35. for (int i = b_head; i < b.length; i++) {
    36. tempList[count] = b[i];
    37. count++;
    38. }
    39. }
    40. return tempList;
    41. }
    42. }

    •  从杠铃增重问题转换为排序问题
      • 相较于杠铃增重问题而言,排序问题的问题输入有所改变,排序问题中以完整的数组输入,同时局部有序缺失。

    •  针对于问题输入的变化,我们可以将问题分解,当输入长度为1时,数组天然有序,这项可以处理局部有序确实的变化。

    • 通过两两合并构建有序数组

    • 归并排序算法流程:
      • 将数组A[1,n]排序问题分解为A[1,\left \lfloor \frac{n}{2} \right \rfloor]A[\left \lfloor \frac{n}{2} \right \rfloor + 1,n]的排序问题
      • 递归解决子问题得到两个有序的子序列
      • 将两个有序的子数组合并为一个有序数组

    •  分而治之法的一般步骤

    •   归并排序代码实现
    1. package com.tiger.study;
    2. public class MergeSort {
    3. public static void main(String[] args) {
    4. // 归并排序
    5. int[] arr = {24, 17, 40, 28, 13, 14, 22, 32, 40, 21, 48, 4, 47, 8, 37, 18};
    6. int[] result = new int[arr.length];
    7. mergeSort(arr, 0, arr.length - 1, result);
    8. for (int i = 0; i < result.length; i++) {
    9. System.out.println(result[i]);
    10. }
    11. }
    12. // 递归方法
    13. private static void mergeSort(int[] arr, int left, int right, int[] result) {
    14. // 递归结束条件
    15. if (right == left) return;
    16. int mid = (right + left) / 2;
    17. mergeSort(arr, left, mid, result);
    18. mergeSort(arr, mid + 1, right, result);
    19. merge(arr, left, right, result);
    20. }
    21. // 合并有序的部分
    22. private static void merge(int[] arr, int left, int right, int[] result) {
    23. int mid = (right + left) / 2;
    24. int left_head = left, right_head = mid + 1, result_index = left;
    25. while (left_head <= mid && right_head <= right) {
    26. if (arr[left_head] <= arr[right_head]) {
    27. result[result_index++] = arr[left_head++];
    28. } else {
    29. result[result_index++] = arr[right_head++];
    30. }
    31. }
    32. if (left_head <= mid) {
    33. for (int i = left_head; i <= mid; i++) {
    34. result[result_index++] = arr[i];
    35. }
    36. }
    37. if (right_head <= right) {
    38. for (int i = right_head; i <= right; i++) {
    39. result[result_index++] = arr[i];
    40. }
    41. }
    42. for (int i = left; i <= right; i++) {
    43. arr[i] = result[i];
    44. }
    45. }
    46. }

    二、递归式求解

    • 递归树法:用树的形式表示抽象递归

    •  代入法:这种方法比较看直觉,要先猜一个,然后去证明
    • 主定理法:这个方法有一堆的推导,然后我们作题的话只要记住下面的形式就行了

    •  递归式分析方法比较

     三、最大子数组问题

    • 问题描述:数组X中有若干个子数组,每个子数组为X中的一段序列,寻找X中最大的非空子数组。
    1. package com.tiger.study;
    2. // 最大子数组问题:数组X中有若干个子数组,每个子数组为X中的一段序列,寻找X中最大的非空子数组
    3. import java.util.ArrayList;
    4. import java.util.Arrays;
    5. public class MaxSubArray {
    6. public static void main(String[] args) {
    7. int[] arr = {1, -2, 4, 5, -2, 8, 3, -2, 6, 3, 7, -1};
    8. // 暴力解法
    9. ArrayList maxSubArray = new ArrayList<>();
    10. int maxValue = Integer.MIN_VALUE;
    11. for (int i = 0; i < arr.length; i++) {
    12. int temp = 0;
    13. for (int j = i; j < arr.length; j++) {
    14. temp += arr[j];
    15. if (temp >= maxValue) {
    16. maxValue = temp;
    17. maxSubArray.clear();
    18. for (int k = i; k <= j; k++) {
    19. maxSubArray.add(arr[k]);
    20. }
    21. }
    22. }
    23. }
    24. for (int i = 0; i < maxSubArray.size(); i++) {
    25. System.out.println(maxSubArray.get(i));
    26. }
    27. // 分治策略
    28. int[] maxSubArray1 = maxSubArray(arr, 0, arr.length - 1);
    29. for (int i = 0; i < maxSubArray1.length; i++) {
    30. System.out.println(maxSubArray1[i]);
    31. }
    32. }
    33. private static int[] maxSubArray(int[] arr, int low, int high) {
    34. if (low == high) {
    35. return new int[]{arr[low]};
    36. } else {
    37. int mid = (low + high) / 2;
    38. int[] s1 = maxSubArray(arr, low, mid);
    39. int[] s2 = maxSubArray(arr, mid + 1, high);
    40. int[] s3 = crossingSubArray(arr, low, mid, high);
    41. int[] sMax = max(s1, s2, s3);
    42. return sMax;
    43. }
    44. }
    45. private static int[] crossingSubArray(int[] arr, int low, int mid, int high) {
    46. int left_head = mid, right_head = mid + 1;
    47. int temp = 0;
    48. int max = Integer.MIN_VALUE;
    49. for (int i = mid; i >= low; i--) {
    50. temp += arr[i];
    51. if (temp > max) {
    52. left_head = i;
    53. max = temp;
    54. }
    55. }
    56. temp = 0;
    57. max = Integer.MIN_VALUE;
    58. for (int i = mid + 1; i <= high; i++) {
    59. temp += arr[i];
    60. if (temp > max) {
    61. right_head = i;
    62. max = temp;
    63. }
    64. }
    65. int[] result = new int[right_head - left_head + 1];
    66. for (int i = left_head, j = 0; i <= right_head && j < result.length; i++, j++) {
    67. result[j] = arr[i];
    68. }
    69. return result;
    70. }
    71. private static int[] max(int[] s1, int[] s2, int[] s3) {
    72. int sum1 = Arrays.stream(s1).sum();
    73. int sum2 = Arrays.stream(s2).sum();
    74. int sum3 = Arrays.stream(s3).sum();
    75. int max = Math.max(Math.max(sum1, sum2), sum3);
    76. if (max == sum1) {
    77. return s1;
    78. } else if (max == sum2) {
    79. return s2;
    80. } else {
    81. return s3;
    82. }
    83. }
    84. }

    四、逆序对计数问题

    • 问题描述:数组中存在下标小的值比下标大的值大的称为逆序对,求数组中一共有多少个逆序对?
    1. package com.tiger.study;
    2. // 逆序对计数问题:数组中共有多少个逆序对
    3. public class CountingInversions {
    4. public static void main(String[] args) {
    5. int[] arr = {13, 8, 10, 6, 15, 18, 12, 20, 9, 14, 17, 19};
    6. System.out.println(countingInversions(arr));
    7. System.out.println(countInver(arr, 0, 2));
    8. }
    9. // 暴力方法
    10. private static int countingInversions(int[] arr) {
    11. int count = 0;
    12. for (int i = 0; i < arr.length - 1; i++) {
    13. for (int j = i + 1; j < arr.length; j++) {
    14. if (arr[i] > arr[j]) {
    15. count++;
    16. }
    17. }
    18. }
    19. return count;
    20. }
    21. // 分治策略
    22. private static int countInver(int[] arr, int left, int right) {
    23. if (left == right) {
    24. return 0;
    25. }
    26. int mid = (left + right) / 2;
    27. int s1 = countInver(arr, left, mid);
    28. int s2 = countInver(arr, mid + 1, right);
    29. int s3 = mergeCount(arr, left, mid, right);
    30. int s = s1 + s2 + s3;
    31. return s;
    32. }
    33. private static int mergeCount(int[] arr, int left, int mid, int right) {
    34. int[] temp = new int[right - left + 1];
    35. int tempIndex = 0;
    36. int left_head = left, right_head = mid + 1;
    37. int s3 = 0;
    38. while (right_head <= right && left_head <= mid) {
    39. if (arr[left_head] > arr[right_head]) {
    40. temp[tempIndex++] = arr[right_head++];
    41. } else {
    42. temp[tempIndex++] = arr[left_head++];
    43. s3 += right_head - mid - 1;
    44. }
    45. }
    46. if (left_head <= mid) {
    47. for (int i = left_head; i <= mid; i++) {
    48. temp[tempIndex++] = arr[i];
    49. s3 += (right - mid);
    50. }
    51. }
    52. if (right_head <= right) {
    53. for (int i = right_head; i <= right; i++) {
    54. temp[tempIndex++] = arr[i];
    55. }
    56. }
    57. for (int i = 0; i < temp.length; i++) {
    58. arr[left + i] = temp[i];
    59. }
    60. return s3;
    61. }
    62. }

  • 相关阅读:
    透视星环科技上市:基础工具、技术融合、场景应用三维击穿
    Rust语言从入门到入魔 (更新中)
    〔001〕虚幻 UE5 安装教程
    pandas notes 25
    Ambari-yarn-timeline 内置 HBase数据表清理
    固态硬盘接口类型介绍
    光说不练假把式,一起Kafka业务实战。
    在Springboot项目中使用Redis提供给Lua的脚本
    JavaScript项目1_猜数字(前导)
    针不戳 腾讯开源GitHub星标125K微服务架构进阶宝典
  • 原文地址:https://blog.csdn.net/qq_38689352/article/details/126505584