34.在排序数组中查找元素的第一个和最后一个位置
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]
提示:
0 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
nums
是一个非递减数组-10^9 <= target <= 10^9
34.2 题解
二分查找两次
// 方法一:二分查找两次,分别查找开始位置和结束位置
static int[] SearchRange1(int[] nums, int target)
{
// 查找目标值的开始位置
int start = FindBound(nums, target, true);
// 查找目标值的结束位置
int end = FindBound(nums, target, false);
// 返回结果
return new int[] { start, end };
}
// 查找目标值的边界位置
static int FindBound(int[] nums, int target, bool isFirst)
{
int left = 0; // 左边界初始化为0
int right = nums.Length - 1; // 右边界初始化为数组最后一个元素的索引
int bound = -1; // 初始化边界值为-1
while (left <= right) // 当左边界小于等于右边界时进行循环
{
int mid = left + (right - left) / 2; // 计算中间位置
if (nums[mid] == target) // 如果中间位置的元素等于目标值
{
bound = mid; // 更新边界值为中间位置
if (isFirst) // 如果查找的是开始位置
{
right = mid - 1; // 更新右边界为中间位置减1
}
else // 如果查找的是结束位置
{
left = mid + 1; // 更新左边界为中间位置加1
}
}
else if (nums[mid] < target) // 如果中间位置的元素小于目标值
{
left = mid + 1; // 更新左边界为中间位置加1
}
else // 如果中间位置的元素大于目标值
{
right = mid - 1; // 更新右边界为中间位置减1
}
}
return bound; // 返回边界值
}
34.3 代码
using System;
class Program
{
static void Main()
{
#region 题目
// 给你一个按照非递减顺序排列的整数数组 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]
// 提示:
// 0 <= nums.length <= 105
// -109 <= nums[i] <= 109
// nums 是一个非递减数组
// -109 <= target <= 109
#endregion
#region 测试
// 示例 1
int[] nums1 = { 5, 7, 7, 8, 8, 10 };
int target1 = 8;
int[] result1 = SearchRange1(nums1, target1);
Console.WriteLine($"示例1 方法1 输出:[{result1[0]}, {result1[1]}]");
// 示例 2
int[] nums2 = { 5, 7, 7, 8, 8, 10 };
int target2 = 6;
int[] result2 = SearchRange1(nums2, target2);
Console.WriteLine($"示例2 方法1 输出:[{result2[0]}, {result2[1]}]");
// 示例 3
int[] nums3 = { };
int target3 = 0;
int[] result3 = SearchRange1(nums3, target3);
Console.WriteLine($"示例3 方法1 输出:[{result3[0]}, {result3[1]}]");
#endregion
}
#region 答案
// 方法一:二分查找两次,分别查找开始位置和结束位置
static int[] SearchRange1(int[] nums, int target)
{
// 查找目标值的开始位置
int start = FindBound(nums, target, true);
// 查找目标值的结束位置
int end = FindBound(nums, target, false);
// 返回结果
return new int[] { start, end };
}
// 查找目标值的边界位置
static int FindBound(int[] nums, int target, bool isFirst)
{
int left = 0; // 左边界初始化为0
int right = nums.Length - 1; // 右边界初始化为数组最后一个元素的索引
int bound = -1; // 初始化边界值为-1
while (left <= right) // 当左边界小于等于右边界时进行循环
{
int mid = left + (right - left) / 2; // 计算中间位置
if (nums[mid] == target) // 如果中间位置的元素等于目标值
{
bound = mid; // 更新边界值为中间位置
if (isFirst) // 如果查找的是开始位置
{
right = mid - 1; // 更新右边界为中间位置减1
}
else // 如果查找的是结束位置
{
left = mid + 1; // 更新左边界为中间位置加1
}
}
else if (nums[mid] < target) // 如果中间位置的元素小于目标值
{
left = mid + 1; // 更新左边界为中间位置加1
}
else // 如果中间位置的元素大于目标值
{
right = mid - 1; // 更新右边界为中间位置减1
}
}
return bound; // 返回边界值
}
#endregion
}
34.4 运行结果
示例1 方法1 输出:[3, 4]
示例2 方法1 输出:[-1, -1]
示例3 方法1 输出:[-1, -1]
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com