二分查找理论Binary Search代码实现及P1102 A-B 数对

二分查找

在一个1~n的递增序列中,怎么用二分查找数字x的下标?

在一个没用重复元素的递增序列中,用二分查找的方法最多需要查找log(n)次,也就是在一个区间内l~r不断的选取中间下标(mid),直到mid等于需要查找的数。这种查找方法在最坏的情况下能以很少的执行次数找到数。

而C++的二分查找实现是这样的:

  • 对于一个区间有左指针 l 和右指针 r
  • 在所有数字代表的区间,尝试中间下标 (mid)。
  • 如果中间下标对应的数字就是答案则输出答案。
  • 如果数字太小,则继续处理右区间。
  • 如果数字太大,则继续处理左区间。

程序中的重要变量:

  • a[100010] 输入的有序递增序列
  • n 序列中数字的个数
  • x 要查询的数字
  • l,r,mid 左指针,右指针,中间下表
  • ans 答案,中间下标等于x时下标的数
#include 
int a[1000010];
using namespace std;
int main() {
    int n, x;
    cin >> n;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    cin >> x;
    int l = 1, r = n, ans = -1; 
    // 左指针是第一个元素的下标,右指针是最后一个元素的下标
    while (l<=r) {     // 只要左指针不大于右指针,就不断地循环。
        int mid = (l+r)/2; // 中间下标是左右指针的平均值
        if (a[mid] == x) {  // 如果中间下标对应的值刚好是要找的那个
            ans = mid;      // 记录答案并返回
            break;
        } else if (a[mid]>x) // 如果中间下标对应的值比要找到更大
            r = mid-1;  // 右指针缩到中间下标的左边一个
        else                // 如果中间下标对应的值比要找到小
            l = mid+1;  // 左指针缩到中间下标的右边一个
    }
    cout << ans; // 输出答案
}

查找第一个出现的位置时,可以这样执行

 while (l <= r) {
        int mid = (l + r) / 2;
        if (a[mid] == x) {  // 如果中间的数字等于要找的
            ans = mid;     // 记录答案位置
            r = mid-1;     // 局限在左区间
        } else if (a[mid] > x) // 如果中间数字大于要找的
            r = mid-1;     // 局限在左区间
        else                // 如果中间数字小于要找的
            l = mid+1;     // 局限在右区间
    }

查找最后一个出现的位置时,可以这样执行

while (l <= r) {
        int mid = (l + r) / 2;
        if (a[mid] == x) {  
            ans = mid;   
            l = mid+1;    
        } else if (a[mid] > x)  
            r = mid-1;     
        else                
            l = mid+1;     
    }

P1102 A-B 数对

题面

题目背景

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

题目描述

给出一串正整数数列以及一个正整数 C,要求计算出所有满足 A−B=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个正整数 N,C。

第二行,N 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 A−B=C 的数对的个数。

输入输出样例

输入 #1

4 1
1 1 2 3

输出 #1

3

题解

代码

#include 
int a[200010], n, c;
long long ans = 0;
using namespace std;

int findx(int k) { // 找到第一次出现的位置
    int l = 1, r = n, ans = -1;
     while (l <= r) {
        int mid = (l + r) / 2;
        if (a[mid] == k) {  // 如果中间的数字等于要找的
            ans = mid;     // 记录答案位置
            r = mid-1;     // 局限在左区间
        } else if (a[mid] > k) // 如果中间数字大于要找的
            r = mid-1;     // 局限在左区间
        else                // 如果中间数字小于要找的
            l = mid+1;     // 局限在右区间
    }
    return ans;
}
int findy(int k) { // 找到最后一次出现的位置
    int l = 1, r = n, ans = -1;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (a[mid] == k) {  
            ans = mid;   
            l = mid+1;    
        } else if (a[mid] > k)  
            r = mid-1;     
        else                
            l = mid+1;     
    }
    return ans;
}
int main() {
    cin >> n >> c;
    for(int i = 1; i <= n; i++)
        cin >> a[i];
    sort(a + 1, a + n + 1);
    for(int i = 1; i <= n; i++) {
        int x = findx(a[i] + c);
        int y = findy(a[i]+c);
        if(x == -1) continue;
        ans += y-x+1; 
    }
    cout << ans; // 输出答案
}

你可能感兴趣的:(算法,算法,c++,数据结构)