zoukankan      html  css  js  c++  java
  • 【数据结构与算法】二分查找法合集

    搜索插入位置

    LeetCode:搜索插入位置

    题目描述:

    给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

    你可以假设数组中无重复元素。

    示例:

    输入: [1,3,5,6], 5
    输出: 2
    

    思想:

    正常的二分查找

    代码:

    递归方式

    class Solution {
        public int searchInsert(int[] nums, int target) {
            return binarySearch(0,nums.length-1,nums,target);
        }
        private int binarySearch(int low, int high, int[] nums, int target){
            if(high<low){
                return low;
            }
            int mid = (high + low)/2;
            if(nums[mid] > target){
                return binarySearch(low,mid - 1,nums,target);
            }else if(nums[mid] < target){
                return binarySearch(mid + 1, high,nums,target);
            }else{
                return mid;
            }
        }
    }
    

    循环方式

    public int binarySearch(int[] nums, int key) {
        int l = 0, h = nums.length - 1;
        while (l <= h) {
            int m = l + (h - l) / 2;
            if (nums[m] == key) {
                return m;
            } else if (nums[m] > key) {
                h = m - 1;
            } else {
                l = m + 1;
            }
        }
        return -1;
    }
    

    x的平方根

    LeetCode:X的平方根

    题目描述:

    实现 int sqrt(int x) 函数。

    计算并返回 x 的平方根,其中 x 是非负整数。

    由于返回类型是整数,结果只保留整数的部分,小数部分将被舍去。

    示例:

    输入: 8
    输出: 2
    说明: 8 的平方根是 2.82842..., 
         由于返回类型是整数,小数部分将被舍去。
    

    思想:

    二分查找的变种。注意不要用m * m == x来判断,m * m会越界,要用m==x/m来做判断条件。

    代码:

    class Solution {
        public int mySqrt(int x) {
            if(x<=1)
                return x;
            int low=0;
            int high=x;
            int m=x;
            while(high>=low){
                m=low+(high-low)/2;
                if(m==x/m){
                    return m;
                }else if(m>x/m){
                    high=m-1;
                }else if(m<x/m){
                    low=m+1;
                }
            }
            return high;
        }
    }
    

    寻找比目标字母大的最小字母

    LeetCode:寻找比目标字母大的最小字母

    题目描述:

    给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。

    在比较时,字母是依序循环出现的。举个例子:

    如果目标字母 target = 'z' 并且字符列表为 letters = ['a', 'b'],则答案返回 'a'

    示例:

    输入:
    letters = ["c", "f", "j"]
    target = "a"
    输出: "c"
    
    输入:
    letters = ["c", "f", "j"]
    target = "c"
    输出: "f"
    
    输入:
    letters = ["c", "f", "j"]
    target = "d"
    输出: "f"
    

    思想:

    注意有多个重复元素的情况,要把<和==的情况合在一起进行处理,得到右边界。

    代码:

    class Solution {
        public char nextGreatestLetter(char[] letters, char target) {
            int l=0,h=letters.length-1,mid;
            while(h>=l){
                mid = (h+l)/2;
                if(letters[mid]>target)
                    h=mid-1;
                else
                    l=mid+1;
            }
            return letters[l%letters.length];
        }
    }
    

    有序数组的单一元素

    LeetCode:有序数组的单一元素

    题目描述:

    给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。

    示例:

    输入: [3,3,7,7,10,11,11]
    输出: 10
    

    思想:

    这题的关键在于,mid每次都要取偶数,再判断nums[mid]==nums[mid+1],即可得知结果在左半边还是右半边。

    代码:

    class Solution {
        public int singleNonDuplicate(int[] nums) {
            int low=0,high=nums.length-1,mid;
            while(high>low){
                mid = low+(high-low)/2;
                if(mid%2==1) mid--;
                if(nums[mid]==nums[mid+1])
                    low = mid+2;
                else
                    high = mid;
            }
            return nums[low];
        }
    }
    

    第一个错误版本

    LeetCode:第一个错误版本

    题目描述:

    你是产品经理,目前正在带领一个团队开发新的产品。不幸的是,你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的,所以错误的版本之后的所有版本都是错的。

    假设你有 n 个版本 [1, 2, ..., n],你想找出导致之后所有版本出错的第一个错误的版本。

    你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。

    示例:

    给定 n = 5,并且 version = 4 是第一个错误的版本。
    
    调用 isBadVersion(3) -> false
    调用 isBadVersion(5) -> true
    调用 isBadVersion(4) -> true
    
    所以,4 是第一个错误的版本。 
    
    

    思想:

    二分查找的变种,要找true的最左边。判断条件比较特殊,不能生搬硬套3.寻找比目标字母大的最小字母一题中的规律。

    代码:

    /* The isBadVersion API is defined in the parent class VersionControl.
          boolean isBadVersion(int version); */
    
    public class Solution extends VersionControl {
        public int firstBadVersion(int n) {
            int low=1,high=n,mid;
            while(high>low){
                mid = low+(high-low)/2;
                if(isBadVersion(mid)){
                    high = mid;
                }else{
                    low = mid+1;
                }
            }
            return low;
        }
    }
    

    寻找旋转排序数组中的最小值

    LeetCode:寻找旋转排序数组中的最小值

    题目描述:

    假设按照升序排序的数组在预先未知的某个点上进行了旋转。

    ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

    请找出其中最小的元素。

    你可以假设数组中不存在重复元素。

    示例:

    输入: [4,5,6,7,0,1,2]
    输出: 0
    

    思想:

    二分查找变种,主要是判断条件 nums[mid]>=nums[0] 需要细细琢磨。

    代码:

    class Solution {
        public int findMin(int[] nums) {
            int low=0,high=nums.length-1,mid;
            while(high>=low){
                mid = low+(high-low)/2;
                if(nums[mid]>=nums[0]){
                    low = mid+1;
                }else{
                    high = mid -1;
                }
            }
            return nums[low%nums.length]; 
        }
    }
    

    二分查找变种总结

    标准形式:

    public int binarySearch(int[] nums, int key) {
        int low = 0, high = nums.length - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (nums[m] == key) {
                return mid;
            } else if (nums[mid] > key) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return -1;
    }
    

    总结:

    • 当判断条件使用“nums[i]<=target”时,即将“<”和“==”合在一起执行 low = mid + 1, 最后结果 low 为“含重复target最左边”下标,high 为“比target小的最大的值”;
    • 当判断条件使用“nums[i]>=target”,即将“>”和“==”合在一起执行 high = mid - 1,最后结果 high 为“含重复target最右边”下标,low 为“比target大的最小的值”。
    • 使用变种“<=”+"low=mid"时,(要将 while 条件改为 low<high),最后结果 low 和 high 都为“含重复target最右边”下标;
    • 使用变种“>=”+"high=mid"时,(要将 while 条件改为 low<high),最后结果 low 和 high 都为“含重复target最左边”下标;
  • 相关阅读:
    Netty 零拷贝(一)Linux 零拷贝
    启动服务(上)服务端:NioServerSocketChannel 是什么时候激活的
    Reactor 模型(一)基本并发编程模型
    并发编程(三)Promise, Future 和 Callback
    并发编程(二)concurrent 工具类
    并发编程(一)同步类容器和并发类容器
    Executor(二)ThreadPoolExecutor、ScheduledThreadPoolExecutor 及 Executors 工厂类
    Executor(一)ExecutorService 线程池
    Java NIO系列教程(一四) Files
    Java NIO系列教程(一三) Path
  • 原文地址:https://www.cnblogs.com/buptleida/p/12517643.html
Copyright © 2011-2022 走看看