1. 程式人生 > >複雜的二分查詢

複雜的二分查詢

1.解決上一篇的問題
問題並不難,首先要求的記憶體是小於100M,1000萬的資料每個資料八個位元組,全部存入陣列佔用的記憶體大概也是80M,所以完全可以使用二分查詢來實現。

2.複雜的二分查詢
簡單的二分查詢我們都會,我們的假設也是陣列中沒有相同元素,假設陣列中有相同的元素,查詢第一個值等於給定值的元素呢?查詢最後一個值等於給定值的元素呢?顯而易見我們之前的程式碼已經不適合這兩種問題了。先來一段比較燒腦的程式碼解決查詢第一個值等於給定元素的值:

        int low = 0;
        int high =n-1;
        while (low<=high){
            int mid = ((high-low)>>1)+low;
            if(arr[mid]<k){
                low =mid+1;
            }else if(arr[mid]>=k){
                high=mid-1;
            }
        }
        if(arr[low]==k)return low;
        else return -1;
    }

上邊的程式碼是不是特別不好理解,我也是繞了好久才繞出來,接下來我們換一種思路來實現:

public static int binarySearchFirstK2(int[] arr,int n,int k){
        int low =0;
        int high = n-1;

        while (low<=high){
            int mid =low + ((high-low)>>1);
            if(arr[mid]<k){
                low=mid+1;
            }else if(arr[mid]>k){
                high=mid-1;
            }else {
                if(mid==0||arr[mid-1]!=k)return mid;
                else high=mid-1;
            }
        }
        return -1;
    }

上邊的程式碼只是將等於的情況分離出來,並且在適合跳出迴圈的時候跳出,個人覺得這種程式碼可讀性強,邏輯清晰,bug相對來說較少,而且減少了多餘的比較。

接下來解決最後一個等於給定元素的值就直接上程式碼了:

public static int binarySearchLastK(int[] arr,int n,int k){
        int low = 0;
        int high = n-1;
        while (low<=high){
            int mid = low + ((high-low)>>1);
            if(arr[mid]<k){
                low=mid+1;
            }else if(arr[mid]>k){
                high=mid-1;
            }else {
                if(mid==n-1||arr[mid+1]!=k)return mid;
                else low=mid+1;
            }
        }
        return -1;
    }

上邊兩個問題弄明白後,那麼我們要查詢第一個大於等於給定值的元素?或者最後一個小於等於給定值的元素怎麼處理呢?其實邏輯很簡單就是將等於的情況分別與arr[mid]<k和arr[mid]>k的情況合併即可,程式碼如下:

 public static int binarySearchFirstMoreOrEqualThanK(int[]arr,int n,int k){
          int low = 0;
          int high = n-1;
          while (low<=high){
              int mid = low + ((high-low)>>1);
              if(arr[mid]>=k){
                  if(mid==0||arr[mid-1]<k)return mid;
                  high = mid-1;
              }else if(arr[mid]<k){
                  low= mid +1;
              }
          }
          return -1;
    }
public static int binarySearchLastLessOrEqualThanK(int[] arr,int n,int k){
            int low = 0;
            int high = n-1;
            while (low<=high){
                int mid = low + ((high-low)>>1);
                if(arr[mid]<=k){
                    if(mid==n-1||arr[mid+1]>k)return mid;
                    else low=mid+1;
                }else if(arr[mid]>k){
                    high=mid-1;
                }
            }
            return -1;
    }

問題
如何查詢一個ip所在的區域?

總結
其實實際情況中能夠使用二分查詢的情況我們都更傾向於使用散列表或者二叉樹來進行處理,那麼二分查詢的用處呢?
其實二分查詢更適合於“近似查詢”的問題。在這種問題的處理上二分查詢要明顯好於散列表和二叉樹等。