数据结构 二分查找1

Posted mr-stn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构 二分查找1相关的知识,希望对你有一定的参考价值。

包括以下内容:

  1. 二分查找key在数组中的位置
  2. 二分查找数组中第一个大于或等于key的位置
  3. 二分查找数组中第一个大于key的位置

变量解释:int[] arr1; 记录查找表,所有元素都是唯一的

                  int[] arr2; 记录查找表,元素不唯一

 

测试用例:

  0 1 2 3 4 5 6 7 8
arr1[] 01 05 07 09 10 15 18 22 25
arr2[] 01 01 07 07 15 15 15 22 25

 

 

 

一. 查找key在数组中的位置, 查找不成功则返回-1;

迭代实现:

1 int binary_search1(int arr[], int low, int high, int key){
2     while(low<=high){
3         int mid = low + (high-low)/2;
4         if(arr[mid]<key) low=mid+1;
5         else if(arr[mid]>key) high=mid-1;
6         else return mid;
7     }
8     return -1;
9 }

 

归实现:

1 int binary_search(int arr[], int low, int high, int key){
2     int mid = low+(high-low)/2;
3     if(low>high) return -1;
4     if(arr[mid]<key)  return binary_search(arr, mid+1, high, key);
5     if(arr[mid]>key)  return binary_search(arr, low, mid-1, key);
6     return mid;
7 }

 

这里对递归实现,做一定的解释:

首先这个函数的功能是在查找表arr1[]中查找key所在的位置, 如果查找成功则返回所在位置, 否则返回-1,标志未查找成功; 二分查找的前提的查找表有序,这里以查找表递增为例实现二叉查找;

每次调用函数,是对整个数组查找, 需要查找表数组arr, 查找范围的下限low,和上限high, 以及要查找的值key; 为了统一,我们这里把上限+1,后面做解释

二分查找的思路:先拿key和查找表最中间的值比较:

  1.如果比中间值大,则key只可能在查找表的右半边, 在查找表的右边进行二叉查找

  2. 如果key比中间的值小,那么key只可能在查找表的左半边,在查找表的左边进行二叉查找

  3.如果相等, 则找到key所在key的位置 

以查找7为例, 

0 1 2 3 4 5 6 7 8 9  
1 5 7 9 10 15 18 22 25    
low       mid         high 7比10大,在查找表左边进行二叉查找, high=mid-1
low mid   high             7比5大,在查找表的右边进行二叉查找, low=mid+1
  low mid high             找到7,返回7所在位置

 

 

 

 

有几个需要注意的地方:

  1. while的循环条件:应该是low<=high,  而不是low<high;
  2. 每次循环如果没有找到key所在的位置,要修改low=mid-1,或者high=mid+1; 不能是low=mid, 或者是high=mid,否则会导致死循环
  3. 只适用于递增的查找表

二叉查找,相当于在一颗平衡二叉树中进行查找, 平衡二叉树的高度h=[logn]+1, n是查找表的长度, 所以使用二叉树查找值得时候,在O(logn)的时间内就能找到所需要查找的值,及时查找失败, 比较次数也不会超过查找表对于的平衡二叉树的深度

二叉查找并不能保证比顺序查找的更优 , 对于查找概率相同的的key来说二叉树更优,当查找表前面的值查找频率较高的时候,顺序查找的效率可能比二叉查找更优

 

二. 查找表中第一个大于等于key的位置

1 int lower_bound(int arr[], int low, int high, int key){
2     while(low<high){
3         int mid = low + (high-low)/2;
4         if(key>arr[mid]) low=mid+1;
5         else  high=mid;
6     }
7     return low;
8 }

函数解释:在不减的查找表里面找第一个大于或者等于key的位置, 如果key在查找表中不存在, 返回的是key在数组中应该在的位置, 比如在1,3,5中查找2, 返回的值是1, 意思就是如果查找表中有2这个值,他的位置应该在1这个位置, 如果key在查找表中, 返回的值就是key所在的位置; 解释一下上面的上限为什么要+1,任然以1,3,5为例,如果在表中查找8,传入2作为上限, 返回的值就是2,但这是错误的,8的位置应该在3;所以上限加一在于解决查找值key比查找表中所有元素还大的情况, 而对其他值得查找不会有影响;

这个函数和上面的函数很相似,整体的框架类似, 但是存在一些细微的差别:

  1. while的循环判断条件不同
  2. high的修改条件不同
  3. 返回值不同

下面对这三点做出解释

  1. 在二分查找时,有一个隐含条件是查找值key必须在查找表中,当low==high以及确定了一个唯一的位置,但是需要验证该位置的值是否等于key;但是在这个函数中,我们要找的是第一个大于或等于key的位置,如果key存在于查找表中,那么当low==hight时,查找表的值一定为key,不存在也没关系,该位置即为key在查找表中应该所在的位置
  2. 因为我们要找的是第一个大于或者等于key所在位置,那么当arr[mid]<key时, key的位置肯定在mid的右边,不可能在mid上,因而low=mid+1; 当arr[mid]>key时,key的位置可能在mid的左边也可能就在mid上,因为要找的是大于或等于key的值, 当arr[mid]==key的时候, key的位置就在mid上, 让high=mid,经过几次迭代就能让low==high,从而退出循环, 可以发现后面两种情况是可以合并的, 将其合并在一起,让代码精简一些
  3. 如果你手写实现一下该迭代过程,就会发现终止循环的的情况一定是low==high,这种情况下确定了一个唯一的位置, 返回low和high其实都一样;因为每一次迭代low的最大值即为low=[(low+high)/2]+1, 改值一定是不大于high的当low等于high退出循环,当low小于high,进行下一次迭代

以查找不存在的8为例, 正确的位置应该在4;

0 1 2 3 4 5 6 7 8 9  
1 1 7 7 15 15 15 23 25    
low        mid          high 8小于15,在mid左边进行查找, high=mid
 low   mid   high           8大于7,在mid右边进行查找,low=mid+1
      low, mid high           8大于7,在mid右边进行查找,low=mid+1
        low, high           low<high条件不满足,返回low找到key应该在的位置

 

 

 

 

 

 

三.查找第一个大于key所在的位置

int upper_bound(int arr[], int low, int high, int key){
    while(low<high){
        int mid = low + (high-low)/2;
        if(key>=arr[mid]) low=mid+1;
        else high=mid;
    }
    return low;
}

路和查找第一个大于或等于key的位置的函数一样, 这里只需要把等于的条件放在左边即可; 这里不再做解释

lower_bound和upper_bound组合使用能很方便的得到查找表中所有值等于key的左闭右开区间

通过相同的思路能能实现找到第一个小于或等于key的位置, 第一个等于key的位置;

 

以上是关于数据结构 二分查找1的主要内容,如果未能解决你的问题,请参考以下文章

浅入深出谈二分!二分查找?

数据结构十大排序算法+二分查找(左程云 左神版 全文2W字+ word很大,你忍一下~~)

(王道408考研数据结构)第七章查找-第二节2:二分查找及其判定树

Day589.二分查找(非递归) -数据结构和算法Java

二分查找

44期盘点那些必问的数据结构算法题之二分查找算法