🌱博主简介:大一计科生,努力学习Java中!热爱写博客~预备程序媛
📜所属专栏:LeetCode每日一题–进击大厂
✈往期博文回顾: 【JavaSE】保姆级教程|1万字+10张图学会类与对象–建议收藏
🕵️♂️近期目标:成为千粉小博主。
🌺“再牛的程序员也是从小白开始,既然开始了,就全身心投入去学习技术”
力扣每日刷题--35
- 题目描述
- 解题思路
- 代码实现--Java
- 总结&易错
题目描述
35. 搜索插入位置
解题思路
-
题型:数组、二分查找(变式)—寻找第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<right)(不考虑所谓的什么闭区间,就仅仅代表它本身的含义:当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要好理解一些,虽然两者达到的效果是一样的)
代码实现–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,此时可以缩小区间,即不会发生死循环。
-
【重点】二分法的关键是
缩小区间
,死循环发生的原因是某次循环没有缩小区间导致二分失败。 -
【重点】此题right设置为array.length的原因是array.length也有可能是问题答案
-
【重点】将二分查找的判断条件写成
while(left<right)
,代表的是搜索区间为[left,right],一旦left==right,即此时找到问题答案,立即跳出循环。 -
【重点】循环不变量:在[left,right]搜索答案
👩🎨write in the end:
- 🙇♀️建立此专栏的初衷是为了监督自己每天认真刷一个题,积少成多。并把自己每次刷题的思路、收获以博文的形式分享出来,帮助更多人,以及方便后续复习。如果有兴趣的同学可以订阅此专栏,我们一起刷题,一起交流,进步和学习!专栏:LeetCode每日一题–进击大厂