C++ find 查找

在 C++ 中,如果我们想检查容器里是否存在某个元素,或者找出它排在什么位置,可以使用 <algorithm> 头文件提供的通用查找函数。常用的主要有 2 个:一个是按固定值查找的 std::find(),另一个是按自定义条件查找的 std::find_if()。

std::find() 按值查找

std::find() 用来在指定的 [first, last) 区间内,从左往右依次比对,查找第 1 个等于目标值的元素。

语法:

auto it = std::find(first, last, value);

说明:

std::find() 接收以下 3 个参数:

  • first(必选):查找范围的起始位置,比如 prices.begin(),包含在查找范围内。
  • last(必选):查找范围的结束边界,比如 prices.end(),实际查找的是 [first, last) 左闭右开区间。
  • value(必选):需要查找的目标值。

查找成功时,它会返回指向第 1 个匹配元素的迭代器;直到末尾都没找到时,则返回结束边界 last。

为什么 std::find() 不直接返回 true 或 false,而是返回一个迭代器呢?因为拿到迭代器之后,我们不仅能通过 it != end() 判断元素存不存在,找到目标后还能直接算出它的下标,或者通过 *it 修改该位置的值。

示例 1:查找元素并计算下标

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> prices = {18, 35, 50, 68};

    // 1. 查找存在的元素 50
    auto it1 = std::find(prices.begin(), prices.end(), 50);
    if (it1 != prices.end()) {
        auto idx = it1 - prices.begin();    // 计算元素下标
        std::cout << "找到 50,下标为:" << idx << '\n';
    }

    // 2. 查找不存在的元素 99
    auto it2 = std::find(prices.begin(), prices.end(), 99);
    if (it2 == prices.end()) {
        std::cout << "未找到 99\n";
    }
    return 0;
}

运行结果如下。

找到 50,下标为:2
未找到 99

分析:

在这个例子中,我们第 1 次查找整数 50 时,函数从头依次比对,在第 3 个位置找到 50 并返回指向它的迭代器 it1。

确认找到目标后,由于 prices.begin() 指向下标 0 处的 18,我们将两个迭代器相减 it1 - prices.begin()。算出两者中间隔了 2 个位置,正好得到下标 2。

第 2 次查找 99 时,直到末尾都没有匹配项,函数返回 prices.end(),程序进入 “未找到” 分支。需要注意,两个迭代器直接相减算下标只适用于 vector、array、deque,list 迭代器不支持直接相减。

注意 当 std::find() 没有找到目标元素时,返回的迭代器等于结束边界 end(),并不指向任何有效元素。因此在通过 *it 读取或修改元素之前,小伙伴们一定要先检查 it != 容器.end() 是否成立,千万不要直接解引用。

如果容器中存在重复元素,或者我们只想跳过前几个元素、在局部子区间内查找并修改元素,只需要调整传入的起止迭代器即可。下面我们来看一个例子。

示例 2:子区间查找与修改元素

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> nums = {10, 20, 30, 20, 40};

    // 从下标 2 开始往后查找 20
    auto it = std::find(nums.begin() + 2, nums.end(), 20);
    if (it != nums.end()) {
        *it = 200;    // 直接修改找到的元素
    }

    for (int n : nums) {
        std::cout << n << ' ';
    }
    std::cout << '\n';
    return 0;
}

运行结果如下。

10 20 30 200 40

分析:

nums.begin() + 2 表示将查找起点直接设在下标为 2 的元素 30 处。这样一来,排在下标 1 处的第 1 个 20 就被跳过了,最终找到的是位于下标 3 处的第 2 个 20,并通过 *it = 200; 直接把该位置的值改成了 200。

对于上面这个例子,小伙伴们可以试着把查找起点改回 nums.begin(),看看最终被修改成 200 的是哪一个位置。

std::find_if() 按条件查找

std::find() 只能查找 “等于某个固定值” 的元素。但如果我们想查找 “第 1 个大于 85 分的成绩”,或者在对象容器中查找 “编号为 1002 的图书”,也就是按自定义条件查找,就需要使用 std::find_if() 函数。

语法:

auto it = std::find_if(first, last, pred);

说明:

前两个参数 first 和 last 依然表示查找区间。第 3 个参数 pred 是一个自定义的判断函数或 Lambda 表达式,它接收当前正在检查的元素,并返回一个 bool 值。

std::find_if() 会从 first 开始,依次把区间内的每个元素传给 pred 检查。一旦遇到第 1 个让 pred 返回 true 的元素,它就会立即停止查找并返回指向该元素的迭代器。如果所有元素都不满足条件,则返回结束边界 last。

1. 按数值条件查找

我们先来看看如何使用 std::find_if() 配合 Lambda 表达式,找出容器中第 1 个满足大小条件的数值。

示例 3:查找首个大于 85 的分数

#include <algorithm>
#include <iostream>
#include <vector>

int main() {
    std::vector<int> scores = {72, 80, 92, 65, 95};

    // 查找第 1 个大于 85 的分数
    auto it = std::find_if(scores.begin(), scores.end(),
        [](int s) {
            return s > 85;
        });

    if (it != scores.end()) {
        std::cout << "首个大于 85 的分数:" << *it << '\n';
    }
    return 0;
}

运行结果如下。

首个大于 85 的分数:92

分析:

std::find_if() 从第 1 个元素 72 开始,依次把每个分数传给 Lambda 表达式检查。前面的 72 和 80 都不满足 s > 85,当检查到第 3 个元素 92 时,Lambda 返回 true,查找立即停止并返回指向 92 的迭代器。

虽然排在后面的 95 也大于 85,但因为 std::find_if() 只找第 1 个满足条件的元素,所以找到 92 后就不会再继续往后检查了。

2. 按结构体成员查找

在实际开发中,当 vector 里存放的是自定义的结构体或类对象时,结构体默认不支持直接使用 == 比对相等,因此无法直接调用 std::find()。此时使用 std::find_if() 按某个成员变量来匹配是最常用的做法。

示例 4:按编号查找结构体元素

#include <algorithm>
#include <iostream>
#include <string>
#include <vector>

struct Book {
    int id;
    std::string title;
    int price;
};

int main() {
    std::vector<Book> books = {
        {1001, "C++ 入门", 59},
        {1002, "数据结构", 68},
        {1003, "操作系统", 75}
    };

    // 查找编号 id 为 1002 的图书
    auto it = std::find_if(books.begin(), books.end(),
        [](const Book& b) {
            return b.id == 1002;
        });

    if (it != books.end()) {
        std::cout << it->title << ":" << it->price << " 元\n";
    }
    return 0;
}

运行结果如下。

数据结构:68 元

分析:

在这个例子中,Lambda 参数写成了常量引用 const Book& b,避免每次检查时复制整个结构体对象。匹配到第 2 本书后,函数返回指向该元素的迭代器 it,我们通过 it->title 和 it->price 就能直接读取书名与价格。

map 和 set 自带的 find() 方法

虽然 std::find() 可以用于大多数容器,但它的工作方式是 “从头到尾挨个比对”。对于 vector、array、list 这类顺序容器,使用 std::find() 是很自然的选择。

不过对于 map、set 这类关联容器,它们内部已经按照特定结构组织好了数据,并且自身就提供了专属的 find() 成员函数。尤其是 map 容器,调用它自带的 find() 方法可以直接按照 “键” 快速定位数据,比从头遍历高效得多。

示例 5:使用 map.find() 按键查找

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> scores = {
        {"Jack", 85},
        {"Lucy", 95},
        {"Tony", 90}
    };

    auto it = scores.find("Lucy");
    if (it != scores.end()) {
        std::cout << it->first << ":" << it->second << '\n';
    }
    return 0;
}

运行结果如下。

Lucy:95

分析:

scores.find("Lucy") 表示直接在 map 中按键查找 "Lucy":找到时返回指向该键值对的迭代器;找不到时返回结束边界 scores.end()。通过迭代器访问时,it->first 表示键 "Lucy",it->second 表示对应的值 95。

三种常见查找方式对比

在 C++ 开发中,面对不同的数据类型,对应的查找写法与返回值规则有所不同。平时写代码时,小伙伴们可以按下面 3 种情况来区分:

顺序容器:像 vector、array、list 本身没有 find() 成员函数,查找元素时使用 <algorithm> 中的 std::find() 或 std::find_if(),找不到时返回 end() 迭代器。

关联容器:像 map、set、unordered_map 优先调用容器自带的成员函数 find(),查找速度比从头遍历快得多,找不到时同样返回 end() 迭代器。

字符串:在字符串查找时,std::string 提供了专属的 find() 成员函数,它返回子串所在的下标数字,找不到时返回 std::string::npos 而不是迭代器。

上一篇:

下一篇:

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号