• 使用二分查找来判断一个有序序列中是否存在特定元素


    你们玩过猜数字的游戏吗?你的朋友心里想一个 (1000) 以内的正整数,你可以给出一个数字 (x),你朋友只要回答 “比 (x) 大” 或者 “比 (x) 小” 或者 “猜中”,你能保证在 (10) 次以内猜中吗?运气好只要一次就能猜中。

    开始猜测是 (1)(1000) 之间,你可以先猜 (500) ,运气好可以一次猜中;如果答案比 (500) 大,显然答案不可能在 (1)(500) 之间,下一次猜测的区间变为 (501)(1000);如果答案比 (500) 小,那答案不可能在 (500)(1000) 之间,下一次猜测的区间变为 (1)(499)。只要每次都猜测区间的中间点,这样就可以把猜测区间缩小一半。由于 (frac{1000}{2^{10}} lt 1),因此不超过 (10) 次询问区间就一个缩小为 (1),答案就会猜中了,这就是 二分查找 的基本思想 ——

    每一次使得可选范围缩小一半,最终使得范围缩小为一个数,从而得出答案。

    假设问的范围是 (1)(n),根据 (frac{n}{2^x} le 1)(x ge ext{log}_2n),所以我们只需要问 (sim log_2n) 次就可以查找到元素的位置(或者得到元素不存在)。

    需要注意的是使用二分查找有一个重要的前提,就是 有序性 。接下来通过一个例子来入门二分查找的应用。

    例1 找数。

    题目描述:
    给一个长度为 (n) 的单调递增的正整数序列,即序列中每一个数都比前一个数大。有 (m) 个询问,每次询问一个 (x) ,问序列中最后一个小于等于 (x) 的数是什么?

    输入:
    第一行两个整数 (n,m(1 le n,m le 1000))
    第二行 (n) 个整数用于表示这个序列。
    接下来 (m) 行每行包含一个整数,用于表示询问的 (x)

    输出:
    输出共 (m) 行,对于每一次询问的 (x) ,输出序列中值为 (x) 的元素的位置;如果序列中不存在置为 (x) 的元素,输出单独的一行 (-1)

    样例输入:

    5 3
    1 3 5 7 9
    3
    4
    5
    

    样例输出:

    2
    -1
    3
    

    问题分析

    我们用 (L) 表示询问的区间的左边界,用 (R) 表示询问的区间的右边界, ([L,R]) 组成询问区间。
    一开始 (L=1,R=n)。序列已经按照升序排好,保证了二分的有序性。

    每一次二分,我们这样来做:

    1. 取区间中间值 (mid = lfloor frac{L+R}{2} floor)
    2. 如果 (a[mid] = x),则说明找到了 (x),输出 (x) 对应的位置 (mid) 并结束查找;
    3. 如果 (a[mid] gt x),由于序列是升序排列,所以区间 ([mid,R]) 都可以排除,修改右边界 (R = mid-1)
    4. 如果 (a[mid] lt x),由于序列是升序排列,所以区间 ([L,mid]) 都可以排除,修改左边界 (L = mid+1)

    重复执行二分直到 (L gt R)(因为最终循环结束时一定是 (L = R+1))。

    示例代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    int n, m, a[1001], x;
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; i ++) cin >> a[i];
        while (m --) {
            cin >> x;
            int L = 1, R = n, res = -1;
            while (L <= R) {
                int mid = (L + R)/2;
                if (a[mid] == x) {
                    res = mid;
                    break;
                }
                else if (a[mid] > x) R = mid-1;
                else L = mid+1;
            }
            cout << res << endl;
        }
        return 0;
    }
    
  • 相关阅读:
    LinkLabel控件使用
    读取mysql代码片段
    设置点风格
    C# List 用法
    图片焦点图切换效果
    dreamweaver 泛泛之谈
    js 之for..in、表单及事件触发
    实现省份查询 功能
    input类主要是
    js (1)
  • 原文地址:https://www.cnblogs.com/quanjun/p/12713316.html
Copyright © 2020-2023  润新知