【Leetcode每日一题】35.搜素插入位置|二分查找数组下标

在这里插入图片描述

博主简介:大一计科生,努力学习Java中!热爱写博客~预备程序媛
所属专栏:LeetCode每日一题–进击大厂
✈往期博文回顾: 【JavaSE】保姆级教程|1万字+10张图学会类与对象–建议收藏
️‍♂️近期目标:成为千粉小博主。
“再牛的程序员也是从小白开始,既然开始了,就全身心投入去学习技术”

力扣每日刷题--35

  • 题目描述
  • 解题思路
  • 代码实现--Java
  • 总结&易错

题目描述

35. 搜索插入位置
【Leetcode每日一题】35.搜素插入位置|二分查找数组下标_第1张图片

解题思路

  • 题型:数组、二分查找(变式)—寻找第1个大于等于目标值的元素

  • 关键:二分查找的关键点就是—两边夹(高数上又叫作夹逼准则)。left和right确定答案所在区间,通过mid(把区间划分为[left,mid]&[mid+1,right]两个区间)来缩小区间范围,直到left==right,即获得答案。

    • 为什么呢?存在如下定理:A <= target <= B,当A = B时,target = A = B
  • 思路
    1.确定答案可能取值区间[left,right]
    2.left = 0;right = array.length;
    (因为此题中array.length也有可能为答案)
    3.while(left(不考虑所谓的什么闭区间,就仅仅代表它本身的含义:当left==right时,即找到答案,跳出循环

    • 为什么呢?因为很多问题就这么设计,一定要等到最后才能确定问题的答案,在很多时候,不能在循环体中找到答案。

    4.确定循环体中分支的两种情况:

    • a.target>array[mid]:left=mid+1
    • b.else (即target<=array[mid]):right=mid

    5.left==right跳出循环体后:return right
    为什么呢直接return left/right呢?我们来分析一下跳出循环后的两种情况

    • 情况1:最简单的一种情况,即夹逼准则成立时,找到答案:array[left]=array[right]=targer,这个很好理解。此时array[left]==targert
    • 情况2:即没找到与target相等的元素的下标。因为array[left]<=target<=array[right]是默认恒成立的–即target应该存在于这个区间。即left指针指向[left,right]第一个小于等于target的元素,right指针指向[left,right]最后一个大于等于target的元素。在[left,right]实际区间范围不断缩小的过程中,当left和right重合时,right指向的是[0,array.length-1]这个区间第一个大于等于target的元素.(我个人目前觉得在理解层面上,return right比return left要好理解一些,虽然两者达到的效果是一样的)

2023:1:24日补充图解:
【Leetcode每日一题】35.搜素插入位置|二分查找数组下标_第2张图片

代码实现–Java

class Solution {
    public int searchInsert(int[] nums, int target) {
         int left=0,right=nums.length;//因为nums.length可能为答案
         while (left<right){
            int mid=left+(right-left)/2;//防止溢出(但其实left和right表示数组小标没什么必要)
            if(target>nums[mid]){
                left=mid+1;//下一次搜索区间为[mid+1,right]
            }
            else{
                right=mid;//下一次搜素区间为[left,mid]
            }
        }
        return right;
    }
}

总结&易错

  • 【易错】二分查找的重点就划分区间、逐渐缩小、两边夹,关于划分区间这题我用的划分为[left,mid]和[mid+1,right],为什么不是[left,mid-1]和[mid,right]呢?—因为会出现死循环
//下面是把区间划分为[left,mid-1]和[mid,right]的情况(错误代码)
class Solution {
    public int searchInsert(int[] nums, int target) {
         int left=0,right=nums.length;//因为nums.length可能为答案
         while (left<right){
            int mid=left+(right-left)/2;//防止溢出(但其实left和right表示数组小标没什么必要)
            if(target<nums[mid]){
                right=mid-1;//下一次搜索区间为[left,mid-1]
            }
            else{
                left=mid;//下一次搜素区间为[mid,right]
            }
        }
        return right;
    }
}

死循环分析:
输入[1,3,5,6] 2出现超时。分析:
第一次循环:mid=2;区间被分为[0,1],[2…4],下一次搜索区间为[0,1],left:0,right:1
第二次循环:mid=0,left:0,right:1,下次循环区间还是[0,1]
第三次循环:mid=0,left:0,right:1,下次循环区间还是[0,1]
…死循环

  • 解决方法:把(left+right)/2修改为(left+right+1)/2
    • 其实本质死循环的原因是只有两个元素时,mid指针指向的是前一个元素,再mid=(left+right)/2,不能把区间[left,mid-1]和[mid,right]其缩小,所以才会发生死循环。通过修改为mid=(left+right+1)/2,此时可以缩小区间,即不会发生死循环。

【Leetcode每日一题】35.搜素插入位置|二分查找数组下标_第3张图片

  • 【重点】二分法的关键是缩小区间,死循环发生的原因是某次循环没有缩小区间导致二分失败。

  • 【重点】此题right设置为array.length的原因是array.length也有可能是问题答案

  • 【重点】二分查找的判断条件写成while(left,代表的是搜索区间为[left,right],一旦left==right,即此时找到问题答案,立即跳出循环。

  • 【重点】循环不变量:在[left,right]搜索答案


write in the end:

  • ‍♀️建立此专栏的初衷是为了监督自己每天认真刷一个题,积少成多。并把自己每次刷题的思路、收获以博文的形式分享出来,帮助更多人,以及方便后续复习。如果有兴趣的同学可以订阅此专栏,我们一起刷题,一起交流,进步和学习!专栏:LeetCode每日一题–进击大厂

在这里插入图片描述

你可能感兴趣的:(leetcode,算法,数据结构)