二分查找及变体

       二分查找,如何用最胜内存的方式实现快速查找
        针对有序数据集合的查找算法,二分查找。

       问题:假设我们有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;
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章