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

相关推荐
mit6.8246 分钟前
dijk|tire+floyd+dp %
算法
独自破碎E21 分钟前
【总和拆分 + 双变量遍历】LCR_012_寻找数组的中心下标
数据结构·算法
WBluuue21 分钟前
Codeforces 1076 Div3(ABCDEFG)
c++·算法
u01092727132 分钟前
模板编译期排序算法
开发语言·c++·算法
GIS瞧葩菜41 分钟前
Cesium 轴拖拽 + 旋转圈拖拽 核心数学知识
人工智能·算法·机器学习
m0_686041611 小时前
C++中的适配器模式变体
开发语言·c++·算法
txzrxz1 小时前
结构体排序,双指针,单调栈
数据结构·算法·双指针算法·单调栈·结构体排序
AndrewHZ1 小时前
【AI黑话日日新】什么是AI智能体?
人工智能·算法·语言模型·大模型·llm·ai智能体
wWYy.1 小时前
算法:二叉树最大路径和
数据结构·算法
葱明撅腚1 小时前
利用Python挖掘城市数据
python·算法·gis·聚类