首页 > 技术文章 > 八、二分查找

hbhszxyb 2020-01-24 14:13 原文

二分查找常用与一个有序序列或一个具有单调性问题中。

8.1 编程思路:

  1. 设有一数组 \(a[n]\) ,数组中的元素按值从小到大排列有序。用变量 \(low, high\)\(mid\) 分别指示待查元素所在区间的下界、上界和中间位置。初始时,\(low=0,high=n-1\) ,查找 \(x\) 是否在序列中。
  2. \(mid = (low+ high) /2\) 。(建议写成 \(mid = low+(high-low)/2\),可以避免数过大越界)
  3. 比较给定值 \(x\)\(a[mid]\) 值的大小:
    • \(a[mid] == x\) ,则查找成功,结束查找;
    • \(a[mid]> x\) ,则表明给定值 \(x\) 只可能在区间 \(low \sim mid-1\) 内,修改检索范围。令 \(high=mid-1\)\(low\) 值保持不变;
    • \(a[mid]< x\) ,则表明给定值 \(x\) 只可能在区间 \(mid+1\sim high\) 内,修改检索范围。令 \(low=mid+1,high\) 值保持不变。
  4. 比较当前变量 \(low\)\(high\) 的值,若 \(low≤high\) ,重复执行第1、2两步,若 \(low>high\) ,表明数组中不存在待查找的元素,查找失败。
  • eg : a[]={10,14,21,38,45,47,53,81,87,99},查找47

8.2 代码实现:

int binarysearch(int a[],int num,int x){
    int left=0,right=num-1;//查找区间[left,right]
    while(left<=right){//跳出循环时left=right+1
        int mid = left+(right-left)/2;//避免(left+right)溢出
        if(a[mid] == x)return mid;
        if(a[mid] < x)
            left = mid + 1;
        else
            right = mid - 1;
    }
    return -1;//执行到这一步说明个序列中不存在x
}
  • 上面算法在查找一个数是否存在很高效,但存在局限性。
  • 比如说给你有序数组\(a[]={1,2,2,2,3}, x=2\),此算法返回的索引是 \(2\),没错。但是如果我想得到 \(x\) 的左侧边界,即索引 \(1\),或者我想得到 \(x\) 的右侧边界,即索引 \(3\) ,那我们该如何处理。

8.2.1 左侧边界代码实现

int binarysearch(int a[],int num,int x){//查找数组a,[0,num-1]中比x小的个数
    int left=0,right=num;//查找区间[left,right)
    while(left<right){//跳出循环时left=right
        int mid = left+(right-left)/2;//避免(left+right)溢出
        if(a[mid] >= x) //求左边界我们尽量遇到==x的时候右边界往左移
            right = mid;//注意是左闭右开区间右区间缩小不需要减一
        else
            left = mid + 1;//新的区间不包含a[mid]
    }
    return left;//跳出循环时left指向第一个x的位置
}

8.2.2 右侧边界代码实现

int binarysearch(int a[],int l,int r,int x){//查找数组a,[l,r]中元素x的位置
    int left=0,right=num;//查找区间[left,right)
    while(left<right){//跳出循环时left=right
        int mid = left+(right-left)/2;//避免(left+right)溢出
        if(a[mid] <= x) //求右边界我们尽量遇到==x的时候左边界往右移
            left = mid+1;//注意是左闭右开区间
        else
            right = mid;//新的区间不包含a[mid]
    }
    return left-1;//注意left要减一,跳出循环时left指向最右x的下一个位置
}

推荐阅读