二分搜索——分治思想

二分查找

二分查找是一种在每次比较之后将查找空间一分为二的算法。每次需要查找集合中的索引或元素时,都应该考虑二分查找。如果集合是无序的,我们可以总是在应用二分查找之前先对其进行排序。
时间复杂度是: log N。因为,二分查找是通过将现有数组一分为二来执行的。每次调用子例程(或完成一次迭代)时,其大小都会减少到现有部分的一半。迭代的最大次数是 log N (base 2) 。

最常用的模板

二分查找一般由三个主要部分组成:

  1. 预处理 —— 如果集合未排序,则进行排序
  2. 二分查找 —— 使用循环或递归在每次比较后将查找空间划分为两半。
  3. 后处理 —— 在剩余空间中确定可行的候选者。

先介绍学校中学习的常用模板(由于每次都判断nums[mid]==target 无需后处理):

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

初始条件:left = 0, right = length-1
终止:left <= right
向左查找:right = mid-1
向右查找:left = mid+1

高阶模板

终止条件修改为:left+1>right始终保留区间内大于两个元素,因此需要对剩下的两个元素进行后处理

int binarySearch(int[] nums, int target) {
    int left = 0, right = nums.length - 1;
    while (left + 1 < right){
        int mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        } else if (nums[mid] < target) {
            left = mid;
        } else {
            right = mid;
        }
    }
    //后处理
    if(nums[left] == target) return left;
    if(nums[right] == target) return right;
    return -1;
}

算法应用

模板一

猜数字:从 1 到 n 选择一个数字。 你需要猜我选择了哪个数字。
每次你猜错了,我会告诉你这个数字是大了还是小了。你调用一个预先定义好的接口 guess(int num),它会返回 3 个可能的结果(-1,1 或 0):

public class Solution extends GuessGame {
    public int guessNumber(int n) {
        long left=0,right=n;
        while(left<=right)
        {
            long mid=(left+right)/2;
            if(guess((int)mid)==0)
                return (int)mid;
            else if(guess((int)mid)==-1)
                right=mid-1;
            else
                left=mid+1;
        }
        return -1;
    }
}

模板二

寻找峰值:峰值元素是指其值大于左右相邻值的元素。
要求:给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。你可以假设 nums[-1] = nums[n] = -∞。

class Solution {
    public int findPeakElement(int[] nums) {
        if(nums==null) return -1;
        if(nums.length==1) return 0;
        int left=0,right=nums.length;
        while(left+1<right)
        {
            int mid= left+(right-left)/2;
            if((mid-1==-1||nums[mid]>nums[mid-1])&&   //mid-1可能越界,先做判断
               (mid+1==nums.length||nums[mid]>nums[mid+1]))
                return mid;
            else if (nums[mid]<nums[mid-1])
                right=mid;
            else
                left=mid;
        }
        if(left-1==-1||nums[left]>nums[left-1]&&nums[left]>nums[left+1])
            return left;
        if(nums[right]>nums[right-1]&&
           right+1==nums.length||nums[right]>nums[right+1])
            return right;
        return -1;
    }
}

核心:分治思想

二分查找的核心思想就是分治思想

pow(n,x)

分治思想还可以解决其他的一些问题:pow(n,x)的实现,即计算 x 的 n 次幂函数。

class Solution {
    public double myPow(double x, int n) {
        return n>0?quickMul(x,n):1/quickMul(x,-n);
    }
    public double quickMul(double x, int n)
    {
        if(n==0) return 1;
        if(n==1) return x;
        double y=quickMul(x,n/2);
        return n%2==0?y*y:y*y*x;
    }
}

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

给你一个排序后的字符列表 letters ,列表中只包含小写英文字母。另给出一个目标字母 target,请你寻找在这一有序列表里比目标字母大的最小字母。
在比较时,字母是依序循环出现的。举个例子:
如果目标字母 target = ‘z’ 并且字符列表为 letters = [‘a’, ‘b’],则答案返回 ‘a’.

class Solution {
    public char nextGreatestLetter(char[] letters, char target) {

        int left=0,right=letters.length-1,mid=0;
        while(left<=right)
        {
            mid=left+(right-left)/2;
            if(letters[mid]==target)
            {
                while(mid+1<letters.length&&letters[mid]==letters[mid+1])
                    mid++;
                break;
            }
            else if(letters[mid]<=target)
                left=mid+1;
            else
                right=mid-1;
        }
        if(target<letters[mid])
            return letters[mid];
        return letters[(mid+1)%letters.length];
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值