题目:
Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.
Your algorithm’s runtime complexity must be in the order of O(log n).
If the target is not found in the array, return [-1, -1].
Example 1:
Input: nums = [5,7,7,8,8,10], target = 8
Output: [3,4]
Example 2:
Input: nums = [5,7,7,8,8,10], target = 6
Output: [-1,-1]
解法1:
与正常的二分查找不同,由于此题要求的是最开始的元素和最后的元素,所以我们在二分查找时如果找到目标元素,我们首先记录下他的下标,如果是找第一个,我们就往左移,如果是找最后一个,我们就往右移
例如:5 7 7 8 9 10
先求first:mid = 7, 7 < 8, 舍弃左半边, lo = 3,hi = 5,lo <= hi 成立,继续迭代,mid = 8,记录此时的下标为4,由于是找第一个元素,所以我们要舍弃右半边,lo = 3,hi = 3,lo <= hi 成立,继续迭代,mid = 8,记录此时的下标为3,由于是找第一个元素,所以我们要舍弃右半边,lo = 3,hi = 2,,lo <= hi 不成立,结束循环,此时我们得到第一个元素的下标3
求last的情况和first一样,只不过我们在找到目标元素后向右移,舍弃左半边
我们还要考虑找不到的情况 所以我们预设idx为-1,结束后如果下标没有更新则返回-1
c++:
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res;
int first = findFirst(nums,target);
int last = findLast(nums,target);
res.push_back(first);
res.push_back(last);
return res;
}
int findFirst(vector<int>& nums, int target){
int n = nums.size();
int lo = 0;
int hi = n - 1;
int idx = -1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target){
hi = mid - 1;
idx = mid;
} else if(nums[mid] > target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return idx;
}
int findLast(vector<int>& nums, int target){
int n = nums.size();
int lo = 0;
int hi = n - 1;
int idx = -1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target){
lo = mid + 1;
idx = mid;
} else if(nums[mid] > target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return idx;
}
};
java:
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[2];
int first = findFirst(nums,target);
int last = findLast(nums,target);
res[0] = first;
res[1] = last;
return res;
}
public int findFirst(int[] nums, int target){
int n = nums.length;
int lo = 0;
int hi = n - 1;
int idx = -1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target){
hi = mid - 1;
idx = mid;
} else if(nums[mid] > target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return idx;
}
public int findLast(int[] nums, int target){
int n = nums.length;
int lo = 0;
int hi = n - 1;
int idx = -1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] == target){
lo = mid + 1;
idx = mid;
} else if(nums[mid] > target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return idx;
}
}
python:
python list不能赋值,只能append
class Solution(object):
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
res = []
first = self.findFirst(nums,target)
last = self.findLast(nums,target)
res.append(first)
res.append(last)
return res
def findFirst(self, nums, target):
n = len(nums)
lo = 0
hi = n - 1
idx = -1
while lo <= hi:
mid = lo + (hi - lo) / 2
if nums[mid] == target:
hi = mid - 1
idx = mid
elif nums[mid] > target:
hi = mid - 1
else:
lo = mid + 1
return idx
def findLast(self, nums, target):
n = len(nums)
lo = 0
hi = n - 1
idx = -1
while lo <= hi:
mid = lo + (hi - lo) / 2
if nums[mid] == target:
lo = mid + 1
idx = mid
elif nums[mid] > target:
hi = mid - 1
else:
lo = mid + 1
return idx
解法2:
lower_bound() 找第一个大于等于val的位置
upper_bound() 找第一个大于val的位置
在本题中我们可以用lower_bound()找第一个位置,用upper_bound() - 1来找最后一个位置
此外我们还要考虑没找到的情况,由于lower_bound() 找第一个大于等于val的位置,所以它必定大于0,如果target大于数组的最大值,它将返回n(数组的长度)
所以我们可以用
if(first < n && nums[first] == target)
来判断是否找到了target
重点是lower_bound()和upper_bound()的实现
lower_bound():
int lower_bound(vector<int>& nums, int target){
int n = nums.size();
int lo = 0;
int hi = n;
while(lo < hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] >= target){
hi = mid;
} else {
lo = mid + 1;
}
}
return lo;
}
该方案lo < hi 且初始空间为[0,n]
[5,7,7,8,8,10], target = 8
mid = 8 >=8,满足条件,说明第一个满足条件的元素一定在mid或者mid的做出,hi = mid
mid = 7,7 < 8,说明第一个大于等于8的一定在mid的右侧, lo = mid + 1
mid = 7,7 < 8,说明第一个大于等于8的一定在mid的右侧, lo = mid + 1
lo = hi结束循环,lo的位置即为第一个大于等于8的位置
[5,7,7,8,8,10], target = 11
mid = 8,8 < 11,说明第一个大于等于11的一定在mid的右侧, lo = mid + 1
mid 为10,·10< 11,说明第一个大于等于11的一定在mid的右侧, lo = mid + 1
lo = hi结束循环,lo的位置即为说明第一个大于等于8的位置
lower_bound()的第二种写法:
循环条件:lo <= hi 初始[0,n-1]
int lower_bound(vector<int>& nums, int target){
int n = nums.size();
int lo = 0;
int hi = n - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] >= target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return lo;
}
[5,7,7,8,8,10], target = 8
mid = 7 7 < 8, lo = mid + 1
mid = 8 8 >= 8, hi = mid - 1不是维持mid
mid = 8 8 >= 8, hi = mid - 1不是维持mid
hi < lo 结束循环返回lo
[5,7,7,8,8,10], target = 11
mid = 7 7 < 11, lo = mid + 1
mid = 8 8 < 11, lo = mid + 1
mid = 10 8 < 11, lo = mid + 1
lo > hi 结束循环,返回lo
upper_bound()函数和lower_bound()函数一样只不过判断条件为>,而不是>=
C++:
C++ vector重载了= 所以可以用res[0] = first;
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
vector<int> res(2,-1);
int n = nums.size();
int first = lower_bound(nums,target);
int last = upper_bound(nums,target) - 1;
cout << first << " " << last <<endl;
if(first < n && nums[first] == target){
res[0] = first;
res[1] = last;
return res;
} else {
return res;
}
}
int lower_bound(vector<int>& nums, int target){
// int n = nums.size();
// int lo = 0;
// int hi = n;
// while(lo < hi){
// int mid = lo + (hi - lo) / 2;
// if(nums[mid] >= target){
// hi = mid;
// } else {
// lo = mid + 1;
// }
// }
// return lo;
int n = nums.size();
int lo = 0;
int hi = n - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] >= target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return lo;
}
int upper_bound(vector<int>& nums, int target){
int n = nums.size();
int lo = 0;
int hi = n;
while(lo < hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] > target){
hi = mid;
} else {
lo = mid + 1;
}
}
return lo;
// int n = nums.size();
// int lo = 0;
// int hi = n - 1;
// while(lo <= hi){
// int mid = lo + (hi - lo) / 2;
// if(nums[mid] > target){
// hi = mid - 1;
// } else {
// lo = mid + 1;
// }
// }
// return lo;
}
};
java:
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] res = new int[2];
res[0] = -1;
res[1] = -1;
int n = nums.length;
int first = lower_bound(nums,target);
int last = upper_bound(nums,target) - 1;
if(first < n && nums[first] == target){
res[0] = first;
res[1] = last;
return res;
} else {
return res;
}
}
public int lower_bound(int[] nums, int target){
int n = nums.length;
int lo = 0;
int hi = n - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] >= target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return lo;
}
public int upper_bound(int[] nums, int target){
int n = nums.length;
int lo = 0;
int hi = n - 1;
while(lo <= hi){
int mid = lo + (hi - lo) / 2;
if(nums[mid] > target){
hi = mid - 1;
} else {
lo = mid + 1;
}
}
return lo;
}
}
python:
python list不能用赋值=
class Solution(object):
def searchRange(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
res = []
n = len(nums)
first = self.lower_bound(nums,target)
last = self.upper_bound(nums,target) - 1
if first < n and nums[first] == target:
res.append(first)
res.append(last)
return res
else:
res.append(-1)
res.append(-1)
return res
def lower_bound(self, nums, target):
n = len(nums)
lo = 0
hi = n - 1
while lo <= hi:
mid = lo + (hi - lo) / 2
if nums[mid] >= target:
hi = mid - 1
else:
lo = mid + 1
return lo
def upper_bound(self, nums, target):
n = len(nums)
lo = 0
hi = n - 1
while lo <= hi:
mid = lo + (hi - lo) / 2
if nums[mid] > target:
hi = mid - 1
else:
lo = mid + 1
return lo