第12课 神奇的二分法

小航猜数字 | 趣味C++课堂

🎬
课前动画
看二分法怎么快速猜中数字
📚
知识点回顾
梳理本节课所有知识点
💻
课堂任务
在线写代码,实现猜数字游戏
🚀
拓展任务
3道拓展代码填空,挑战更高难度
✏️
随堂测试
5道选择+3道判断,检验学习成果
🌟
课后总结
动画回顾本节课重点
老师心里想了一个数(1-100),看二分法怎么快速猜中!
点击开始演示 ▶
📦 模块一:二分法基础概念

1. 二分法定义 重点

每次取搜索区间的中间值,根据反馈判断目标在左半还是右半区间,将范围缩小一半,重复直到找到目标。

取中间 → 判方向 → 缩区间 → 重复

2. 二分法核心思想

"分而治之":把大问题不断拆成一半大小的小问题,每次排除一半的可能性。

每次排除50%的可能性

3. 生活中的二分法

查字典、找东西、快递分拣、电脑文件搜索,都是二分法思想的应用。

翻字典→看页码→往左/往右翻

4. 二分法的效率 重点

n个元素中查找,最多需要⌈log₂(n+1)⌉次。1-1000最多10次,1-100万最多20次。

次数 ≈ log₂(范围大小)
🔄 模块二:循环与变量

5. while(true)无限循环 重点

while(true)条件永远为真,会一直执行循环体,必须用break才能跳出。适合"不知道循环多少次"的场景。

while(true) { ... break; ... }

6. break跳出循环

break语句立即终止当前循环,执行循环后面的代码。猜对数字时用break结束游戏。

if(猜对了) break;

7. 边界变量left/right

left是当前搜索区间的左边界,right是右边界。每次猜测后根据反馈更新其中一个。

初始 left=1, right=1000

8. 中间值mid计算

mid=(left+right)/2,取当前区间的中间值作为猜测值。整数除法自动向下取整。

mid = (left + right) / 2
💻 模块三:二分法代码实现

9. 区间更新逻辑 重点

猜小了→left=mid+1(目标在右边);猜大了→right=mid-1(目标在左边)。+1/-1避免死循环。

小了:left=mid+1 大了:right=mid-1

10. 计数器count

count记录猜测次数,初始值为1(第一次猜测),每次循环末尾count++。

int count=1; ... count++;

11. 用户输入判断

用if/else if/else判断用户输入:1=猜小了,2=猜大了,3=猜对了。注意用==比较。

if(ans==1)...else if(ans==2)...else...

12. 程序完整流程

初始化边界→循环:取中值→输出猜测→读取反馈→更新边界或break→计数+1→结束输出总次数。

初始化→while(true)→猜→反馈→更新→break

🎯 本节课5大重点

二分法定义(取中间、缩区间、重复)
二分法效率(log₂n次,1000个数最多10次)
while(true)无限循环(需配合break跳出)
区间更新逻辑(left=mid+1, right=mid-1)
中间值计算(mid=(left+right)/2)

guess_number.cpp
#include <iostream>
using namespace std;
int main() {
    int left = ; // ①左边界初始值
    int right = ; // ②右边界初始值
    int count = ; // ③计数器初始值(第1次开始)
    int mid, ans;
    while() { // ④循环条件(无限循环)
        mid = () / 2; // ⑤中间值计算
        cout << "第" << count << "次,猜" << mid << endl;
        cout << "1 猜小了?" << endl;
        cout << "2 猜大了?" << endl;
        cout << "3 猜对了?" << endl;
        cin >> ans;
        if(ans == 1) {
            left = ; // ⑥猜小了,左边界更新
        } else if(ans 2) { // ⑦判断猜大了
            right = mid - 1;
        } else {
            cout << "猜对了" << endl;
            ; // ⑧跳出循环
        }
        count++;
    }
    cout << "一共竞猜了" << count << "次" << endl;
    return 0;
}

🎮 操作按钮

📤 输出结果

// 点击"运行程序"查看输出

💡 填空提示

  • ① 左边界从几开始?(范围最小值)
  • ② 右边界是几?(范围最大值)
  • ③ 第一次猜是第几次?
  • ④ 什么循环会一直执行直到break?
  • ⑤ 中间值 = (左边界 ? 右边界) / 2
  • ⑥ 猜小了,左边界跳到mid后面几?
  • ⑦ 判断相等用一个等号还是两个?
  • ⑧ 猜对了用什么跳出循环?
1

范围显示功能

难度:⭐ | 填空数:2

任务描述:在原猜数字程序基础上,每次猜测后额外输出一行"当前范围:left ~ right",让玩家清楚知道还剩哪些可能的数字。

提示:在输出猜测信息之后、询问用户反馈之前,增加一行cout输出当前的left和right。

extend1_range.cpp
#include <iostream>
using namespace std;
int main() {
    int left = 1, right = 1000, count = 1;
    int mid, ans;
    while(true) {
        mid = (left + right) / 2;
        cout << "第" << count << "次,猜" << mid << endl;
        // 拓展1:显示当前范围
        cout << "当前范围:" << << " ~ " << << endl;
        cout << "1 猜小了? 2 猜大了? 3 猜对了?" << endl;
        cin >> ans;
        if(ans == 1) left = mid + 1;
        else if(ans == 2) right = mid - 1;
        else { cout << "猜对了" << endl; break; }
        count++;
    }
    cout << "一共竞猜了" << count << "次" << endl;
    return 0;
}

🎮 操作

📤 输出

// 点击"运行"查看输出
0
2

智能提示功能

难度:⭐⭐ | 填空数:4

任务描述:当剩余范围小于等于10时,程序自动提示"答案就在这几个数中:X X X ...",把所有可能的数字列出来帮助玩家。

提示:用if判断范围大小(right-left+1),满足条件时用for循环从left遍历到right输出每个数。

extend2_hint.cpp
#include <iostream>
using namespace std;
int main() {
    int left = 1, right = 1000, count = 1;
    int mid, ans;
    while(true) {
        mid = (left + right) / 2;
        cout << "第" << count << "次,猜" << mid << endl;
        cout << "当前范围:" << left << " ~ " << right << endl;
        // 拓展2:智能提示,剩余范围≤10时列出所有可能
        if( <= 10) {
            cout << "答案就在这几个数中:";
            for(int i = ; i <= ; ) {
                cout << i << " ";
            }
            cout << endl;
        }
        cout << "1 猜小了? 2 猜大了? 3 猜对了?" << endl;
        cin >> ans;
        if(ans == 1) left = mid + 1;
        else if(ans == 2) right = mid - 1;
        else { cout << "猜对了" << endl; break; }
        count++;
    }
    return 0;
}

🎮 操作

📤 输出

// 点击"运行"查看输出
0
3

效率对比功能

难度:⭐⭐ | 填空数:2

任务描述:猜对数字后,程序输出"理论最多10次,你用了X次",让玩家知道二分法的最优效率和自己的表现。

提示:在else分支(猜对了)中,输出猜测次数count,并与理论最多次数10进行对比。1-1000范围二分法最多10次(因为2^10=1024≥1000)。

extend3_efficiency.cpp
#include <iostream>
using namespace std;
int main() {
    int left = 1, right = 1000, count = 1;
    int mid, ans;
    while(true) {
        mid = (left + right) / 2;
        cout << "第" << count << "次,猜" << mid << endl;
        cout << "当前范围:" << left << " ~ " << right << endl;
        cout << "1 猜小了? 2 猜大了? 3 猜对了?" << endl;
        cin >> ans;
        if(ans == 1) left = mid + 1;
        else if(ans == 2) right = mid - 1;
        else {
            cout << "猜对了!" << endl;
            // 拓展3:效率对比
            cout << "理论最多" << << "次,你用了" << << "次" << endl;
            break;
        }
        count++;
    }
    return 0;
}

🎮 操作

📤 输出

// 点击"运行"查看输出
0

🔐 拓展任务答案解析

完成拓展任务后,输入老师/家长提供的密码,查看3道拓展题的完整答案与详细解析。

拓展1:范围显示功能 — 答案解析

填空答案:

left — 输出左边界变量
right — 输出右边界变量

解析:cout输出"当前范围:"后,需要输出左边界left和右边界right两个变量的值,中间用" ~ "连接。这是最基本的变量输出练习,注意变量名要和定义时一致(小写left和right)。

运行效果:每次猜测后会多输出一行,如"当前范围:1 ~ 1000",让玩家清楚知道搜索范围在不断缩小。

拓展2:智能提示功能 — 答案解析

填空答案:

right-left+1 — 计算当前范围内数字的个数
left — for循环从左边界开始
right — for循环到右边界结束
i++ — 每次循环i增加1

解析:

范围大小计算:right-left+1 计算当前区间内有多少个数字。例如left=1,right=10时,10-1+1=10个数。注意要+1,因为左右边界都包含在内。

②③ for循环遍历区间:for(int i=left; i<=right; i++) 是遍历一个区间最经典的写法——从左边界开始,到右边界结束(包含右边界,所以用<=)。

i++:每次循环后i自增1,逐个输出区间内的每个数字。

运行效果:当范围缩小到10以内时(如最后阶段),程序会自动列出所有可能的数字,如"答案就在这几个数中:75 76 77 78 79"。

拓展3:效率对比功能 — 答案解析

填空答案:

10 — 理论最多次数(固定值)
count — 实际猜测次数(变量)

解析:

为什么是10?二分法每次范围缩小一半,k次后范围为1000/2^k。当范围≤1时就找到了,即2^k ≥ 1000。因为2^9=512<1000,2^10=1024≥1000,所以最多需要10次。这里范围固定1-1000,所以直接写常量10即可。

count变量:count是计数器,记录玩家实际猜了多少次。猜对后输出count,和理论最多次数10对比,让玩家知道自己的表现是否达到最优。

运行效果:猜对后输出"理论最多10次,你用了6次",如果≤10次说明达到了二分法的最优效率。

🔒

输入密码查看答案解析

密码由老师/家长提供,输入正确后可查看3道拓展题的完整答案与详细解析。

❌ 密码错误,请重试
🎉 答题完成!
0
第1题单选题
二分法的核心思想是什么?

✅ 正确答案:B

二分法的核心就是"分而治之"——每次取当前范围的中间值,根据反馈(大了/小了)判断目标在左半区间还是右半区间,从而把搜索范围直接缩小一半。重复这个过程就能快速找到目标。

第2题单选题
在1-1000范围内用二分法猜数字,最多需要猜几次?

✅ 正确答案:A

每次猜测范围缩小一半,k次后范围大小为1000/2^k。当范围≤1时就找到了,即2^k ≥ 1000。因为2^9=512<1000,2^10=1024≥1000,所以最多需要10次。这就是二分法的威力——1000个数最多10次就能猜中!

第3题单选题(中)
代码中 mid = (left + right) / 2 的作用是什么?

✅ 正确答案:C

mid是middle(中间)的缩写,(left+right)/2就是取左边界和右边界的平均值,即当前区间的正中间值。二分法每次都猜中间值,这样无论反馈是"大了"还是"小了",都能把范围缩小一半。整数除法会自动向下取整,对结果没有影响。

第4题单选题(中)
如果用户反馈"猜小了",应该怎么更新边界?

✅ 正确答案:B

"猜小了"说明目标数字比mid大,所以目标一定在右半区间(mid的右边)。左边界应该更新为mid+1,而不是mid——因为mid已经猜过了,而且确认比目标小,可以直接排除。如果写成left=mid,当left和right相邻时(如left=5,right=6),mid=5,猜5小了,left=mid=5,下一轮mid还是5,就会死循环!+1就是为了避免这种情况。

第5题单选题(难)
while(true) 是什么类型的循环?

✅ 正确答案:B

while(true)的条件永远为真,所以会一直执行循环体,是无限循环。必须在循环体内用break语句才能跳出。猜数字游戏正好适合用while(true)——因为不知道要猜几次,猜对了才用break结束。如果忘记写break,程序就会一直运行下去(死循环),需要手动终止。

第6题判断题
二分法每次猜测后,搜索区间都会缩小一半。

✅ 正确答案:✅ 正确

正确!二分法每次猜中间值,如果"大了"就排除右半区间,如果"小了"就排除左半区间,无论哪种情况都排除了一半的可能性,所以区间大小每次都缩小一半。这就是二分法效率高的根本原因。

第7题判断题(中)
反馈"猜小了"时,应该更新 right = mid - 1。

✅ 正确答案:❌ 错误

错误!"猜小了"说明目标比mid大,目标在右半区间,应该更新左边界 left = mid + 1。更新 right = mid - 1 是"猜大了"的时候的操作——目标比mid小,在左半区间,所以右边界移到mid-1。一定要分清楚方向:小了→往右找(left+1),大了→往左找(right-1)。

第8题判断题(难)
left = mid + 1 中的 +1 是为了避免重复猜mid,也防止当left和right相邻时出现死循环。

✅ 正确答案:✅ 正确

正确!+1有两个重要作用:①mid已经猜过了,确认比目标小,可以直接排除,不需要再包含在范围内;②更关键的是防止死循环——假设left=5, right=6,如果写成left=mid,那么mid=(5+6)/2=5,猜5小了,left=mid=5,下一轮mid还是5,永远卡在5,程序死循环!写成left=mid+1=6,下一轮mid=6,就能继续推进了。所以+1/-1是二分法边界更新的关键细节。

🎉 本节课你学会了什么?
二分法——用最少的次数找到目标的神奇算法!
🎯
二分法
取中间值,根据反馈缩小区间,重复直到找到
高效率
1000个数最多10次,100万最多20次(log₂n)
🔄
while(true)
无限循环,必须配合break才能跳出
📏
边界更新
猜小了left=mid+1,猜大了right=mid-1
🔢
中间值
mid=(left+right)/2,每次猜区间正中间
💻
代码实现
8个填空全部搞定,猜数字游戏运行成功!
🚀 继续保持这份探索精神,下节课见!