(圖文)二分查找,查指定值、小於或等於k的最大值,大於或等於k的最大值


我們經常會用到二分查找
二分查找應該很多人都會寫了,今天要寫一個用二分查找找到小於k的最大值的時候看了很久不懂他設計的思路,后來想通了,記錄一下。
所以這篇主要是講 用二分查找找到小於k的最大值大於k的最大值

二分查找查找指定值

這個挺簡單的,直接上代碼吧

 //獲取值是k的位置,找不到則返回-1
    public static int getK(int[] a, int k){
        if(a.length == 0){
            return -1;
        }
        int l = 0;
        int r = a.length - 1;
        //注意這里的判斷條件,是必須允許 l = r 的情況存在的
        //因為可能會出現剛好到最后左邊指針到右邊指針只有1個元素,而且這個元素恰恰就是我們想找的k
        while (l <= r){
            int mid = (l + r) / 2;
            //如果mid元素已經是k了,那么直接返回
            if (a[mid] == k){
                return mid;
            //查找左邊的
            }else if(a[mid] > k) {
                r = mid - 1;
            //查找右邊的
            }else {
                l = mid + 1;
            }
        }
        //如果沒有找到指定的值,那么直接返回-1
        return -1;
    }

用二分查找找到小於或者等於k的最大值

思路:如果mid元素是小於或等於k,往右找,如果大於k,往左邊找,直到找到一個值,最接近與k的。
看代碼中標注了 處地方,下面解釋為什么如此設計

    //獲取值<=k的最大值
    public static int uperK(int[] a, int k){
        int l = 0;
        int r = a.length - 1;
        //標注1: 這里是l<r,
        while(l < r){
            //標注2: 這樣的操作是為了取高位
            int mid = (l + r + 1) / 2;
            if(a[mid] <= k) { //標注3:因為a[mid]<=k,所以a[mid]可能=k,所以mid坐標也滿足條件,l = mid而不是mid+1;
                l = mid;
            }else{
                r= mid - 1; //這是a[mid] > k的時候。
            }
        }
        //標注4: 因為此時求得到的是最接近於目標值k的數,
        // 如果最小值都大於k的話,那么就沒有辦法得到了,所以就進行一個判斷
        if(a[l] > k) return -1;
        //標注5: 其實這里無論返回 a[l] 還是a[r]都行,循環的退出時間是l == r 的時候
        return a[l];
    }

  • 標注1解釋:
    因為我們的目的是“通過縮小范圍,得到當l = r的時候,l 標記的值”的情況,所以直到 l< r條件被打破的時候的l就是我們要求的值。這跟上一道問題“查找指定值”是不一樣的

  • 標注2解釋:
    標注2: 這樣的操作是為了讓 mid 標志 取高位, 才能讓循環順序跳出來,舉個死循環的例子
    注意這個例子采取的是mid = (l + r) / 2 取低位的情況,為了展示死循環的形成過程,我們原題的做法是取高位的

數據: int[] num = {1,2,3,4,5,6,7,8,9}, 查找小於或者等於8的最小值。


如上圖,此時左坐標是0, 右是8, 那么
mid = (0 + 8) / 2 = 4,num[mid] = num[4] <= k, ,向右找結果,所以有 l = mid,開始下一個循環。


此時的mid = 6, num[6] = 7;
num[mid] <= k, 向右找 l = 6
下一步:

可以得到 l = 6, r = 8, mid = (6+8) / 2 = 7,a[7] = 8 <= k,那么有l = mid = 7, r = 8,
推到得到mid = 7, 問題來了上一步的時候mid已經是7了,結果會使得mid一直是7,一直循環下去。
所以如果我們求的是二分法求小於或者等於k的最大值的話,我們mid 必須取得中值的上界,

  • 標注3解釋:
    因為a[mid]<=k,所以a[mid]可能=k,所以mid坐標也滿足條件,l = mid而不是mid+1;
  • 標注4解釋:
    如果最小值都大於k的話,那么就沒有辦法得到了,所以就進行一個判斷
    最典型的例子是:
    {1,2,3,4,5,6,7,8} ,然后k是0的時候, 這樣得到的結果只能是數組中最接近與k的數,但是如果他還是大於k
    你們可以算一下上面這個例子,到最后num[l] 是1, 永遠都大於0, 那么得返回取不到值,所以return -1;
  • 標注5解釋:
    其實這里無論返回 a[l] 還是a[r]都行,循環的退出時間是l == r 的時候

二分查找大於或等於k的最小值

直接上代碼了,想必大家都清楚了,看了上面問題2的解釋

public static int downK(List list, int key){
    while (low < high) {
                //這里進行的是取低位, 也是為了使得循環可以正確退出,防止死循環
		int mid = (low + high)/2;
		if (a[mid] < key) {
		    low = mid +1;
		} else { //a[mid] >= key
		    high = mid;   //因為mid也滿足情況
		}
	}
            //這里進行檢查的原因參考上面的標注
	if (a[high] >= key) {
		return high;
	} else {
	    return -1;
	}
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM