前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >算法学习|二分查找

算法学习|二分查找

作者头像
微笑的小小刀
发布2019-08-20 14:26:57
4910
发布2019-08-20 14:26:57
举报
文章被收录于专栏:java技术大本营

二分查找

  1. 二分查找的也称为折半查找,由于每次都能够将查找区间缩小为原来一半,这种算法的时间复杂度为O(logN)。
  2. 计算中值的方法有两种 m = (low+ hight) / 2 m = low + (hight - low) / 2 推荐使用第二种,因为减法不会涉及到数据因为相加导致的溢出问题。
  3. 查找的返回值一般返回为low,但是实际情况需要从实际的题目出发。
  4. 可以多举例子,便于理解边界问题。

Question 1

x 的平方根(https://leetcode-cn.com/problems/sqrtx/)(长按复制到浏览器即可到leetcode对应的题目)

算法的逻辑在具体的代码注释里

代码语言:javascript
复制
/**
 * 计算并返回 x 的平方根,其中 x 是非负整数。
 * 由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。
 */
public class Question1 {
    public int mySqrt(int x) {
        if (x <= 1) {
            return x;
        }
//        二分
        int low = 1;
        int hight = x;
//        结合双指针
        while (low <= hight) {
//            定义中间值  这样定义middle不会出现溢出问题
            int middle = low + (hight - low) / 2;
//            计算是否能够直接算出开方
            int sqrt = x / middle;
            if (sqrt == middle) {
                return middle;
            } else if (middle > sqrt) {
//                大了 调整hight
                hight = middle - 1;
            } else {
//                小了 调整low
                low = middle + 1;
            }
        }
        return hight;
    }
}

Question 2

寻找比目标字母大的最小字母(https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/)

代码语言:javascript
复制
/**
 * 给定一个只包含小写字母的有序数组letters 和一个目标字母 target,寻找有序数组里面比目标字母大的最小字母。
 * 数组里字母的顺序是循环的。举个例子,如果目标字母target = 'z' 并且有序数组为 letters = ['a', 'b'],则答案返回 'a'。
 * 关键词:有序、循环
 */
public class Question2 {
    public char nextGreatestLetter(char[] letters, char target) {
//        定义头尾
        int hight = letters.length-1;
        int low = 0;
        while (hight>=low){
            int middle = low + (hight-low)/2;
            if(target>=letters[middle]){
//                middle小了
                low = middle+1;
            }else {
//                middle大了
                hight = middle-1;
            }
        }
//        最终得到的low是接近结果的(因为只有low大于hight的时候才会跳出循环)
        return low<letters.length?letters[low]:letters[0];
    }
}

Question3

有序数组中的单一元素(https://leetcode-cn.com/problems/single-element-in-a-sorted-array/)

代码语言:javascript
复制
/**
 * 给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。
 * 注意: 您的方案应该在 O(log n)时间复杂度和 O(1)空间复杂度中运行。
 * <p>
 * 因为要求时间复杂度和空间复杂度,所以不能够使用直接遍历的方法。
 */
public class Question3 {
    public int singleNonDuplicate(int[] nums) {
        int low = 0;
        int hight = nums.length - 1;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
//            判断中间的数是偶数还是奇数
            if (middle % 2 == 0) {
//                偶数  说明middle前面有偶数个数字 此时判断middle 和 middle+1的关系
                if (nums[middle] == nums[middle + 1]) {
//                    说明middle前面数字都是出现两次的
                    low = middle + 2;
                } else {
//                    说明middle后面的数字都是正确的
                    hight = middle;
                }
            } else {
//                奇数   说明前后都只有奇数个数字  判断middle 和middle+1的关系
                if (nums[middle] == nums[middle + 1]) {
//                   说明middle之后的都是正确的
                    hight = middle - 1;
                } else {
//                    说明middle+1之后的是有问题的
                    low = middle + 1;
                }
            }
        }
        return nums[low];
    }
}

Question4

第一个错误的版本(https://leetcode-cn.com/problems/first-bad-version/)

代码语言:javascript
复制
/**
 * 你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。
 * 由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。
 * <p>
 * 假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。
 * <p>
 * 你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。
 * 实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。
 */
public class Question4 {
    public int firstBadVersion(int n) {
        int low = 1;
        int hight = n;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
//            二分查找 提高效率
            if(isBadVersion(middle)){
//                版本出现错误 可能是在之前就出错了,所以需要往前回溯
                hight = middle;
            }else {
//                版本正确 往后推进
                low = middle+1;
            }
        }
        return low;
    }

    /**
     * leetcode系统提供的方法
     *
     * @param version
     * @return true:错误版本
     * false:正确版本
     */
//    boolean isBadVersion(int version) {
//        return false;
//   }
}

Question 5

寻找旋转排序数组中的最小值(https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/)

代码语言:javascript
复制
/**
 * 旋转数组的最小数字问题
 * 假设按照升序排序的数组在预先未知的某个点上进行了旋转。
 * ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
 * 请找出其中最小的元素。
 * 你可以假设数组中不存在重复元素。
 */
public class Question5 {
    public int findMin(int[] nums) {
        int low = 0;
        int hight = nums.length - 1;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
//            旋转数组的特征就是:发生旋转的话,就会有一个旋转点
//            我们的目标就是找到这个旋转点
//            (通过二分法比对middle和hight的值)
//            hight>middle的话 说明后半部分没哟旋转点 反之同理!
            if(nums[middle]<nums[hight]){
//                说明后半部分是正常的顺序的数组,没有旋转点,将目标缩小到前半部分
                hight = middle;
            }else {
//                说明后半部分数组有旋转点,继续调查后半部分的数组
                low = middle+1;
            }
        }
        return nums[low];
    }

Question 6

在排序数组中查找元素的第一个和最后一个位置(https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)

代码语言:javascript
复制
/**
 * 题目描述:
 * 给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
 * 你的算法时间复杂度必须是 O(log n) 级别。
 * 如果数组中不存在目标值,返回 [-1, -1]。
 * <p>
 * 关键词:升序数组、时间复杂度要求O(log n) 所以需要使用二分查找算法
 */
public class Question6 {
    public int[] searchRange(int[] nums, int target) {
//        寻找第一次出现target的地方
        int first = binarySearch(nums, target);
//        寻找第一次出现比target大1的数字的地方
        int last = binarySearch(nums, target + 1) - 1;
        if (first == nums.length || nums[first] != target) {
            return new int[]{-1, -1};
        } else {
            return new int[]{first, Math.max(first, last)};
        }
    }

    /**
     * 封装成方法 用于寻找target第一次出现的位置
     * @param nums
     * @param target
     * @return
     */
    private int binarySearch(int[] nums, int target) {
        int low = 0;
        int hight = nums.length;
        while (hight > low) {
            int middle = low + (hight - low) / 2;
            if (nums[middle] >= target) {
//                说明目标在上半部分
                hight = middle;
            } else {
//                说明目标在下半部分
                low = middle + 1;
            }
        }
        return low;
    }
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-08-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 java技术大本营 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二分查找
  • Question 1
  • x 的平方根(https://leetcode-cn.com/problems/sqrtx/)(长按复制到浏览器即可到leetcode对应的题目)
  • Question 2
  • 寻找比目标字母大的最小字母(https://leetcode-cn.com/problems/find-smallest-letter-greater-than-target/)
  • Question3
  • 有序数组中的单一元素(https://leetcode-cn.com/problems/single-element-in-a-sorted-array/)
  • Question4
  • 第一个错误的版本(https://leetcode-cn.com/problems/first-bad-version/)
  • Question 5
  • 寻找旋转排序数组中的最小值(https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/)
  • Question 6
  • 在排序数组中查找元素的第一个和最后一个位置(https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档