zoukankan      html  css  js  c++  java
  • 二分查找

      二分查找前提条件:带查找的数列有序

      二分查找,也叫折半查找,它遵循三步法,把原序列分成元素个数尽量接近的两个子序列,然后递归查找。二分查找只适用于有序序列。

      时间复杂度:O(logn)

      尽管可以递归实现,但二分查找一般写成非递归的。

    /*
        @function:在有序序列A中查找key的位置
        @param1: A --- 有序序列A
        @param2: [x,y) 待查找的左闭右开区间[x,y)
        @param3: key 待查找的值
        @return: 返回key的位置,若未找到则返回-1
        @explain: 若序列中有多个key,则返回的是中间的那个key的位置
    */
    int BinarySearch(int *A, int x, int y, int key){
        while (x < y){
            int mid = x + (y - x) / 2;
            if (A[mid] == key){
                return mid;
            }
            else if (A[mid] > key){
                y = mid;
            }
            else{
                x = mid + 1;
            }
        }//while(x<y)
        return 0;
    }
    View Code

      如果数组中有多个元素的值为key,那么上面的函数返回的是中间的那一个,有时这样的结果并不理想,我们可能需要求出key的区间(即上下界)

      因此下面的函数实现的是二分查找求下界,当key存在时返回它出现的第一个位置。

      如果不存在,返回这样一个下标i,在此处插入key(原来的元素A[i],a[i+1]...全都往后移动一个位置)后序列仍然有序

    /*
        @funtion:二分查找求下界
        @param1: A 有序序列
        @param2: [x, y)待查找的左闭右开区间
        @param3: 待查找的值
        @return: 返回第一个key出现的位置,
                 若不存在key则返回一个位置,在这个位置插入key该序列仍然有序
    */
    int Lower_Bound(int *A, int x, int y, int key){
        while (x < y){
            int mid = x + (y - x) / 2;
            if (A[mid] >= key){
                y = mid;
            }
            else{
                x = mid + 1;
            }
        }//while(x < y)
        return x;
    }
    View Code

      程序分析:

        首先,返回值不仅可能是x,x+1,x+2,...,y-1,还可能是y,如果v大于A[y-1]时只能插入这里了。

        因此,查找的区间为[x,y),返回值的区间可能为[x,y]。另:

        A[m] = key : 至少已经找到一个,而左边可能还有,因此区间变为[x,m]

        A[m] > key : 所求位置不可能在后面,但有可能是m,因此区间变为[x,m]

        A[m] < key : m和前面都不可行,因此区间变为[m+1,y]

        此外,该程序不会发生死循环,死循环会发生则可能在只剩一个元素时发生,因为有多个元素时都会缩短区间再次判断,

        假设只剩一个元素, 设其区间为[x, x+1),则 mid = x + (x+1-x)/2 = x;

        不管区间如何变, [x, x) 或 [x+1, x+1),循环都会结束,因而不会产生死循环

      类似的,可以实现二分查找求上界,

      当序列中存在key时,返回它出现的最后一个位置的后面一个位置

      如果不存在,则返回这样一个下标i,在此处插入key后序列仍然有序

    /*
        @function:二分查找求上界
        @param1: A 有序序列
        @param2: [x,y)待查找的左闭右开区间
        @param3: key 待查找的值
        @return: key存在时,返回最后一个key出现的位置的后一个位置(注意是后一个)
                 若不存在key,则返回一个位置,在该位置插入key原序列仍然有序
    */
    int Upper_Bound(int *A, int x, int y, int key){
        while (x < y){
            int mid = x + (y - x) / 2;
            if (A[mid] <= key){
                x = mid + 1;
            }
            else{
                y = mid;
            }
        }//while(x<y)
        return x;
    }
    View Code

      程序分析:

        注意程序返回的是最后一个key的后一个位置,

        同理,查找的区间为[x,y),返回的区间为[x,y],另:

        A[m] = key时,m以及左边都不可能(因为返回的key的后一个),因此区间变为[m+1,y)

        A[m] < key时,m以及左边的都不可能,因此区间变为[m+1,y)

        A[m] > key时,m的右边已经不可能,但m仍然可能(若A[m-1]<=key的话),因此区间变为[x,m)

      其实,STL的algorithm中实现了二分查找求上下界,分别为lower_bound()和upper_bound()

      函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素的迭代器。如果所有元素都小于val,则返回last

      要记住:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。

          如果所有元素都小于val,则返回last的位置,且last的位置是越界的!!

      同理,函数upper_bound()返回的在前闭后开区间查找的关键字的上界,

      如一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,

      同样,如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)

      返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置

  • 相关阅读:
    IDEA 修改JavaWeb的访问路径
    坦克大战--Java类型 ---- (1)音乐播放
    codeforces 620C
    算法笔记之KMP算法
    算法笔记数组
    26. Remove Duplicates from Sorted Array
    哈夫曼树的证明
    Complete Binary Search Tree
    Root of AVL Tree
    .net framework环境
  • 原文地址:https://www.cnblogs.com/tommychok/p/5374367.html
Copyright © 2011-2022 走看看