【算法题】二分查找扩展题学习总结

95 阅读2分钟

什么题考虑二分?

  • 有序数组,大概率都能用

从简单到难,做了以下三道题,虽然我是按题号做的,第一道最难。。。不过三道ac 100%,还是很开心的

leetcode 35. 搜索插入位置

image.png

  • 这道题有序,找一个位置,要求ologn,肯定是二分查找,但是稍微变了一点点,其实就是终止条件变了下
  • 当我们找到了,直接返回下标
  • 如果mid的值小于target时,我们要判断:
    • 1.如果mid已经是最后一个了,那么直接返回最后一位下标+1,也就是len
    • 2.如果不满足1,那么mid后至少有一个点,如果mid+1的值>target,那么我们也找到了target的合适位置,返回mid+1;
    • 3.如果1、2都不行,再进行二分搜索
  • mid大于的情况也类似

基于上面的思路,代码就很简单了

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

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

image.png

思路

  • 有序,但是有重复的值,可以用二分搜索确定一个阈值
  • 也是很简单,如果mid==target,先把mid赋值给答案[i,j],此时从mid发散,找前后的区间
 public static int[] searchRange(int[] nums, int target) {
        int[] arr = {-1,-1};
        int len = nums.length;
        if (len == 0) return arr;
        int left = 0, right = len -1;
        while (left <= right){
            int mid = (left + right) / 2;
            if (nums[mid] < target){
                left = mid +1;
            }else if (nums[mid] > target){
                right = mid -1;
            }
            else {
                arr[0] = mid;
                arr[1] = mid;
                for (int i =mid-1;i >=0;i--){
                    if (nums[i]<target) {break;}
                    arr[0] = i;
                }
                for (int i =mid+1;i<len;i++){
                    if (nums[i]>target) {break;}
                    arr[1] = i;
                }
                return arr;
            }

        }
//        find(nums, target, 0, len -1,  arr);
        return arr;
    }

leetcode 33. 搜索旋转排序数组

image.png

思路:

  • 这道题部分有序,其实也可以想到用二分,但是我当时第一次做,没ac出来
  • 其实就是分为两部分,至少一部分是有序的,在有序的部分里找target,另一部分可能是有序的(恰好分在点上了),不过大概率是无序的,但是也是一个翻转有序数组,递归处理就行了,思路比前面的还简单
public int search(int[] nums, int target) {
    int len = nums.length;
    int index = -1;
    if (len == 1){
        return nums[0] == target ? 0 : index;
    }
    return search(nums, target, 0, len-1);
}
public int search(int[] nums, int target, int start, int end){
    if (start == end) return nums[start] == target ? start : -1;
    if (start > end) return -1;
    if (nums[start] == target) return start;
    if (nums[end] == target) return end;
    int mid = (start + end) /2;
    if (nums[mid] == target) return mid;
    int i1=-1,i2=-1;
    if (nums[mid] > nums[start]){
        i1 = find(nums, target,  start, mid -1);
        i2 =  search(nums, target, mid +1, end);
    }else {
        i1 = find(nums, target,  mid+1, end);
        i2 = search(nums, target, start, mid-1);
    }
    if (i1!=-1) return i1;
    if (i2!=-1) return i2;
    return -1;
}
// 二分查找的的另一种递归写法,
public int find(int[] nums, int target, int start ,int end){
    if (start > end){
        return -1;
    }
    if (start == end){
        return nums[start]==target?start:-1;
    }
    int mid = (start + end) / 2;
    if (nums[mid] == target) return mid;
    else if (nums[mid] < target) return find(nums, target, mid +1,end);
    else  return find(nums, target, start,mid-1);
}