算法导论-分治法和合并(Merge)排序

算法合集

https://github.com/SYRollingStone/SiYangUnityAlgorithmsSamples

一、分治法

分:大问题分解物小问题。

治:递归解决小问题。

并:将小问题的结果合并成为结果。

二、合并排序

4, 8, 1, 6, 0, 5, 3, 9

分

4, 8, 1, 6, 0, 5, 3, 9

↓ 切一刀

4, 8, 1, 6\] \[0, 5, 3, 9

↓ ↓

4, 8\] \[1, 6\] \[0, 5\] \[3, 9

↓ ↓ ↓ ↓

4\] \[8\] \[1\] \[6\] \[0\] \[5\] \[3\] \[9

治+合:

合并第 1 层:把两个"长度1的有序段"合成"长度2的有序段"

merge([4], [8]) => [4, 8]

merge([1], [6]) => [1, 6]

merge([0], [5]) => [0, 5]

merge([3], [9]) => [3, 9]

合并第 2 层:把两个"长度2的有序段"合成"长度4的有序段"

复制代码
merge([4,8], [1,6]) => [1,4,6,8] 
merge([0,5], [3,9]) => [0,3,5,9] 

合并第 3 层:合成最终结果

merge([1,4,6,8], [0,3,5,9]) => [0,1,3,4,5,6,8,9]

merge中的双指针排序

以这一步为例:

merge([4,8], [1,6])

用两个指针 i、j 分别指向左右数组的"当前最小元素":

Left : 4, 8

i^

Right: 1, 6

j^

结果 result: \[\]

先4和1比较,1更小,1放入result,j指针移动到6

Left : 4, 8

i^

Right: 1, 6

j^

结果 result: 1

4和6比较,4放入result,i指针移动到8

Left : 4, 8

i^

Right: 1, 6

j^

结果 result: 1,4

6和8比较,6放入result

Left : 4, 8

i^

Right: 1, 6

j^

结果 result: 1,4,6

将最后一个8放入result

结果 result: 1,4,6,8

三、代码

cs 复制代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// 分治法
// 合并排序
public class MergeSort : MonoBehaviour
{
    private int[] _data = {2,1,5,3,4,7,8,9,0,15,67,45 ,100,134,121,111,11};
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Before: " + string.Join(", ", _data));
        int[] temp = new int[_data.Length];
        Sort(_data, temp, 0, temp.Length - 1);
        Debug.Log("After: " + string.Join(", ", _data));
    }

    private void Sort(int[] array,int[] temp,int left,int right)
    {
        // 分,每次分一半,直到不可以再分 即 数组长度为1
        if (left >= right) return;
        
        int mid = left + (right - left) / 2;
        Sort(array,temp,left,mid);
        Sort(array,temp,mid+1,right);

        Merge(array, temp, left, mid, right);
    }

    private void Merge(int [] array,int [] temp,int left,int mid, int right)
    {
        for (int i = left; i <= right; i++)
        {
            temp[i] = array[i];
        }

        int iLeft = left;   // 左边数组的下指针
        int iRight = mid + 1;// 右边数组的指针,从mid+1开始
        int cur = left;

        while (iLeft <= mid && iRight <= right)
        {
            // <= 保持稳定性(相等时先取左边)
            if (temp[iLeft] <= temp[iRight]) // 改成 >= 就是降序
                array[cur++] = temp[iLeft++];
            else
                array[cur++] = temp[iRight++];
        }
        
        // 左边剩余拷贝回去(右边剩余不需要:已经在 a 的正确位置)
        while (iLeft <= mid)
            array[cur++] = temp[iLeft++];
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

四、分治法和动态规划的区别

我们发现分治法和动态规划都是划分子问题。

如果子问题重复度极高,用动态规划。子问题重复度不高用分治法。

严谨的说法:

  • 分治法 :把问题拆成相互独立(或基本不重叠)的子问题,分别求解后合并;通常不需要缓存子结果。

  • 动态规划 :子问题存在重叠(Overlapping Subproblems) ,并且满足最优子结构(Optimal Substructure),所以要用记忆化/填表复用子问题结果,避免重复计算。

相关推荐
UIU1147 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
论文复现现场7 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi7 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
秋天的怀念_David8 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
碧海银沙音频科技研究院9 小时前
在PC电脑上用Whisper→Marian算法实现离线语音翻译功能
算法·whisper
niyongsheng9 小时前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
j7~12 小时前
【穷举、深搜与回溯算法】一棵决策树讲透全排列与子集
数据结构·c++·算法·leetcode·回溯·递归
傅科摆 _ py13 小时前
DP,贪心,堆和栈,二分力扣复习
开发语言·python·算法
Shaoshing13 小时前
LeetCode100 42.接雨水
java·算法·leetcode
域限奇点访客13 小时前
用AI生成问卷的网站怎么选?看题目录入结构、题型映射和生成后的校验
算法