34. 在排序数组中查找元素的第一个和最后一个位置(二分)
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array
题目
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。
你的算法时间复杂度必须是 O(log n) 级别。
如果数组中不存在目标值,返回 [-1, -1]。
示例 1:
输入: nums = [5,7,7,8,8,10], target = 8
输出: [3,4]
示例 2:
输入: nums = [5,7,7,8,8,10], target = 6
输出: [-1,-1]
代码
//从两侧分别二分寻找目标在数组中第一个和最后一个位置
class Solution {
public int[] searchRange(int[] nums, int target) {
int len = nums.length;
int left = 0;
int right = len - 1;
if(len == 0){
return new int[]{-1, -1};
}
int first = findFirstLOC(nums, target);//先从前往后找到target第一个位置,如果不存在就可以返回-1了
if(first == -1){
return new int[]{-1, -1};
}
int last = findLastLOC(nums, target);//第一个下标存在才会找最后一个下标,所以最不济只有一个等于target
return new int[]{first, last};
// return new int[]{last, first};
}
public int findFirstLOC(int[] nums, int target) {//从前往后找到target第一个位置
int len = nums.length;
int left = 0;
int right = len - 1;
while(left < right){
int mid = left + (right - left) / 2;
if(nums[mid] < target){//小于target一定不是解
//下一个搜索区间为[mid + 1, right]
left = mid + 1;
}
else{
right = mid;
}
}
if(nums[left] == target){
return left;
}
else{
return -1;
}
}
public int findLastLOC(int[] nums, int target) {//从后往前找到target最后一个位置
int len = nums.length;
int left = 0;
int right = len - 1;
while(left < right){
int mid = left + (right - left + 1) / 2;//left = mid时,一定注意上取整
if(nums[mid] > target){//大于target一定不是解
//下一个搜索区间为[left, mid -1]
right = mid - 1;
}
else{//小于target时
left = mid;
}
}
return left;
}
}
思想
题目要求算法时间复杂度必须是 O(log n) 级别,又数组递增有序,立刻想到二分搜索,根据二分查找的思想。先从左侧搜索target找寻第一个下标,如果没找到返回[-1, -1];再从右侧搜索target的最后一个下标,返回[leftLOC, rightLOC]即可。
整体思想比较简明,但是代码细节很繁琐,至今还有些不明朗。
关于二分查找的问题
• 掌握二分查找的两种思路重点:
• 思路 1:在循环体内部查找元素:while (left <= right);
• 思路 2:在循环体内部排除元素:while (left < right)。
• 全部使用左闭右闭区间,不建议使用左闭右开区间,反而使得问题变得复杂;
这里如何区分使用场景呢?
int mid = left + (right - left) / 2;//下取整
int mid = left + (right - left + 1) / 2;//left = mid时,一定注意上取整
两者如何区分?