GESP5级C++考试语法知识(九、二分算法(一))


🌟《小侦探找宝藏:二分查找大冒险》

🏰 第一幕:宝藏在哪里?

1、有一天,小侦探小明来到一条已经排好序的宝藏街

复制代码
1  3  5  7  9  11  13  15

2、每个数字代表一个宝箱编号(从小到大排好序!)


3、🎯任务:

找到编号为 11 的宝箱!


🤔 第二幕:普通方法(慢!)

1、小明一开始是这样找的:

👉 使用枚举法,一个一个找:

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

2、😫问题:太慢了!


🚀 第三幕:聪明办法(二分查找登场!)

1、小明突然想到:

💡"既然已经排好序,我是否可以看看中间数字是大还是小!"


2、🧠 核心思想(非常重要!)

👉 每次都检查中间位置

  • 如果中间值 == 目标 → 找到了 🎉

  • 如果中间值 < 目标 → 去右边找 👉

  • 如果中间值 > 目标 → 去左边找 👈


🎯 第四幕:一步一步找宝藏!

1、我们来找 11

数组:

复制代码
[1, 3, 5, 7, 9, 11, 13, 15]

2、🥇 第1次

左:0 (数组索引为0)

右:7 (数组索引为7)


(1)👉 中间:

复制代码
mid = (0 + 7) / 2 = 3

(2)👉 数值:

复制代码
a[3] = 7

(3)😮 7 < 11

👉 去右边!


3、🥈 第2次

左:4 (数组索引为4)

右:7 (数组索引为7)


(1)👉 中间:

复制代码
mid = (4 + 7) / 2 = 5

(2)👉 数值:

复制代码
a[5] = 11 ✔

(3)🎉 找到了!


4、🧾 总结

👉 二分查找四步走:

1️⃣ 左右边界定好(left, right)

2️⃣ 找中间(mid)

3️⃣ 比大小

4️⃣ 缩范围


🧩 第五幕:参考程序:

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

int main()
{
    int a[] = {1,3,5,7,9,11,13,15};
    int n = 8;
    int target = 11;

    int left = 0, right = n - 1;

    while(left <= right)
    {
        int mid = (left + right) / 2;

        if(a[mid] == target)
        {
            cout << "找到了!位置是:" << mid << endl;
            break;
        }
        else if(a[mid] < target)
        {
            left = mid + 1; // 去右边
        }
        else
        {
            right = mid - 1; // 去左边
        }
    }

    return 0;
}

⚠️ 第六幕:容易犯的错误!

1、❌ 错误1:数组没有排序!

👉 二分查找必须是:

复制代码
有序数组!(从小到大或从大到小)

2、❌ 错误2:死循环

👉 本题循环条件:

复制代码
left <= right

3、❌ 错误3:更新错误

👉 本题更新为:

复制代码
left = mid + 1
right = mid - 1

🚀 第七幕:二分查找有多快?

1、假设有 1000 个数:

  • 普通查找:最多找 1000 次 😫

  • 二分查找:最多找 10 次!😲


2、👉 因为每次都"砍一半"!


3、时间复杂度是: O(logn)


🎯 简单理解(一句话)

👉 二分查找就是:

💡每次砍掉一半范围的查找方法!


我们接下来继续进行:


🌟《小侦探的升级任务:寻找"最左"和"最右"的宝藏》

🏰 第一幕:奇怪的宝藏街

1、这次,小明来到了一条有重复数字的街道

复制代码
1  2  2  2  3  4  4  5

🎯任务升级:

👉 找数字 2 的第一个位置(最左边)

👉 找数字 2 的最后一个位置(最右边)


2、🤔 为什么普通二分不够?

普通二分只能找到:

👉 "某一个 2"

但不知道是不是:

  • 最左的 ❌

  • 最右的 ❌


3、🧠 核心思想(关键理解!)

👉 不要一找到就停!

而是:

  • 继续往左缩(找左边界)

  • 继续往右缩(找右边界)


🎯 第二幕:找左边界(最左的2)

1、🧭 思路

👉 即使找到了 target,也要继续往左找!


2、🪄 过程演示

复制代码
1  2  2  2  3  4  4  5
   ↑

找到 2 后:

👉 继续往左缩!


3、🧾 C++模板(左边界)

复制代码
int left_bound(int a[], int n, int target)
{
    int left = 0, right = n - 1;
    int ans = -1;

    while(left <= right)
    {
        int mid = (left + right) / 2;

        if(a[mid] == target)
        {
            ans = mid;       // 记录答案
            right = mid - 1; // 继续往左找!
        }
        else if(a[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }

    return ans;
}

🎯 一句话总结

👉 找到后:

复制代码
往左缩!(right = mid - 1)

🎯 第三幕:找右边界(最右的2)

🧭 思路

👉 找到 target 后,继续往右找!


🧾 C++模板(右边界)

复制代码
int right_bound(int a[], int n, int target)
{
    int left = 0, right = n - 1;
    int ans = -1;

    while(left <= right)
    {
        int mid = (left + right) / 2;

        if(a[mid] == target)
        {
            ans = mid;      // 记录答案
            left = mid + 1; // 继续往右找!
        }
        else if(a[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }

    return ans;
}

🎯 一句话总结

👉 找到后:

复制代码
往右缩!(left = mid + 1)

⚖️ 对比总结:

类型 找到后怎么做
普通二分 直接 return
左边界 right = mid - 1
右边界 left = mid + 1

🧩 第四幕:记忆口诀

🎯 找左边界:

👉 "找到了别停,继续往左挤!"

🎯 找右边界:

👉 "找到了别停,继续往右挤!"


🚨 第五幕:常见坑!

❌ 坑1:找到就 return

👉 错!那只是普通二分!


❌ 坑2:忘记记录答案

👉 必须:

复制代码
ans = mid;

❌ 坑3:写反方向

👉 左边界:

复制代码
right = mid - 1 ✔

👉 右边界:

复制代码
left = mid + 1 ✔

🎁 第六幕:参考代码

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

int left_bound(int a[], int n, int target)
{
    int left = 0, right = n - 1;
    int ans = -1;

    while(left <= right)
    {
        int mid = (left + right) / 2;

        if(a[mid] == target)
        {
            ans = mid;
            right = mid - 1;
        }
        else if(a[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }

    return ans;
}

int right_bound(int a[], int n, int target)
{
    int left = 0, right = n - 1;
    int ans = -1;

    while(left <= right)
    {
        int mid = (left + right) / 2;

        if(a[mid] == target)
        {
            ans = mid;
            left = mid + 1;
        }
        else if(a[mid] < target)
        {
            left = mid + 1;
        }
        else
        {
            right = mid - 1;
        }
    }

    return ans;
}

int main()
{
    int a[] = {1,2,2,2,3,4,4,5};
    int n = 8;

    cout << "左边界:" << left_bound(a, n, 2) << endl;
    cout << "右边界:" << right_bound(a, n, 2) << endl;

    return 0;
}

🚀 总结:

👉 普通二分:找"有没有"

👉 左边界:找"最左"

👉 右边界:找"最右"


相关推荐
2401_879693872 小时前
C++跨平台开发实战
开发语言·c++·算法
浅念-2 小时前
Linux 基础命令与核心知识点
linux·数据结构·c++·经验分享·笔记·算法·ubuntu
旺仔.2912 小时前
C++ String 详解
开发语言·c++·算法
2301_816651222 小时前
模板代码跨平台适配
开发语言·c++·算法
m0_743470372 小时前
C++代码静态检测
开发语言·c++·算法
m0_738098022 小时前
C++中的代理模式实战
开发语言·c++·算法
俄城杜小帅2 小时前
C++线程异步和wpf中比较
java·c++·wpf
The_Ticker2 小时前
日股实时行情接口使用指南
java·经验分享·笔记·python·算法·区块链
靠沿2 小时前
【递归、搜索与回溯算法】专题一——递归
算法