zoukankan      html  css  js  c++  java
  • 二分查找细则讨论

    二分查找细则讨论

             二分查找有两种实现方式:非递归和递归。我们首先给出非递归的实现,然后对其中的细则进行讨论。之后,我们再讨论递归实现的细则。

    一、非递归实现

             这里我们假设待查找序列是有序且互异的。

    #include <iostream>
    #include <vector>
    using namespace std;
    
    void nonrec_binary(const vector<int>& arr, int n, int& pos)
    {
        pos = -1;
        assert(arr.size() > 0);
        int left = 0, right = arr.size() - 1, middle = 0;
        while (left <= right) // 细则1
        {
            middle = (left + right) / 2; // 细则2
            if (n == arr[middle])
            {
                pos = middle;
                break;
            }
            else if (n > arr[middle])
            {
                left = middle + 1; // 细则3
            }
            else
            {
                right = middle - 1; // 细则4
            }
        }
        return;
    }
    
    int main()
    {
        int a[] = {2, 4, 5, 8, 9, 10, 13, 16, 18, 19};
        vector<int> arr(a, a+sizeof (a) / sizeof (*a));
        for (vector<int>::size_type i = 0; i != arr.size(); ++i)
        {
            cout << arr[i] << ' ';
        }
        cout << endl;
        int n = 9, pos = -1;
        nonrec_binary(arr, n, pos);
        cout << n << ':' << pos << endl;
    
        n = 12, pos = -1;
        nonrec_binary(arr, n, pos);
        cout << n << ':' << pos << endl;
        
        system("PAUSE");
        return 0;
    }

             细则讨论:

    1) 细则1

    while (left <= right)

             这里是 left <= right,而不是 left < right。举个例子,如果序列中只有一个元素,则left=0,right=0,如果n=arr[0],二分查找的结果pos应为0,但是如果是while(left < right)则无法进入循环,程序运行结果pos为-1,导致bug。

    2) 细则2

    middle = (left + right) / 2;

             left、right、middle均为int型,所以除法/操作得到的数只是取整,如果left=0,right=9,则middle为4,而非4.5。

    3) 细则3

    left = middle + 1;

             if (n > arr[middle])则left = middle + 1; 而不是 left = middle。因为,这是根据细则2讨论的整形除法,如果arr为{3, 5},待查找元素为5,正常来说结果pos应为为1。但是如果left=middle;,在每一次循环中,left一直为0,middle一直为0,所以导致了死循环,最终程序异常终止。

    4) 细则4

    right = middle – 1

    if (n < arr[middle])则right = middle – 1, 而不是 right = middle。细则4与细则3并不一样,如果写成right=middle并不程序的运行效果,因为整数整除对细则4的影响并不像细则3那么严重。但是由于 n < arr[middle] 了,虽然 right = middle; 可以照常工作,arr[middle] 已可以不在考虑范围内,所以最好写为 right = middle – 1,这样既可以提高效率,也可以与细则3表现一致。

    5) 细则5

    程序开头的部分并没有检测待查找元素n与arr[0]、arr[arr.size()-1]的大小关系,如果进行检查,如果n < arr[0] || n > arr[arr.size()-1] 则返回 pos = -1,如果 n == arr[0],则 pos = 0,如果 n == arr[arr.size() - 1],则 pos = arr.size() – 1。如果都不符合上面的条件,则进入 while 循环。

    其实没必要进行前面的检查,因为不管前面检查与否,while 循环都会考虑到前面的所有因素,细则1和 if (n == arr[middle]) break; 会涵盖n < arr[0] || n > arr[arr.size()-1]的情况,细则2会涵盖n == arr[arr.size() - 1],则 pos = arr.size() – 1的情况。

    在有先验知识的情况下,可以决定是否要加上前面的判断情况:如果进行大量查找的情况下,且n不经常出现于序列中或n主要为最大或最小元素时,则进行前面的判断可以提高一定的效率,否则没有必要加上这些判断条件。

    二、递归实现

    #include <iostream>
    #include <vector>
    using namespace std;
    
    void rec_binary(const vector<int>& arr, int left, int right, int middle, int n, int& pos) // 细则1
    {
        if (left > right) // 细则2
        {
            pos = -1;
            return;
        }
        else if (n == arr[middle])
        {
            pos = middle;
            return;
        }
        else if (n > arr[middle])
        {
            rec_binary(arr, middle+1, right, (middle+1+right)/2, n, pos); // 细则3
        }
        else
        {
            rec_binary(arr, left, middle-1, (left+middle-1)/2, n, pos); // 细则4
        }
    }
    
    int main()
    {
        int a[] = {2, 4, 5, 8, 9, 10, 13, 16, 18, 19};
        vector<int> arr(a, a+sizeof (a) / sizeof (*a));
        for (vector<int>::size_type i = 0; i != arr.size(); ++i)
        {
            cout << arr[i] << ' ';
        }
        cout << endl;
        int n = 9, pos = -1;
        rec_binary(arr, 0, arr.size()-1, (0+arr.size()-1)/2, n, pos); // 细则5
        cout << n << ':' << pos << endl;
    
        n = 12, pos = -1;
        rec_binary(arr, 0, arr.size()-1, (0+arr.size()-1)/2, n, pos);
        cout << n << ':' << pos << endl;
        
        system("PAUSE");
        return 0;
    }

             递归的实现,关键在于递归结构的定义,以及终止条件以及递归公式。

    1)  细则1

    void rec_binary(const vector<int>& arr, int left, int right, int middle, int n, int& pos)

             这里需要有 left 和 right 作为查找序列的边界,递归的意义也就在于对 left 和 right 上下边界的操作。

             middle 参数可有可无,这里在函数定义的时候作为了参数,也可以在函数内部有 left 和 right 得到。

    2)  细则2

    细则2和下面的 n == arr[middle] 作为递归函数的终止条件。

    3)  细则3、4

    递归函数的内部调用——递归公式。

    4)  细则5

    递归函数的外部调用。

    rec_binary(arr, 0, arr.size()-1, (0+arr.size()-1)/2, n, pos);

             附:递归的另一个实现

    void rec_binary_2(const vector<int>& arr, int left, int right, int n, int& pos)

    #include <iostream>
    #include <vector>
    using namespace std;
    
    void rec_binary_2(const vector<int>& arr, int left, int right, int n, int& pos)
    {
        int middle = (left + right) / 2;
        if (left > right)
        {
            pos = -1;
            return;
        }
        else if (n == arr[middle])
        {
            pos = middle;
            return;
        }
        else if (n > arr[middle])
        {
            rec_binary_2(arr, middle+1, right, n, pos);
        }
        else
        {
            rec_binary_2(arr, left, middle-1, n, pos);
        }
    }
    
    int main()
    {
        int a[] = {2, 4, 5, 8, 9, 10, 13, 16, 18, 19};
        vector<int> arr(a, a+sizeof (a) / sizeof (*a));
        for (vector<int>::size_type i = 0; i != arr.size(); ++i)
        {
            cout << arr[i] << ' ';
        }
        cout << endl;
        int n = 9, pos = -1;
        rec_binary_2(arr, 0, arr.size()-1, n, pos);
        cout << n << ':' << pos << endl;
    
        n = 12, pos = -1;
        rec_binary_2(arr, 0, arr.size()-1, n, pos);
        cout << n << ':' << pos << endl;
        
        system("PAUSE");
        return 0;
    }

     

  • 相关阅读:
    九个令人兴奋的新功能将与Java 9 展示两点
    自学前端开发 新版css时钟效果图
    自学前端,你要的学习资料到了~~~~~~
    Angularjs中ng-repeat与移动端滑动插件iScroll的冲突
    计蒜客学习记录
    明明的随机数
    模板题
    泉州一中复赛模拟
    快速幂模板
    NOIP2013
  • 原文地址:https://www.cnblogs.com/unixfy/p/3151446.html
Copyright © 2011-2022 走看看