二分类算法

二分查找

三种模板

img

模板 #1 (left <= right)left,right

二分查找的最基础和最基本的形式。 查找条件可以在不与元素的两侧进行比较的情况下确定(或使用它周围的特定元素)。 不需要后处理,因为每一步中,你都在检查是否找到了元素。如果到达末尾,则知道未找到该元素。

模板 #2 (left < right)[left,right)(半闭半开区间)

一种实现二分查找的高级方法。 查找条件需要访问元素的直接右邻居。 使用元素的右邻居来确定是否满足条件,并决定是向左还是向右。 保证查找空间在每一步中至少有 2 个元素。 需要进行后处理。 当你剩下 1 个元素时,循环 / 递归结束。 需要评估剩余元素是否符合条件。

模板 #3 (left + 1 < right)(left,right)(半闭半开区间)

实现二分查找的另一种方法。 搜索条件需要访问元素的直接左右邻居。 使用元素的邻居来确定它是向右还是向左。 保证查找空间在每个步骤中至少有 3 个元素。 需要进行后处理。 当剩下 2 个元素时,循环 / 递归结束。 需要评估其余元素是否符合条件。

时间和空间复杂度:

时间:O(log n) —— 算法时间

因为二分查找是通过对查找空间中间的值应用一个条件来操作的,并因此将查找空间折半,在更糟糕的情况下,我们将不得不进行 O(log n) 次比较,其中 n 是集合中元素的数目。

为什么是 log n?

二分查找是通过将现有数组一分为二来执行的。 因此,每次调用子例程(或完成一次迭代)时,其大小都会减少到现有部分的一半。 首先 N 变成 N/2,然后又变成 N/4,然后继续下去,直到找到元素或尺寸变为 1。 迭代的最大次数是 log N (base 2) 。

空间:O(1) —— 常量空间

虽然二分查找确实需要跟踪 3 个指标,但迭代解决方案通常不需要任何其他额外空间,并且可以直接应用于集合本身,因此需要 O(1) 或常量空间。

模板1应用

x 的平方根

猜数字大小

搜索旋转排序数组

力扣上面有相应的题都可以练习,相对较简单

模板2应用

第一个错误的版本(右边发现第一个突变的元素)

//第二模板可以用于查找flase到true的临界变化点    
public int firstBadVersion(int n) {
        int left=0;
        int right=n;
        while(left<right)
        {
            int mid=left+(right-left)/2;
            if(isBadVersion(mid))
            {
                right=mid;//当这时发生了错误版本,right=mid;
            }
            else{
                left=mid+1;//未发生错误版本
            }
        }
        return left;
    }
//因为是求未错误->到错误版本时的临界点,所以left=mid+1,而右边的值等于right=mid;

寻找峰值

寻找一个数组某个元素的峰值

 //用于查找某个数组或集合内中某个值大于左右俩边的值
public int findPeakElement(int[] nums) {
    int left=0;
    int right=nums.length-1;
    while(left<right)
    {
        int mid=left+(right-left)/2;
        if(nums[mid]<nums[mid+1])
        {
            left=mid+1;
        }
        else{
            right=mid;
        }
    }
    return left;
    }
​

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

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

找到 K 个最接近的元素

  public List<Integer> findClosestElements(int[] arr, int k, int x) {
    List<Integer> list=new ArrayList<>();
    int left=0;
    int right=arr.length-k;
    while(left<right)
    {
        int mid=left+(right-left)/2;
        if(x - arr[mid] > arr[mid + k] - x)
        {
            left=mid+1;
        }
        else{
           right=mid;
        }
    }
    for(int i=left;i<left+k;i++)
    {
        list.add(arr[i]);
    }
    return list;
    }

模板三

在排序数组中查找元素的第一个和最后一个位置

 public int[] searchRange(int[] nums, int target) {
        int[] res=new int[2];
        res[0]=leftSearch(nums,target);
        res[1]=rightSearch(nums,target);
        return res;
    }
​
    private int leftSearch(int[] nums, int target){
        if(nums==null||nums.length==0) return -1;
​
        int l=0,r=nums.length-1;
        while(l+1<r){
            int mid=l+(r-l)/2;
            if(nums[mid]<target){
                l=mid;
            }
            else{
                r=mid;
            }
        }
        if(nums[l]==target) return l;
        if(nums[r]==target) return r;
        
        return -1;
    }
​
    private int rightSearch(int[] nums, int target){
        if(nums==null||nums.length==0) return -1;
​
        int l=0,r=nums.length-1;
        while(l+1<r){
            int mid=l+(r-l)/2;
            if(nums[mid]<=target){
                l=mid;
            }
            else{
                r=mid;
            }
        }
        if(nums[r]==target) return r;
        if(nums[l]==target) return l;
        
        return -1;
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值