二分查找及變體

       二分查找,如何用最勝內存的方式實現快速查找
        針對有序數據集合的查找算法,二分查找。

       問題:假設我們有1000萬個整數數據,每個數據佔8個字節,如何設計數據結構和算法快速判斷某個整數是否出現在這1000萬數據中,
        最簡單的辦法就是將數據存儲在數組中,內存佔用差不多80M,符合內存限制,所以,先對數據從小到大排序,然後利用二分查找算法,就可以快速查找想要的數據。

       二分查找針對的是一個有序的數據集合,查找思想類似分治思想,每次都通過跟區間的中間元素對比,將待查找的區間縮小爲之前的一半,直到找到要查找的元素,或者區間縮小爲0
       二分查找時間複雜度低,O(logn),

       二分查找最簡單的就是有序數組中不存在重複元素。

       二分查找依賴的是順序表結構,就是數組。還針對的是有序數據。

       二分查找只能再插入,刪除不頻繁,一次排序多次查找的場景中,針對動態變化的數據集合,二分查找將不再適用。
       數據量太小不適合二分查找。
       數據量太大也不適合二分查找,因爲底層依賴數組這種數據結構,而數組爲了支持隨機訪問,要求內存空間連續,對內存的要求苛刻。

       使用遞歸實現二分查找

public static int recursionBinarySearch(int[] arr,int key,int low,int high){
        if(key<arr[low] || key> arr[high] || low > high){
            return -1;
        }
         int middle = low + ((high - low)>>1);

        //middle = (low + high) / 2; 這種寫法有可能會導致溢出,比如low很大,high也很大,之和就溢出了。
        if(arr[middle] > key){
            return recursionBinarySearch(arr,key,low,middle);
        }
        else if(arr[middle] < key){
            return recursionBinarySearch(arr,key,middle + 1,high);
        }else {
            return middle;
        }
    }

       普通的二分查找。

public static int commonBinarySearch(int[] arr,int key){
        int low = 0;
        int high = arr.length -1;
        int middle = 0;
        if(key < arr[low] || key> arr[high] || low > high){
            return -1;
        }
        while(low <=high){

             middle = low + ((high - low)>>1);

            //middle = (low + high) / 2; 這種寫法有可能會導致溢出,比如low很大,high也很大,之和就溢出了。
            if(arr[middle] > key){
                high = middle -1;
            }else if(arr[middle] < key){
                low = middle + 1;
            }else {
                return middle;
            }
        }
        return  -1;
    }
變體一,查找第一個值等於給定值的元素
public static  int bsearchFirst(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid = low + ((high - low) >> 1);
            if (a[mid] >= value) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }

        if (low < n && a[low]==value) return low;
        else return -1;
    }

第二種方法

public int bsearch(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid =  low + ((high - low) >> 1);
            if (a[mid] > value) {
                high = mid - 1;
            } else if (a[mid] < value) {
                low = mid + 1;
            } else {
                if ((mid == 0) || (a[mid - 1] != value)) return mid;
                else high = mid - 1;
            }
        }
        return -1;
    }
變體二,查找第一個值等於給定值的元素
public int bsearch(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid =  low + ((high - low) >> 1);
            if (a[mid] > value) {
                high = mid - 1;
            } else if (a[mid] < value) {
                low = mid + 1;
            } else {
                if ((mid == n - 1) || (a[mid + 1] != value)) return mid;
                else low = mid + 1;
            }
        }
        return -1;
    }
變體三,查找第一個大於等於給定值的元素
public int bsearch(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid =  low + ((high - low) >> 1);
            if (a[mid] >= value) {
                if ((mid == 0) || (a[mid - 1] < value)) return mid;
                else high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }

變體四,查找最後一個小於等於給定值的元素
public int bsearchFour(int[] a, int n, int value) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid =  low + ((high - low) >> 1);
            if (a[mid] > value) {
                high = mid - 1;
            } else {
                if ((mid == n - 1) || (a[mid + 1] > value)) return mid;
                else low = mid + 1;
            }
        }
        return -1;
    }

如果有序數組是一個循環有序數組,比如4.5.6,1,2,3這種情況。
 //無重複元素
        public int search(int[] nums, int target)
        {
            int start = 0,end = nums.length-1;
            while(start<=end)
            {
                int mid = (start+end)/2;
                if(target == nums[mid]){return mid;}
                if(nums[start]<=nums[mid])//說明start-mid之間是有序的
                {
                    if(nums[start]<=target && target<nums[mid])
                    {
                        end = mid-1;
                    }else
                        start = mid+1;
                }else
                {
                    if(target>nums[mid] && target<=nums[end])
                    {
                        start = mid+1;
                    }else
                        end = mid-1;
                }
            }
            return -1;
        }

        //有重複數字
        public boolean searchCycle(int[] nums, int target) {
            int start = 0,end = nums.length-1;
            while(start<=end)
            {
                int mid = (start+end)/2;
                if(target == nums[mid]){return true;}
                if(nums[mid]>nums[start])
                {
                    if(target>=nums[start] && target<nums[mid])
                    {
                        end = mid-1;
                    }else
                    {
                        start = mid+1;
                    }
                }else
                if(nums[mid] == nums[start])
                {
                    while(start<=mid)
                    {
                        if(nums[start] == target){return true;}
                        if(nums[start] != nums[mid])
                        {
                            break;
                        }
                        start++;
                    }
                }else
                {
                    if(target>nums[mid] && target<=nums[end])
                    {
                        start = mid+1;
                    }else
                        end = mid-1;
                }
            }
            return false;
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章