题目:力扣https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/
class Solution {
public int[] searchRange(int[] nums, int target) {
if(nums.length==1){
if(nums[0]==target){
return new int[]{0,0};
}else{
return new int[]{-1,-1};
}
}else if(nums.length==2){
if(nums[0]==target && nums[1]==target){
return new int[]{0,1};
}else if(nums[0]!=target && nums[1]==target){
return new int[]{1,1};
}else if(nums[0]==target && nums[1]!=target){
return new int[]{0,0};
}else{
return new int[]{-1,-1};
}
}
int left = 0;
int right = nums.length-1;
int l = -1;
int r = -1;
//先找left
while(left<right){
int mid = (left+right)/2;
if(nums[mid]==target){
right = mid;
}else if(nums[mid]>target){
right = mid-1;
}else if(nums[mid]<target){
left = mid+1;
}
if(nums[left]==target){
l = left;
}
}
//再找right;
right = nums.length-1;
while(left<=right){
int mid = (left+right)/2;
if(nums[mid]==target){
left = mid;
}else if(nums[mid]>target){
right = mid-1;
}else if(nums[mid]<target){
left = mid+1;
}
if(nums[right]==target || right-1>=0 && nums[right-1]==target){
r = (((left+right)/2)<right && nums[right]!=target)?right-1:right;
break;
}
}
if(l!=-1){
return new int[]{l,r};
}else{
return new int[]{-1,-1};
}
}
}
思路:这题一开始一直超时,没想到只要AC了就超于100%了哈哈哈,挺开心的。一开始一直超时,其中很重要的一个原因是我使用了线性查找,哪怕只是局部使用。题目要求时间复杂度是,一开始我并没有留意这个点。我最初的思路是,二分查找+线性查找,但是只要一用到线性查找时间复杂度就不符合题目要求。因此,只能一直用二分查找直至找到答案。
1.特殊情况一:数组长度为1。只需要判断这个元素是否为target就好,是则返回[0,0],否则返回[-1,-1]。
if(nums.length==1){
if(nums[0]==target){
return new int[]{0,0};
}else{
return new int[]{-1,-1};
}
}/*else if(){
//......
}*/
2.特殊情况二:数组长度为2。仅仅有四种情况,全部一一枚举出来。若数组中两个位置的数都与target不同,则返回[-1,-1];若仅第一个位置的数与target相同,则返回[0,0];若仅第二个位置的数与target相同,则返回[1,1];若数组中两个位置的数都与target相同,则返回[0,1]。
if(nums.length==2){
if(nums[0]==target && nums[1]==target){
return new int[]{0,1};
}else if(nums[0]!=target && nums[1]==target){
return new int[]{1,1};
}else if(nums[0]==target && nums[1]!=target){
return new int[]{0,0};
}else{
return new int[]{-1,-1};
}
}
3.接下来就是正式开始操作了。left和right是左右边界,l和r是最后存储答案的变量先赋值为-1。
int left = 0;
int right = nums.length-1;
int l = -1;
int r = -1;
4.先确定左边界l的值。利用二分法查找,确定左边界的值。首先,先求中间位置mid的值(这个中间位置不一定是严谨的,后续会详细说明)。若中间位置的值等于target,此时有两种情况有可能发生(1)此处已经是左边界(2)左边界在此处的左侧,这两种情况我们此时仍然不能确定,所以需要将右边界移动到中间位置,继续进行判断。直到left和right重合时,既可以找出左边界,用变量l储存。
while(left<right){
int mid = (left+right)/2;
if(nums[mid]==target){
right = mid;
}else if(nums[mid]>target){
right = mid-1;
}else if(nums[mid]<target){
left = mid+1;
}
if(nums[left]==target){
l = left;
}
}
5.然后再确定右边界r的值。仍然是利用二分法查找右边界,具体方法与第4步相似。求右边界有一个难点,与求左边界有一个不同。因为int类型没有小数,当除2时出现小数会自动向下取整,所以在求左边界时会很顺利,而在求右边界时自动向下取整会出现“卡死”现象(例如:left为1,right为2,1+2=3,3/2=1,因为"letf = mid;"所以left又为1,mid的值也一直为1,一直无限循环下去......)此时我们需要另外作一个判断,我用的时三目运算符a=b?c:d;这个符号的意思时,若b为true则将c的值赋值给a,若为false则将d的值赋值给a。回到题目,若(left+right)/2小于right且nums[nums]不等于target,则出现了“卡死”现象,我们将right-1赋值给r;否则,即不是“卡死”现象,直接将right赋值给r即可。最后补个break,避免出现“卡死”一直在此处死循环。
right = nums.length-1;
while(left<=right){
int mid = (left+right)/2;
if(nums[mid]==target){
left = mid;
}else if(nums[mid]>target){
right = mid-1;
}else if(nums[mid]<target){
left = mid+1;
}
if(nums[right]==target || right-1>=0 && nums[right-1]==target){
r = (((left+right)/2)<right && nums[right]!=target)?right-1:right;
break;
}
}
6.选择正确的值返回。来到这一步,证明已经走过了上面最难的一步了。若上述代码已经找到了左右边界,则l和r的值肯定已经改变了;若不存在左右边界,则按题意返回(-1,-1)即可。
if(l!=-1){
return new int[]{l,r};
}else{
return new int[]{-1,-1};
}