vlambda博客
学习文章列表

【经典算法——查找】二分查找


 Happy Halloween 


二分查找

 Happy Halloween


二分查找又称为折半查找,仅适用于事先已经排好序的顺序表。其查找的基本思路:首先将给定值K,与表中中间位置元素的关键字比较,若相等,返回该元素的存储位置;若不等,这所需查找的元素只能在中间数据以外的前半部分或后半部分中。然后在缩小的范围中继续进行同样的查找。如此反复直到找到为止。算法如下:


1 template<typename T>
2 int BinarySearch(vector<T> &data, T key) {
3     int low = 0, high = data.size() - 1;
4     while (low <= high) {
5         int mid = low + (high - low) / 2;
6         if (data[mid] == key) {
7             return mid;
8         } else if (data[mid] > key) {
9             high = mid - 1;
10         } else {
11             low = mid + 1;
12         }
13     }
14
15     return -1;
16 }


因为二分查找需要方便地定位查找区域,所以适合二分查找的存储结构必须具有随机存储的特性。因此,该查找方法仅适合于线性表的顺序存储结构,不适合链式存储结构,且要求元素按关键字有序排列。


判定树:

二分查找的过程可以用下图表示,称为判定树。树中每个圆形节点表示一个纪录,节点中的值表示为该记录的关键字值:树中最下面叶节点都是方形的,它表示查找不成功的情况。从判定树中可以看出,查找成功时查找的查找长度为从根节点到目的节点的路径上的节点数,而查找不成功时的查找长度为从根节点到对应失败节点的父节点的父节点路径上的节点数;每个节点值均大于其左子节点值,且均小于右子节点值。若有序序列有n个元素,这对应的判定树有n个圆形的非叶节点和n+1个方形的叶节点。

上图中,n个圆形节点(代表有序序列有n个元素)构成的树的深度与n个节点完全二叉树的深度(高度)相等,均为

⌊log2n⌋+1或⌈log2(n+1)⌉。二分查找的时间复杂度为O(log2N),比顺序查找的效率高。由上述分析可知,用二分查找到给定值或查找失败的比较次数最多不会超过树的高度。查找成功与不成功,最坏的情况下,都需要比较⌊log2n⌋+1次。

 Happy Halloween 


    二分查找算法是在有序数组中用到的较为频繁的一种算法,在未接触二分查找算法时,最通用的一种做法是,对数组进行遍历,跟每个元素进行比较,其时间为O(n).但二分查找算法则更优,因为其查找时间为O(lgn),譬如数组{1, 2, 3, 4, 5, 6, 7, 8, 9},查找元素6,用二分查找的算法执行的话,其顺序为:
    1.第一步查找中间元素,即5,由于5<6,则6必然在5之后的数组元素中,那么就在{6, 7, 8, 9}中查找,
    2.寻找{6, 7, 8, 9}的中位数,为7,7>6,则6应该在7左边的数组元素中,那么只剩下6,即找到了。

    二分查找算法就是不断将数组进行对半分割,每次拿中间元素和goal进行比较。


#include <iostream>
using namespace std;

//二分查找
int binary_search(int* a, int len, int goal);

int main()
{
    const int LEN = 10000;
    int a[LEN];
    for (int i = 0; i < LEN; i++)
        a[i] = i - 5000;
    int target = 0;
    int index = binary_search(a, LEN, target);

    if (index != -1)
        cout<<target<<"在数组中的下标为"<<index<<endl;
    else
        cout<<"不存在"<<target<<endl;
    return 0;
}

int binary_search(int* a, int len, int goal)
{
    int low = 0;
    int high = len -1;
    while (low <= high)
    {
        int middle = (high - low) / 2 + low; // 直接使用(high + low) / 2 可能导致溢出
        if (a[middle] == goal)
            return middle;
        //在左半边
        else if (a[middle] > goal)
            high = middle - 1;
        //在右半边
        else
            low = middle + 1;
    }
    //没找到
    return -1;
}

 Happy Halloween 


据说10个程序员,有九个写不对二分查找函数。
《编程珠玑》的作者Jon Bentley曾在贝尔实验室做过一个实验,即给一些专业的程序员几个小时的时间,用任何一种语言编写二分查找程序(写出高级伪代码也可以),结果参与编写的一百多人中:90%的程序员写的程序中有bug。
在查看参考程序前,请自行写个二分查找算法,看看自己是否属于那90%的那一部分人。
二分查找即在已排序数组中查找给定数。
给定二分查找算法接口函数如下:
int binarySearch(int A[],int n,int key);
输入:已排序的数组A,数组长度n,待查找的数key
输出:待查找数字key在数组A中的下标,如果没查找到,则返回-1


一个参考程序:

  1. int binarySearch(int A[],int n,int key)

  2. {

  3.     int left = 0,right = n-1;

  4.     int mid;

  5.     while(left <=right){

  6.         mid  = (left+right)/2;

  7.         if(A[mid] == key)

  8.             return mid;

  9.         else if(A[mid] < key)

  10.             left =  mid+1;

  11.         else

  12.             right = mid-1;

  13.     }

  14.     return -1;

  15. }

 Happy Halloween 


攒一口袋星星吧

扫码关注领取更多学习资料哦

 Happy Halloween