算法导论-分治法和合并(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),所以要用记忆化/填表复用子问题结果,避免重复计算。

相关推荐
灵感__idea7 小时前
Hello 算法:众里寻她千“百度”
前端·javascript·算法
Wect17 小时前
LeetCode 130. 被围绕的区域:两种解法详解(BFS/DFS)
前端·算法·typescript
NAGNIP1 天前
一文搞懂深度学习中的通用逼近定理!
人工智能·算法·面试
颜酱2 天前
单调栈:从模板到实战
javascript·后端·算法
CoovallyAIHub2 天前
仿生学突破:SILD模型如何让无人机在电力线迷宫中发现“隐形威胁”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
从春晚机器人到零样本革命:YOLO26-Pose姿态估计实战指南
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
Le-DETR:省80%预训练数据,这个实时检测Transformer刷新SOTA|Georgia Tech & 北交大
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
强化学习凭什么比监督学习更聪明?RL的“聪明”并非来自算法,而是因为它学会了“挑食”
深度学习·算法·计算机视觉
CoovallyAIHub2 天前
YOLO-IOD深度解析:打破实时增量目标检测的三重知识冲突
深度学习·算法·计算机视觉
NAGNIP2 天前
轻松搞懂全连接神经网络结构!
人工智能·算法·面试