GESP5级C++考试语法知识(十四、分治算法(一))


🌟分治算法第一课:《分治魔法初体验》

------ 魔法学院的"拆问题"超级魔法


🏰 故事开始:乱糟糟的魔法书架

1、在遥远的算法大陆上,

有一所非常厉害的学校:

✨分治魔法学院✨


2、这一天,小魔法师"小码"接到了一个任务:

📚 整理魔法图书馆!


3、图书馆里有很多数字魔法书:

复制代码
8 3 5 1 7 2 6 4

4、管理员爷爷着急地说:

"天啊!这些书完全乱了!"

"如果一本一本慢慢整理,

可能天黑都整理不完呀!"


5、同学们想:

"那怎么办呢?"


6、这时,汉克老师出现了!

他说:

✨"遇到大问题,不要硬拼!"✨

✨"把它拆小!我们可以使用分治算法来解决"✨


🌈 一、什么叫"分治"?

1、汉克老师在黑板上写下:

复制代码
大问题
↓
拆成小问题
↓
分别解决
↓
最后合并

这就是:

🌟分治算法(Divide and Conquer)


2、🧠 分治是什么意思?

(1)"分":

👉 把大问题拆开


(2)"治":

👉 解决小问题


(3)最后:

👉 再把答案合起来


3、🍭 就像吃大蛋糕!

(1)如果一个超大蛋糕:

复制代码
🎂🎂🎂🎂🎂🎂🎂

一口根本吃不下!


(2)怎么办?

当然是:

复制代码
切开

(3)变成:

复制代码
🍰 🍰 🍰 🍰

很多小蛋糕,我们就容易吃的下了!


4、🌟 分治算法最核心的思想

✨"大问题不好做?"

✨"那就拆小!"✨


🏰 二、第一次学习"拆问题"

1、举个例子,现在我们来整理:

复制代码
8 3 5 1

2、汉克老师说:

"先别急着排序!"

先拆!


3、第一步:切一半

复制代码
8 3      5 1

4、第二步:继续拆

(1)左边:

复制代码
8 3

还能拆:

复制代码
8   3

(2)右边:

复制代码
5 1

还能拆:

复制代码
5   1

(3)现在变成:

复制代码
8   3   5   1

5、🌟 神奇的事情来了!

每组只有:

1个数字

的时候,

其实已经:

"自动有序了!"


6、比如:

复制代码
8

只有一个数。

乱不乱?

当然不乱!


7、所以:

🌟分治算法的重要思想:

当问题足够小,

它就变简单了!


🧠 三、开始"治"------解决小问题

1、现在:

复制代码
8   3

怎么合并?


2、汉克老师说:

"谁小谁在前面!"


3、比较:

复制代码
8 和 3

3更小!


4、所以:

复制代码
3 8

5、另一边:

复制代码
5 和 1

6、变成:

复制代码
1 5

7、现在整个世界变成:

复制代码
3 8      1 5

🌟 四、最后一步:合并大答案

1、现在:

(1)左边有序:

复制代码
3 8

(2)右边也有序:

复制代码
1 5

(3)怎么变成:

复制代码
1 3 5 8

呢?


2、🍭 拉拉链魔法!

(1)像拉拉链一样:


(2)比较左右两边的第一个数:

复制代码
3 和 1

1更小:

复制代码
1

1就排在前面了。1的后面的数字5,就要参与下一轮比较了。


(3)再比较:

复制代码
3 和 5

3更小:

复制代码
1 3

3就排在1的后面了。3的后面的数字8,就要参与下一轮比较了。


(4)再比较:

复制代码
8 和 5

5更小:

复制代码
1 3 5

5就排在3的后面了。3的后面的数字8,没有比较的对手了。


(5)最后只剩下:

复制代码
8

得到:

复制代码
1 3 5 8

(6)这个交替比较的过程,是不是就像:

给左右两侧的衣服,拉拉链一样呢?


🌈 五、这就是分治算法!

整个过程:


🌟第一步:分(Divide)

拆开:

复制代码
8 3 5 1
↓
8 3 | 5 1
↓
8 | 3 | 5 | 1

🌟第二步:治(Conquer)

小问题自己解决:

复制代码
8 和 3
→ 3 8

5 和 1
→ 1 5

🌟第三步:合(Combine)

把答案合并:

复制代码
3 8 + 1 5
↓
1 3 5 8

🎯 六、为什么分治算法厉害?

1、汉克老师问大家:

"如果有100万个数字,

一个一个整理,

会不会累死?"


2、同学们回答:

复制代码
会!!!

3、但分治法:

复制代码
一直拆
一直拆
一直拆

4、每次都把问题变小。

而且拆的,特别快!


🌟 七、生活中的分治思想


1、🍕 分披萨

太大:

复制代码
🍕

切开:

复制代码
🍕 🍕 🍕 🍕

2📚 整理书柜

先整理左边。

再整理右边。

左右两边分别治理。

最后达成全部有序。


3、👨‍👩‍👧‍👦 大扫除

一家人一起分工:

  • 妈妈扫地

  • 爸爸擦桌子

  • 孩子整理玩具

比一个人干快多了!


🌟 八、归并排序正式登场

1、刚刚汉克老师讲的这个:

复制代码
拆开
排序
合并

的排序方法,

名字叫:

✨归并排序(Merge Sort)✨


2、🧠 为什么叫"归并"?

因为:

先拆,拆到最小,

再"归"回来,

"并"在一起。


🌳 九、归并排序的递归树

1、汉克老师在黑板画了一棵树:

复制代码
          8 3 5 1
         /       \
      8 3       5 1
     /   \     /   \
    8     3   5     1

2、再往回合并:

复制代码
    3 8       1 5
         \   /
       1 3 5 8

3、🌟 这个递归树:

它能帮助我们:

看清问题是怎么被拆开的!


🎮 十、课堂小游戏:《魔法数字整理师》

1、汉克老师给出数字:

复制代码
7 2 9 1

请同学们:


第一步

拆:

复制代码
7 2 | 9 1

第二步

继续拆:

复制代码
7 | 2 | 9 | 1

第三步

排序合并:

复制代码
2 7 | 1 9

第四步

最终:

复制代码
1 2 7 9

🌟十一、归并排序完整程序


cpp 复制代码
#include <iostream>
using namespace std;

int a[100];      // 原数组
int temp[100];   // 临时数组

// 归并排序函数
void merge_sort(int l, int r)
{
    // 只有一个数字,不需要排序
    if(l == r)
        return;

    // 找中间位置
    int mid = (l + r) / 2;

    // 排序左边
    merge_sort(l, mid);

    // 排序右边
    merge_sort(mid + 1, r);

    // 开始合并
    int i = l;         // 左边起点
    int j = mid + 1;   // 右边起点
    int k = l;         // temp数组的位置

    // 两边都有数字时
    while(i <= mid && j <= r)
    {
        // 谁小谁先放进去
        if(a[i] <= a[j])
        {
            temp[k] = a[i];
            i++;
        }
        else
        {
            temp[k] = a[j];
            j++;
        }

        k++;
    }

    // 左边还有剩余
    while(i <= mid)
    {
        temp[k] = a[i];
        i++;
        k++;
    }

    // 右边还有剩余
    while(j <= r)
    {
        temp[k] = a[j];
        j++;
        k++;
    }

    // 把排好序的数据复制回原数组
    for(int p = l; p <= r; p++)
    {
        a[p] = temp[p];
    }
}

int main()
{
    int n;

    cout << "请输入数字个数:";
    cin >> n;

    cout << "请输入数字:" << endl;

    for(int i = 0; i < n; i++)
    {
        cin >> a[i];
    }

    // 调用归并排序
    merge_sort(0, n - 1);

    cout << "排序后:" << endl;

    for(int i = 0; i < n; i++)
    {
        cout << a[i] << " ";
    }

    return 0;
}

🌟 十二:代码详细讲解


1、🧠 数组定义

(1)原始数组:

复制代码
int a[100];

这是:

原始数字仓库


例如:

复制代码
8 3 5 1

都放在这里。


(2)🌟 temp数组

复制代码
int temp[100];

这是:

临时整理仓库


(3)为什么需要它?

因为:

排序时不能乱覆盖原数据。

所以先放到temp里。


2、🌟 归并排序函数

复制代码
void merge_sort(int l, int r)

(1)上传参数的意思:

排序:

复制代码
l 到 r

这一段数字。


(2)例如:

复制代码
merge_sort(0,3);

意思:

排序:

复制代码
第0~3个数字

3、🌟 结束条件

复制代码
if(l == r)
    return;

(1)🧠 为什么?

因为:

复制代码
只有一个数字

时,

它已经有序了!


(2)例如:

复制代码
8

需要排序吗?

当然不用!


4、🌟 确定中间位置

复制代码
int mid = (l + r) / 2;

(1)例如:

复制代码
0 ~ 3

中间位置:

复制代码
1

(2)所以:

左边:

复制代码
0~1

右边:

复制代码
2~3

5、🌟 递归拆分


(1) 先排左边

复制代码
merge_sort(l, mid);

(2)再排右边

复制代码
merge_sort(mid + 1, r);

6、🌳 递归树(课堂演示)

(1)程序会变成:

复制代码
merge_sort(0,3)
    ↓
merge_sort(0,1)
merge_sort(2,3)

(2)继续:

复制代码
merge_sort(0,0)
merge_sort(1,1)
merge_sort(2,2)
merge_sort(3,3)

(3)最后全部拆成:

单个数字!


7、🌟 开始合并(最核心)


(1)三个小助手

复制代码
int i = l;
int j = mid + 1;
int k = l;

(2)🧠 i是谁?

左边队伍开始的指针。


(3)🧠 j是谁?

右边队伍开始的指针。


(4)🧠 k是谁?

temp数组位置。


(5)🌈 举例

现在:

左边:

复制代码
位置索引:  0  1
数值:     3  8

右边:

复制代码
位置索引: 2 3
数值:     1 5

三个小助手:

复制代码
左侧队伍指针 i = 0
左侧队伍指针 j = 2
临时数组temp k= 0

开始比较:


第一次

复制代码
3 和 1

1小。

放进去:

复制代码
1

三个小助手:

cpp 复制代码
左侧队伍指针 i = 0
左侧队伍指针 j = 2+1 =3
临时数组temp k= 0+1 =1

第二次

复制代码
3 和 5

3小。

变成:

复制代码
1 3

三个小助手:

cpp 复制代码
左侧队伍指针 i = 0+1 =1
左侧队伍指针 j = 3
临时数组temp k= 1+1 =2

第三次

复制代码
8 和 5

5小。

变成:

复制代码
1 3 5

三个小助手:

cpp 复制代码
左侧队伍指针 i = 1
左侧队伍指针 j = 3+1=4  (4>3)
临时数组temp k= 2+1=3

最后剩下8

得到:

复制代码
1 3 5 8
cpp 复制代码
 // 左边还有剩余
    while(i <= mid)
    {
        temp[k] = a[i];
        i++;
        k++;
    }
cpp 复制代码
左侧队伍指针 i = 1+1=2(2>1)
左侧队伍指针 j = 4  (4>3)
临时数组temp k= 3+1=4 

排好了,还要复制回原来的数组

cpp 复制代码
 // 把排好序的数据复制回原数组
    for(int p = l; p <= r; p++)
    {
        a[p] = temp[p];
    }
}

8、🌟 核心while循环

复制代码
while(i <= mid && j <= r)

意思:

左右两边都有数字时

持续比较。


🌟 谁小谁进去

复制代码
if(a[i] <= a[j])

如果左边更小。


复制代码
temp[k] = a[i];

放进temp。


然后:

复制代码
i++;

左边向后走。


9、🌟 处理剩余数字

有时候:

左边结束了。

右边还有数字。


例如:

复制代码
1 3

和:

复制代码
5 8 9

前面比较完:

复制代码
1 3

后:

复制代码
5 8 9

还没放进去。

怎么办?

直接全部复制!


左边剩余

复制代码
while(i <= mid)

右边剩余

复制代码
while(j <= r)

10、🌟 复制回原数组

复制代码
for(int p = l; p <= r; p++)
{
    a[p] = temp[p];
}

为什么?

因为:

真正的数据在:

复制代码
temp

里面。

要复制回:

复制代码
a数组

11、🌟 完整运行过程演示

输入:

复制代码
8 3 5 1

第一步:拆

复制代码
8 3 | 5 1

第二步:继续拆

复制代码
8 | 3 | 5 | 1

第三步:合并小组

复制代码
3 8 | 1 5

第四步:最终合并

复制代码
1 3 5 8

12、🌟 时间复杂度

归并排序非常快!

因为:

每次都:

对半拆!


速度大约:

O(n log n)


小学生可以简单记:

"超级快的排序,而且非常稳定的排序!"


13、🌟 归并排序优点


✅ 非常稳定

相同数字不会乱顺序。


✅ 非常快

适合大量数据。


✅ 分治思想特别经典

很多高级算法都从这里开始。


🌟 十三、本课总结:

1、✨"大问题拆小,小问题解决,再合起来!"✨

这就是:

🌈分治算法的灵魂!


2、✨分治算法不是硬拼!✨

而是:

复制代码
大问题
↓
拆小
↓
小问题解决
↓
再合并

3、今天我们学会了:


🌟 什么是分治法

把大问题拆小。


4、🌟 分治三步

复制代码
分
治
合

5、🌟 什么是归并排序

不断拆分。

最后合并。


6、🌟 为什么分治厉害

因为:

小问题更容易解决!


十四、🏆 课后挑战

请同学们自己试着拆:

复制代码
6 4 9 2 7 1

看看:


🌟 怎么拆?

🌟 怎么合并?

🌟 最后结果是什么?


🌈 下节课预告

下一节课:

⚡《快速排序大冒险》⚡

我们会学习:


🌟 如何选"队长数字"

🌟 如何让小的站左边

🌟 如何让大的站右边

🌟 神奇的双指针魔法

还有更多冒险等着大家!

相关推荐
李日灐1 小时前
【优选算法5】位运算经典算法面试题
后端·算法·面试·位运算
郝学胜-神的一滴1 小时前
干货版《算法导论》03:动态数组 × 链表的极致平衡艺术
java·数据结构·c++·python·算法·链表
风筝在晴天搁浅1 小时前
字节 LeetCode CodeTop 912.排序数组
算法·leetcode
Liangwei Lin1 小时前
LeetCode 48. 旋转图像
算法
AGV算法笔记1 小时前
【具身智能研究进展】RoboBrain 2.5:让机器人真正理解“空间”和“时间”的大脑模型
算法·3d·机器人·具身智能·感知算法
love在水一方1 小时前
【InternNav】 工程详细分析
人工智能·算法·机器学习
合兴软件@2 小时前
合兴软件重磅推出高性能HSM固件 国密算法赋能汽车信息安全新防线
网络·算法·网络安全·汽车·信息与通信
wearegogog1232 小时前
基于遗传算法的阵列天线方向图优化MATLAB实现
算法·matlab
Controller-Inversion2 小时前
312. 戳气球
算法