题解:洛谷 P2249 查找

【题目来源】

洛谷:P2249 【深基13.例1】查找 - 洛谷

【题目描述】

输入 \(n\) 个不超过 \(10^9\) 的单调不减的(就是后面的数字不小于前面的数字)非负整数 \(a_1,a_2,\dots,a_n\),然后进行 \(m\) 次询问。对于每次询问,给出一个整数 \(q\),要求输出这个数字在序列中第一次出现的编号,如果没有找到的话输出 \(−1\)

【输入】

第一行 \(2\) 个整数 \(n\)\(m\),表示数字个数和询问次数。

第二行 \(n\) 个整数,表示这些待查询的数字。

第三行 \(m\) 个整数,表示询问这些数字的编号,从 \(1\) 开始编号。

【输出】

输出一行,\(m\) 个整数,以空格隔开,表示答案。

【输入样例】

11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6

【输出样例】

1 2 -1

【核心思想】

  1. 问题分析:给定一个长度为 \(n\)单调不减(非降序)整数序列,进行 \(m\) 次询问,每次询问一个整数 \(q\),要求输出 \(q\) 在序列中第一次出现的位置(从 \(1\) 开始编号),若不存在则输出 \(-1\)。由于序列有序且询问较多,需要利用单调性进行快速查找,本质是二分查找的经典应用。

  2. 算法选择

    • 整数二分查找:利用序列的非降序性质,通过不断缩小搜索区间来定位目标。对于每次查询,采用“左闭右闭”或“左闭右开”的二分模板,找到第一个 \(\ge q\) 的位置,然后检查该位置的值是否等于 \(q\)
    • C++ STL 的 lower_bound(备选):可直接使用 lower_bound 找到第一个不小于 \(q\) 的迭代器,再判断其值。但为练习二分,手写实现更佳。
  3. 关键步骤

    • 读入与存储:读取 \(n\)\(m\),以及非降序数组 \(a[1..n]\)
    • 二分查找函数 find(q)
      • 初始化左边界 \(l=1\),右边界 \(r=n\)(闭区间 \([l, r]\))。
      • \(l < r\) 时,计算 \(mid = \lfloor (l+r)/2 \rfloor\)
        • \(a[mid] \ge q\),则第一个 \(\ge q\) 的位置可能在 \([l, mid]\) 中,令 \(r = mid\)
        • 否则,\(a[mid] < q\),则应在 \([mid+1, r]\) 中查找,令 \(l = mid+1\)
      • 循环结束后,\(l\) 即为第一个 \(\ge q\) 的位置。
      • \(a[l] == q\),返回 \(l\);否则返回 \(-1\)
    • 处理询问:对每个查询 \(q\),调用 find(q) 并输出结果,用空格分隔。
  4. 时间/空间复杂度

    • 时间复杂度:\(O((n+m)\log n)\),每次二分 \(O(\log n)\),共 \(m\) 次查询。
    • 空间复杂度:\(O(n)\),存储数组。
  5. 二分查找的核心思想

    • 单调性利用:由于数组非降序,判断 \(a[mid]\)\(q\) 的关系即可确定目标在左半还是右半,每次排除一半元素。
    • 重复元素处理:通过条件 \(a[mid] \ge q\) 将右边界 \(r\) 左移,从而找到第一个 \(\ge q\) 的位置,确保即使有重复元素也能定位到首次出现。
    • 边界条件:循环结束后,\(l\) 指向第一个 \(\ge q\) 的位置,但需验证该位置的值是否等于 \(q\),否则说明 \(q\) 不存在。
    • 适用于有序序列的快速查找问题,是算法竞赛中最基础的二分模板。

【解题思路】

【算法标签】

普及- #整数二分

【代码详解】

#include <bits/stdc++.h>
using namespace std;

// 全局变量:
// n: 数组长度
// m: 查询次数
// q: 当前查询值
// tmp: 临时变量(未使用)
// a[1000005]: 存储有序数组
int n, m, q, tmp;
int a[1000005];

/**
 * 二分查找函数
 * @param q 要查找的值
 * @return 值在数组中的位置(从1开始),未找到返回-1
 */
int find(int q)
{
    int l = 1, r = n;  // 初始化左右边界

    while (l < r)
    {
        int mid = l + (r - l) / 2;  // 计算中间位置,防止溢出

        // 调整搜索范围
        if (a[mid] >= q)
        {
            r = mid;  // 搜索左半部分
        }
        else
        {
            l = mid + 1;  // 搜索右半部分
        }
    }

    // 检查是否找到目标值
    if (a[l] == q)
    {
        return l;
    }
    else
    {
        return -1;
    }
}

int main()
{
    // 输入数组长度和查询次数
    cin >> n >> m;

    // 输入有序数组
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 处理每个查询
    for (int i = 1; i <= m; i++)
    {
        cin >> q;  // 输入查询值
        cout << find(q) << " ";  // 输出查找结果
    }

    return 0;
}
// 使用lower_bound再做一遍
#include <bits/stdc++.h>
using namespace std;

// 全局变量:
// n: 数组长度
// m: 查询次数
// q: 当前查询值
// mark: 存储查找结果的位置
// a[1000005]: 存储有序数组
int n, m, q, mark;
int a[1000005];

int main()
{
    // 输入数组长度和查询次数
    cin >> n >> m;

    // 输入有序数组(从索引1开始存储)
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 处理每个查询
    for (int i = 1; i <= m; i++)
    {
        cin >> q;  // 输入查询值

        // 使用lower_bound查找第一个不小于q的元素位置
        mark = lower_bound(a + 1, a + n + 1, q) - a;

        // 检查是否找到确切值
        if (a[mark] == q)
        {
            cout << mark << " ";  // 输出找到的位置
        }
        else
        {
            cout << "-1 ";  // 未找到输出-1
        }
    }

    return 0;
}
#include <bits/stdc++.h>
using namespace std;

int n, m, q, tmp;
int a[1000005];

// 二分查找函数,查找目标值q
int find(int q)
{
    int l = 0, r = n + 1;  // 开区间 (0, n+1),实际有效数据是a[1]到a[n]

    // 二分查找循环
    while (l + 1 < r)  // 当区间长度大于1时继续
    {
        int mid = (l + r) / 2;  // 计算中间位置

        if (a[mid] >= q)  // 如果中间值大于等于目标值
        {
            r = mid;  // 向左收缩,r指向可能的第一个≥q的位置
        }
        else
        {
            l = mid;  // 向右收缩
        }
    }

    // 循环结束时,l是最后一个小于q的位置,r是第一个大于等于q的位置
    if (a[r] == q)  // 检查找到的位置是否等于q
    {
        return r;  // 返回下标
    }
    else
    {
        return -1;  // 未找到
    }
}

int main()
{
    cin >> n >> m;  // 输入数组长度n和查询次数m

    // 输入数组,从1开始存储
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i];
    }

    // 处理m个查询
    for (int i = 1; i <= m; i++)
    {
        cin >> q;  // 输入要查找的值
        cout << find(q) << " ";  // 输出查找结果
    }

    return 0;
}

【运行结果】

11 3
1 3 3 3 5 7 9 11 13 15 15
1 3 6
1 2 -1
posted @ 2026-09-02 21:41  团爸讲算法  阅读(15)  评论(0)    收藏  举报