zoukankan      html  css  js  c++  java
  • 二分查找算法的升级版

    二分查找针对的是一个有序的数据集合,查找思想有点类似分治思想。每次都通过跟区间的中间元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间被缩小为0。

    时间复杂度:O(logn)

    一、二分查找容易出错的3个地方:
      1、循环退出条件

        注意是low <= high.

      2、mid的取值

        mid = (low + high)>>2 这种写法有问题,因为如果low和high比较大的话,两者之和就有可能溢出。因为改进的方法mid = low + ((high - low )>>2).

      3、low和high的更新

        low = mid + 1,high  = mid -1.如果直接写成low = mid或者high = mid,就可能发生死循环。

    二、二分查找的几种变体的代码实现

      最简单的情况:有序数组中不存在重复元素

      循环:

    public static int binarySearch(int[] nums, int value) {
            if(nums == null) {
                return -1;
            }
            int length = nums.length;
            int low = 0;
            int high = length -1;
            while(low <= high) {
                int mid = low + ((high -low)>>2);//为什么不 (low + high)>>2 当low和high 很大时,防止溢出
                if(value == nums[mid]) {
                    return mid;
                }else if (value < nums[mid]) {
                    high = mid -1;
                }else {
                    low = mid + 1;
                }
            }
            return -1;
        }

      递归:

    public int bsearch(int[] nums, int value) {
            return binarySearchRecursion(nums, 0, nums.length -1 , value);
        }
        public static int binarySearchRecursion(int[] nums, int start, int end, int x) {
            if(start > end){//空表
                return -1;
            }
            int mid = (start + end)/2;
            if(x == nums[mid]) {
                return mid;
            }else if(x < nums[mid]) {
                return binarySearchRecursion(nums, start, mid - 1, x);
            }else {
                return binarySearchRecursion(nums, mid + 1, end, x);
            }
        }

      变体一:找出第一个等于给定值的元素

    public static int binarySearch1(int[] nums, int value) {
            if(nums == null) {
                return -1;
            }
            int length = nums.length;
            int low = 0;
            int high = length -1;
            while (low <= high) {
                int mid  = low + ((high - low)>>2);
                if (value < nums[mid]) {
                    high = mid -1;
                }else if(value > nums[mid]) {
                    low = mid + 1;
                }else {
                    if(mid == 0 || nums[mid - 1] != value) {
                        return mid;
                    }
                    high = mid -1;
                }
            }
            return -1;
        }

      变体二:找出最后一个等于给定值的元素

    public static int binarySearch2(int[] nums, int value) {
            if(nums == null) {
                return -1;
            }
            int length = nums.length;
            int low = 0;
            int high = length - 1;
            while(low <= high) {
                int mid = low + ((high - low)>>2);
                if(value < nums[mid]) {
                    high = mid -1;
                }else if(value > nums[mid]) {
                    low  = mid + 1;
                }else {
                    if(mid == length -1 || nums[mid + 1] != value) {
                        return mid;
                    }
                    low = mid + 1;
                }
            }
            return -1;
        }

      变体三:查找第一个大于等于给定值的元素

    public static int binarySearch3(int[] nums, int value) {
            if(nums == null) {
                return -1;
            }
            int length = nums.length;
            int low = 0;
            int high = length - 1;
            while(low <= high) {
                int mid = low + ((high - low)>>2);
                if(value >= nums[mid]) {
                    if(mid == 0 || nums[mid -1] < value) {
                        return mid;
                    }
                    low = mid + 1;
                }else {
                    high = mid -1;
                }
            }
            return -1;
        }
        

      变体四:查找最后一个小于等于给定值的元素

    public static int binarySearch4(int[] nums, int value) {
            if(nums == null) {
                return -1;
            }
            int length = nums.length;
            int low = 0;
            int high = length - 1;
            while(low <= high) {
                int mid = low + ((high - low)>>2);
                if(value <= nums[mid]) {
                    if(mid == high - 1 || nums[mid + 1] > value) {
                        return mid;
                    }
                    high = mid - 1;
                }else {
                    low = mid + 1;
                }
            }
            return -1;
        }

    三、二分查找应用场景的局限性

      1、二分查找依赖的是顺序表结构,简单点就是数组

           二分查找只能用在插入、删除操作不频繁,一次排序多次查找的场景中。针对动态变化的数据集合,二分查找不再适用。

      2、二分查找针对的是有序数据

           二分查找算法需要按照下标随机访问元素。下表随机访问数据的时间复杂度是O(1),而链表随机访问的时间复杂度是O(n),所以数据如果使用链表存储,二分查找的时间复杂度就会变得很高。

      3、数据量太小不适合二分查找

        如果数据量很小,完全没有必要二分查找,直接遍历就足够了,但有一个例外,如果数据之间的比较操作非常耗时,不管数据量大小,都推荐用二分查找。比如:数组中存储的都是长度为300的字符串,比较如此长的两个字符    串,非常耗时,我们尽可能减少比较次数,减少比较次数会提高性能,这时二分查找比顺序遍历更有优势。

      4、数据量太大也不适合二分查找

        二分查找底层依赖的是数组这种数据结构,数组存储需要连续的内存空间,对内存的要求比较苛刻。当太大的数据用数组存储比较吃力,就不能用二分查找了。

  • 相关阅读:
    php基础语言
    cookie和setting
    php数据连接
    php连接sql
    php提交
    今天学习了php的数据类型
    第一天进入php,这只是自己的一个心情
    02-07 (2) 自连接
    内连接 和左连接查询 02-07 (1)
    out 和ref 的区别
  • 原文地址:https://www.cnblogs.com/cherish010/p/10567284.html
Copyright © 2011-2022 走看看