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


⚔️第三课:《分治算法挑战赛》⚔️

------ 分治魔法学院的最终试炼


一、🏰 故事开始:算法勇者大赛

1、经过前两节课的学习,

同学们已经掌握了:


🌟 归并排序

🌟 快速排序

🌟 分治思想


2、这一天,

汉克老师举办了一场:

⚔️"分治算法挑战赛"⚔️


3、只有真正理解:

🌈"拆问题"🌈

成功的同学,


4、才能成为:

👑 分治小勇者!


5、🌟 今天我们接下来要挑战:


🔍 二分查找

🏔️ 分治求最大值

💣 逆序对问题

🌳 汉诺塔问题

🧠 同学们真正理解"举一反三"


二、🌈 第一关:二分查找魔法


1、🏰 故事:猜数字王国


(1)数组中的数字我们都排好序了:

复制代码
1 3 5 7 9 11 13

(2)汉克老师在纸上记了数组中一个数字9。

让同学们来猜


(3)怎样的方法,才能猜的快呢?


2、❌ 普通方法

一个一个找:

复制代码
1 → 3 → 5 → 7 → 9

时间最多需要找n次,好像有些慢!


3、🌟 我们采用可以分治法!

每次:

🌟 砍掉一半!


4、🌈 第一步

看中间:

复制代码
7

因为:

复制代码
9 > 7

所以:


5、左边全部不用看了!

因为:

复制代码
左边都更小

只剩:

复制代码
9 11 13

6、🌈 第二步

再看中间:

复制代码
11

因为:

复制代码
9 < 11

所以:

右边不要了!


只剩:

复制代码
9

找到!


7、🌟 这就是二分查找!


8、🌈 为什么这样查找快?

普通查找:

复制代码
一个一个找

二分查找:

复制代码
每次删掉一半!

9、🌟 二分查找完整程序


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

int main()
{
    int a[100];
    int n, x;

    cin >> n;

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

    cin >> x;

    int l = 0;
    int r = n - 1;

    while(l <= r)
    {
        int mid = (l + r) / 2;

        if(a[mid] == x)
        {
            cout << "找到了!位置是:" << mid;
            return 0;
        }
        else if(a[mid] < x)
        {
            l = mid + 1;
        }
        else
        {
            r = mid - 1;
        }
    }

    cout << "没找到";

    return 0;
}

10、🌟 二分查找核心思想


🌈 看中间!

🌈 小了去右边

🌈 大了去左边

🌈 每次减少一半!


三、⚔️ 第二关:分治求最大值


1、🏰 故事:谁是力量最强的勇者?

现在:

复制代码
3 9 2 7 5

2、汉克老师问:

👑 "谁最大?"


3、❌ 普通方法

我们一个个来比较。


4、🌟 我们也可以使用分治法

(1)先拆成两部分!


复制代码
3 9 | 2 7 5

(2)左边最大:

复制代码
9

(3)右边最大:

复制代码
7

(4)最后:

复制代码
9 和 7

比较。


(5)得到:

复制代码
9

5、🌟 分治法求最大值程序


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

int a[100];

int find_max(int l, int r)
{
    // 只有一个数字
    if(l == r)
    {
        return a[l];
    }

    int mid = (l + r) / 2;

    // 左边最大
    int left_max = find_max(l, mid);

    // 右边最大
    int right_max = find_max(mid + 1, r);

    // 返回更大的
    if(left_max > right_max)
        return left_max;
    else
        return right_max;
}

int main()
{
    int n;

    cin >> n;

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

    cout << find_max(0, n - 1);

    return 0;
}

6、🌟 重点理解

真正关键:


(1)🌈 原来的大问题

复制代码
整个数组最大值

(2)🌈 变成两个小问题:

复制代码
求左边最大值
+
求右边最大值

(3)🌟 这就是:

"拆问题!"


四、⚔️ 第三关:逆序对大冒险(进阶)


1、🏰 什么是逆序对?

(1)例如:

复制代码
5 2

(2)5在前面。

但:

复制代码
5 > 2

(3)这就是:

🌟 逆序对


2、🌈 再看:

复制代码
3 1 2

(1)逆序对有:

复制代码
3 1
3 2

(2)共:

复制代码
2个

3、🌟 如何快速统计?

可以使用我们学过的:

🌈归并排序!


4、🌟 逆序对完整程序


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

int a[100], temp[100];
int ans = 0;

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;

    while(i <= mid && j <= r)
    {
        if(a[i] <= a[j])
        {
            temp[k++] = a[i++];
        }
        else
        {
            // 产生逆序对
            ans += mid - i + 1;

            temp[k++] = a[j++];
        }
    }

    while(i <= mid)
    {
        temp[k++] = a[i++];
    }

    while(j <= r)
    {
        temp[k++] = a[j++];
    }

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

int main()
{
    int n;

    cin >> n;

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

    merge_sort(0, n - 1);

    cout << "逆序对数量:" << ans;

    return 0;
}

5、🌟 最关键的一句代码

复制代码
ans += mid - i + 1;

6、为什么?

(1)例如:

左边:

复制代码
5 6 7

右边:

复制代码
2

(2)发现:

复制代码
5 > 2

(3)因为左边已经有序。

所以:

复制代码
6 > 2
7 > 2

也一定成立!


(4)因此:

一次加:

复制代码
3个逆序对

是不是特别省时间?😃


五、⚔️ 第四关:汉诺塔魔法塔


1、🏰 汉诺塔的故事

(1)有三根柱子:

复制代码
A B C

(2)现在:

有很多盘子:

复制代码
大盘
中盘
小盘

(3)规则:

🌟 小盘不能压大盘!


(4)目标:

把所有盘子:

复制代码
A → C

2、🌟 汉诺塔真正思想

(1)不是:

"直接搬"


(2)而是:


🌈 先搬上面的小盘

🌈 再搬最大的盘

🌈 再搬小盘回来


(3)🌟 这也是分治的方法!


3、🌟 汉诺塔程序


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

void hanoi(int n, char A, char B, char C)
{
    // 只有一个盘子
    if(n == 1)
    {
        cout << A << " -> " << C << endl;
        return;
    }

    // 先移动上面的盘子
    hanoi(n - 1, A, C, B);

    // 移动最大的盘子
    cout << A << " -> " << C << endl;

    // 再移动上面的盘子
    hanoi(n - 1, B, A, C);
}

int main()
{
    int n;

    cin >> n;

    hanoi(n, 'A', 'B', 'C');

    return 0;
}

4、🌳 汉诺塔递归树(重点)

(1)例如:

复制代码
3个盘子

(2)会变成:

复制代码
搬2个
搬最大
搬2个

(3)而:

复制代码
搬2个

又会继续拆。


(4)🌟 这就是:

"把复杂的大问题,拆成简单的小问题!"


六、🌈 第五关:真正理解分治法,"举一反三"

1、现在同学们,开始明白:


🌟 分治不是一种题!


2、而是一种:

🧠 思想!


3、🌟 只要看到:


大问题难

能拆小

小问题能解决

最后能合并答案


🌟 就有可能用分治的方法!


七、🎯 课后总结


1、🌟 分治三步曲


① 分(Divide)

拆问题


② 治(Conquer)

解决小问题


③ 合(Combine)

合并答案


2、🌟 归并排序

拆数组再合并。


3、🌟 快速排序

按pivot分区域。


4、🌟 二分查找

每次砍掉一半。


5、🌟 分治求最大值

左右分别求最大。


6、🌟 汉诺塔

先解决小盘问题。


7、🌟 真正掌握分治方法的同学

不是只会背代码。

而是:

🌈"看到问题,会拆问题!"🌈


8、🏆 分治魔法学院今天毕业了!

同学们终于成为了:

👑 分治小勇者!

接下来,我们一起学习,更强大的算法魔法!


相关推荐
V搜xhliang02461 小时前
OpenClaw进阶完全教程
运维·人工智能·算法·microsoft·自动化
hele_two1 小时前
SDL2设置透明度
c++·图形渲染
小杰3121 小时前
网络框架源码阅读技巧
服务器·网络·c++·reactor·zlmediakit·zltoolkit
叼烟扛炮1 小时前
C++ 知识点12 构造函数
开发语言·c++·算法·构造函数
满天星83035772 小时前
定长内存池ObjectPool
数据结构·c++·算法·链表
叼烟扛炮2 小时前
C++第八讲:string 类
开发语言·c++·算法·string
MOONICK2 小时前
bit7z压缩与解压
c++
Chase_______2 小时前
LeetCode 1493 & 3634 题解:滑动窗口双指针,从“删一个元素的全1子数组“到“最少移除使数组平衡“
算法·leetcode
努力努力再努力wz2 小时前
【Qt入门系列】第一个 Qt Widgets 程序:项目创建、UI 文件、Hello World、对象树与 qDebug 日志
java·c语言·开发语言·数据结构·c++·qt·ui