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

相关推荐
foundbug9998 小时前
自适应滤除直达波干扰的MATLAB实现
开发语言·算法·matlab
CN-Dust9 小时前
【C++】while语句例题专题
数据结构·c++·算法
灵智实验室10 小时前
PX4位置速度估计技术详解(四):LPE 激光雷达高度融合的实现错误
算法·无人机·px 4
CQU_JIAKE10 小时前
【A】3742,3387,并查集
算法
gihigo199810 小时前
CHAN时延估计算法(二维/三维定位实现)
算法
freexyn10 小时前
Matlab自学笔记七十六:表达式的展开、因式分解、化简、合并同类项
笔记·算法·matlab
样例过了就是过了10 小时前
LeetCode热题 不同路径
c++·算法·leetcode·动态规划
dog25011 小时前
圆锥曲线和二次曲线
开发语言·网络·人工智能·算法·php
Wadli11 小时前
27.单调队列
算法
Navigator_Z11 小时前
LeetCode //C - 1031. Maximum Sum of Two Non-Overlapping Subarrays
c语言·算法·leetcode