• 二分查找总结


    二分搜索?莫非就是对于一个值单调递增的序列,给出(l,r),将要找的值与区间([l,r])的中点值(a[m])比较,大了(l)变成(m),小了(r)变成(m)呗?但是如果不细究,很容易发生off one错误。下面我们将要讨论该问题。

    序列内没有重复数字

    int Bsearch(int l, int r, int key, int *a)
    {
    	while(l <= r)
    	{
    		int mid = (l + r) >> 1;
    		if (a[mid] == key)
    			return mid;
    		if (key < a[mid])
    			l = mid + 1;
    		else
    			r = mid - 1;
    	}
    	return -1;
    }
    

    因为没有重复数字,所以如果mid的值就是所求,返回mid就可以,否则既然mid的值无效了,范围缩小后的区间就不包括mid了。

    LowerBound和UpperBound

    int LowerBound(int l, int r, int key, int *a)
    {
        if (key > a[r])
            return NO_ANS;
    	while(l < r)
    	{
    		int mid = (l + r) >> 1;
    		if (key <= a[mid])
    			r = mid;
    		else
    			l = mid + 1;
    	}
    	return l;
    }
    
    int UpperBound(int l, int r, int key, int *a)
    {
        if (key < a[l])
            return NO_ANS;
    	while(l < r)
    	{
    		int mid = (l + r + 1) >> 1;
    		if (key >= a[mid])
    			l = mid;
    		else
    			r = mid - 1;
    	}
    	return l;
    }
    

    定义

    LowerBound:给出(l,r,x),通过给出(p)的形式,求得一个区间([p,r]),使得任意一个下标(k),(kin [p,r]Leftrightarrow a_kgeq x)
    UpperBound:给出(l,r,x),通过给出(p)的形式,求得一个区间([l,p]),使得任意一个下标(k),(kin [l,p]Leftrightarrow a_kleq x)
    (m=frac{l+r}{2})
    左区间:([l,lfloor m floor]),右区间:([lceil m ceil,r])
    在LowerBound代码中,mid为(lfloor m floor);在UpperBound代码中,mid为(lceil m ceil)

    原理

    看看两个代码中mid的定义,发现无论是UpperBound,还是LowerBound,其缩小区间时,要么缩小到左区间,要么缩小到右区间。

    序列中存在(a_k=x)

    这意味着(k)的值可能有多个。

    (r-l+1)为偶数

    这意味着(m otin mathbb{Z})。如果(a_{lfloor m floor},a_{lceil m ceil})中存在不多于1个与(x)相等,这简单,若(xgeq a_{lceil m ceil}),则转移到右区间;若(xleq a_{lfloor m floor}),则转移到左区间。两个代码都满足这个要求。
    如果(a_{lfloor m floor},a_{lceil m ceil})两个值都与(x)相等,则因为LowerBound要的(k)值最小,所以它要舍弃(a_{lceil m ceil}),在左区间中寻找(kleqlfloor m floor,a_k=x),故要求转移到左区间。同理UpperBound要求转移到右区间。

    (r-l+1)为奇数

    这意味着(minmathbb{Z})。若(a_m eq x),则若(x<a_m),则转移到左区间,否则转移到右区间;
    (a_m=x),LowerBound要在左区间中寻找(kleqlfloor m floor,a_k=x),故要求转移到左区间。同理UpperBound要求转移到右区间。

    序列中不存在(a_k=x)

    一开始是普通的二分,最后会遇到这种情况:(m otinmathbb{Z})(a_{lfloor m floor}<x<a_{lceil m ceil})。如果是LowerBound,因为(x>a_{lfloor m floor}),LowerBound的定义要求所得区间内所有值都要大于(x),故要舍弃(a_{lfloor m floor}),故转移到右区间;UpperBound同理转移到了左区间。

    我们看到mid的定义以及小于大于等于判断及其操作,都满足上述要求。

  • 相关阅读:
    java中math类
    java中的值传递和引用传递(转)
    eclipse 字体和配色修改
    JAR WAR EAR包的区别
    java中基本类型
    tomcat 7.0 配置详解
    或许你需要一些可操作性更强的实践
    Assembly.CreateInstance()与Activator.CreateInstanc
    OOD沉思录2 类和对象的关系包含关系
    C#的插件开发
  • 原文地址:https://www.cnblogs.com/headboy2002/p/8977085.html
Copyright © 2020-2023  润新知