二分查找相关题目

1.普通版二分查找

普通版的只要注意到区间范围就好,我使用的是左闭右闭方法,注意以下几点就可以。

第一种【】左闭右闭

  • right = nums.length - 1;
  • left  < = right
  • right = mid -1;
  • left = mid +1  

另一种选择:

[ ) 左闭右开

  • right = nums.length;
  • left  <  right
  • right = mid ;
  • left = mid +1  

牛客链接

二分查找-I_牛客题霸_牛客网 (nowcoder.com)

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param nums int整型一维数组 
     * @param target int整型 
     * @return int整型
     */
    public int search (int[] nums, int target) {
        int result = 0;
        int left = 0 ;
        int right = nums.length - 1;
        while(left <= right){
            int middle = (left + right)/2;
            if(nums[middle] > target){
                right = middle - 1 ;
            }else if(nums[middle] < target){
                left = middle + 1;
            }else{
                return middle;
            }
        }
//      未找到目标值
        return -1;
    }
}

2.排序数组中查找第一个元素和最后一个元素

力扣链接如下:

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

这个题的关键点在于怎么查找出左边界和右边界,先上一个暴力解法的版本

暴力解法

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int left = 0;
        int right = 0;
        int temp = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] == target){
                left = i;
                temp = 1;
                break;
            }
        }
        for(int j = left; j <nums.length ;j++){
            if(nums[j] != target){
                right = j-1;
                break;
            }
            right = j;
        }
        if(temp == 1){
            return new int[]{left,right};
        }else{
            return new int[]{-1,-1};
        }
    }
}

二分法查找

使用二分查找的方法的话,就是先找到左边界的第一个目标元素,在找到右边界的目标元素,使用两次二分查找即可。

class Solution {
    public int[] searchRange(int[] nums, int target) {

        int left = searchLeftBorder(nums,target);
        int right  = searchRightBorder(nums,target);
        // 这里要对边界条件进行一个判断
        if(left <= right && right <= nums.length ){
            return new int[]{left,right};
        }

        return new int[]{-1,-1};

    }
    // 获取左边界
    // 判断左边界就是找到第一个等于target的位置
    public int searchLeftBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int first = -1;
        while(left <= right){
            int mid = (left + right) / 2;
            if(nums[mid] == target){
                first = mid;
                // 因为要获取的是左边界,现在第一个查找出来的也不知道是不是最左的
                // 所有把二分查找的右边界移到现在查找出来的值的左边
                right = mid -1;
            }else if(nums[mid] > target){
                right = mid -1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }
        }
        return first;
        
    }

    // 获取右边界
    // 就是找到最后一个等于target的位置
        public int searchRightBorder(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        int last = -1;
        while(left <= right){
            int mid = (left + right) / 2;
            if(nums[mid] == target){
                last = mid;
                // 因为要获取的是右边界,现在第一个查找出来的也不知道是不是最右的
                // 所有把二分查找的左边界移到现在查找出来的值的右边
                left = mid + 1;
            }else if(nums[mid] > target){
                right = mid -1;
            }else if(nums[mid] < target){
                left = mid + 1;
            }
        }
        return last;
    }
}

3.第一个错误版本

力扣链接:

. - 力扣(LeetCode)

这个题目的关键在于理解题意

// 如果一个版本是正确的,那这个版本之前的都是正确的
// 如果一个版本是错误的,那这个版本之后的都是错误的
// 通过二分查找限定范围
// 起始左边为1,右边为n

while循环不能写成left<= right ,因为如果n =1就成死循环了

// 注意 isBadVersion为false时,代表是正确版本

注意到这几点后就和普通的二分查找是一样的了

/* The isBadVersion API is defined in the parent class VersionControl.
      boolean isBadVersion(int version); */


// 如果一个版本是正确的,那这个版本之前的都是正确的
// 如果一个版本是错误的,那这个版本之后的都是错误的
// 通过二分查找限定范围
// 起始左边为1,右边为n
public class Solution extends VersionControl {
    public int firstBadVersion(int n) {

        int left = 1;
        int right = n;
        // 注意这里不能写 = 号
        // 因为如果n=1 的话就进入死循环了
        while(left < right){
            int mid = left + (right - left) / 2;
            // 注意 isBadVersion为false时,代表是正确版本
            // 刚开始直接把题意给理解错误了,导致写反了
            if(isBadVersion(mid)){
                // mid是错误版本
                right = mid -1; 
            }else {
//              mid 是正确版本
                left = mid + 1;
            }
        }
        return left; 
    }
}

4.寻找两个正序数组的中位数

力扣链接:

. - 力扣(LeetCode)

暴力解法

首先来一个暴力的解法,这里的核心思想就是,先将两个有序数组进行合并,然后根据合并后的数组是奇数还是偶数进行判断。

代码如下:

class Solution {
  // 暴力解法,将两个数组进行合并,然后根据奇数还是偶数返回中位数
    public double findMedianSortedArrays(int[] nums1, int[] nums2) {
        int length = nums1.length + nums2.length;
        int nums[] = new int[length];
        nums = mergeArrays(nums1,nums2);
        // 奇数
        if(nums.length % 2 != 0){
            return nums[(nums.length - 1)/2];
        }else{
            double result = (nums[nums.length/2] + nums[nums.length/2 - 1])/2.0;
            return result;
        }

    }

    // 两个数组合并的代码
    public int[] mergeArrays(int[]nums1,int[] nums2){
        int length = nums1.length + nums2.length;
        int j = 0;
        int i = 0;
        int k = 0;
        int[] nums = new int[length];
        while(i < nums1.length && j < nums2.length){
            if(nums1[i] < nums2[j]){
                nums[k++] = nums1[i++];
            }else{
                nums[k++] = nums2[j++];
            }
        }
        // 如果提前把nums1数组遍历完了,就把nums2剩余的全部添加进去
        while(j < nums2.length){
            nums[i+j] = nums2[j++];
        }
        while(i < nums1.length){
            nums[i+j] = nums1[i++];
        }

        return nums;
    }
}

二分法解法

大家肯定想着用暴力的解法都可以了,为什么还要二分呢,是因为这道题目的要求是时间复杂度为:O(log(m+n))

看到时间复杂度为log,通常就需要用二分法了。

在分割线右边找

在分割线左边找,向 左边移动

下面这种数组越界的情况,要解决的话,就得让长度最小的那个数组充当第一个数组,

要满足的条件:

完整代码:

这个题思路还是难,算了还是用暴力法吧,清楚明了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值