找到目标值的第一次出现和最后一次出现的位置,同样要求 log ( n ) 下完成。
先分享 leetcode 提供的两个解法。
解法一 线性扫描
从左向右遍历,一旦出现等于 target 的值就结束,保存当前下标。如果从左到右没有找到 target,那么就直接返回 [ -1 , -1 ] 就可以了,因为从左到右没找到,那么从右到左也一定不会找到的。如果找到了,然后再从右到左遍历,一旦出现等于 target 的值就结束,保存当前下标。
时间复杂度是 O(n)并不满足题意,但可以了解下这个思路,从左到右,从右到左之前也遇到过。
publicint[] searchRange(int[] nums, inttarget) { int[] targetRange= {-1, -1}; // 从左到右扫描for (inti=0; i<nums.length; i++) { if (nums[i] ==target) { targetRange[0] =i; break; } } // 如果之前没找到,直接返回 [ -1 , -1 ]if (targetRange[0] ==-1) { returntargetRange; } //从右到左扫描for (intj=nums.length-1; j>=0; j--) { if (nums[j] ==target) { targetRange[1] =j; break; } } returntargetRange; }
时间复杂度:O(n)。
空间复杂度:O(1)。
解法二 二分查找
让我们先看下正常的二分查找。
intstart=0; intend=nums.length-1; while (start<=end) { intmid= (start+end) /2; if (target==nums[mid]) { returnmid; } elseif (target<nums[mid]) { end=mid-1; } else { start=mid+1; } }
二分查找中,我们找到 target 就结束了,这里我们需要修改下。
我们如果找最左边等于 target 的值,找到 target 时候并不代表我们找到了我们所需要的,例如下边的情况,
此时 target == nums [ mid ] ,但由于我们改成了 end = mid - 1,所以继续更新,end 就到了 mid 的左边,此时 start > end 了,就会走出 while 循环, 我们要找的值刚好就是 start 指向的了。那么我们修改的代码如下:
while (start<=end) { intmid= (start+end) /2; if (target==nums[mid]) { end=mid-1; } elseif (target<nums[mid]) { end=mid-1 ; } else { start=mid+1; } }
找右边的同样的分析思路,就是判断需要丢弃哪一边。
所以最后的代码就出来了。leetcode 中是把找左边和找右边的合并起来了,本质是一样的。
publicint[] searchRange(int[] nums, inttarget) { intstart=0; intend=nums.length-1; int[] ans= { -1, -1 }; if (nums.length==0) { returnans; } while (start<=end) { intmid= (start+end) /2; if (target==nums[mid]) { end=mid-1; } elseif (target<nums[mid]) { end=mid-1; } else { start=mid+1; } } //考虑 tartget 是否存在,判断我们要找的值是否等于 target 并且是否越界if (start==nums.length||nums[ start ] !=target) { returnans; } else { ans[0] =start; } ans[0] =start; start=0; end=nums.length-1; while (start<=end) { intmid= (start+end) /2; if (target==nums[mid]) { start=mid+1; } elseif (target<nums[mid]) { end=mid-1; } else { start=mid+1; } } ans[1] =end; returnans; }
时间复杂度:O(log(n))。
空间复杂度:O(1)。