LeetCode 34.在排序数组中查找元素的第一个和最后一个位置
1. 题目描述
给你一个按照 非递减顺序 排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中 不存在 目标值 target,返回 [-1, -1]。
你必须设计并实现 时间复杂度为 O(log n) 的算法解决此问题。
示例 1:
输入:nums = [5,7,7,8,8,10], target = 8
输出:[3,4]
示例 2:
输入:nums = [5,7,7,8,8,10], target = 6
输出:[-1,-1]
示例 3:
输入:nums = [], target = 0
输出:[-1,-1]
2. 题目分析
题目描述十分清晰,给你一个数组,让你找到目标值 target 的开始和结束位置。很显然数组中可能出现多个 target ,我们的目标是找到 第一个 出现 target 的下标和 最后一个 出现 target 的下标。我们很容易想到一个方法,直接遍历一遍数组找到 target 。但题目给了一个关键要求,算法时间复杂度必须是 O(log n) 。看到 查找 、O(log n) ,可以断定此题必然是一个经典的 二分查找。
3. 二分查找
什么是二分查找?
实质上就是对要查找的数组,从中间开始向头部和尾部进行查找,判断条件是目标值 target 和中间值 mid 进行比较,若 target > mid,则 target 必然在 mid 右侧,反之则在左侧,这就要求查找的数组是有序的,以升序为例
实现代码如下:
public int binarySearch(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
while (left <= right){
int mid = (left + right) >>> 1;
if (target < nums[mid]){
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}
else
return mid;
}
return -1;
}
但二分查找只是找到了 target ,至于是第一个还是最后一个亦或中间的就不得而知了,在此基础上,我们需要对代码进行一些改进…
4. searchLeft 和 searchRight
searchLeft 即找到目标 target 最左边下标,searchRight 即找到目标 target 最右边下标,以searchLeft为例
实现代码如下:
public int searchLeft(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int flag = -1;
while (left <= right){
int mid = (left + right) >>> 1;
if (target < nums[mid]){
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}
else {
flag = mid;
right = mid - 1;
}
}
return flag;
}
其实就是在找到 target 后不返回值,继续查找,直到最左侧没有 target,searchRight 也是同样的思路。
5. 整体的代码实现
class Solution {
public int[] searchRange(int[] nums, int target) {
int[] arr = new int[]{searchLeft(nums,target),searchRight(nums,target)};
return arr;
}
public int searchLeft(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int flag = -1;
while (left <= right){
int mid = (left + right) >>> 1;
if (target < nums[mid]){
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}
else {
flag = mid;
right = mid - 1;
}
}
return flag;
}
public int searchRight(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int flag = -1;
while (left <= right){
int mid = (left + right) >>> 1;
if (target < nums[mid]){
right = mid - 1;
}else if (nums[mid] < target) {
left = mid + 1;
}
else {
flag = mid;
left = mid + 1;
}
}
return flag;
}
}