力扣hot100-lc34在排序数组中查找元素的第一个和最后一个位置/lc153寻找旋转排序数组中的最小值/lc33搜索旋转排序数组
34.在排序数组中查找元素的第一个和最后一个位置
刷题:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
给你一个按照非递减顺序排列的整数数组 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
🧠 题目思路总结:
本题要求在有序数组中找出目标值的起始和终止位置,且必须在 O(log n) 时间复杂度内完成,因此可以使用二分查找来实现。
我们使用一个辅助函数 lowerBound(nums, target)
实现 找第一个大于等于 target 的位置。
- 起始位置是
lowerBound(target)
; - 终止位置是
lowerBound(target + 1) - 1
(即小于 target+1 的最后一个位置); - 如果
start == nums.length || nums[start] != target
,说明 target 不存在。
✅ 一句话总结解法:
用两次二分查找分别定位 target 的第一个出现位置和最后一个位置。
💻 【Java 代码】
class Solution {public int[] searchRange(int[] nums, int target) {// 查找目标值的起始位置int start = lowerBound(nums, target);// 如果起始位置超出数组范围 或 nums[start] 不是目标值,说明不存在if (start == nums.length || nums[start] != target) {return new int[]{-1, -1};}// 查找目标值的结束位置:等于 lowerBound(target+1) 再往前一步int end = lowerBound(nums, target + 1) - 1;return new int[]{start, end};}// lowerBound:查找第一个大于等于 target 的索引private int lowerBound(int[] nums, int target) {int left = 0;int right = nums.length - 1;// 标准二分写法while (left <= right) {int mid = left + (right - left) / 2;// 如果中间值小于目标,说明目标在右边if (nums[mid] < target) {left = mid + 1;} else {// 否则向左收缩right = mid - 1;}}// 最终返回的是第一个 >= target 的位置return left;}
}
📌 示例运行演示:
- 输入:
nums = [5,7,7,8,8,10], target = 8
start = lowerBound(8) = 3
end = lowerBound(9) - 1 = 5 - 1 = 4
- 返回
[3, 4]
- 输入:
nums = [5,7,7,8,8,10], target = 6
start = lowerBound(6) = 1
nums[1] = 7 != 6
→ 返回[-1, -1]
162. 寻找峰值(false)
刷题:https://leetcode.cn/problems/find-peak-element/
峰值元素是指其值严格大于左右相邻值的元素。
给你一个整数数组 nums
,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。
你可以假设 nums[-1] = nums[n] = -∞
。
你必须实现时间复杂度为 O(log n)
的算法来解决此问题。
示例 1:
输入:nums = [1,2,3,1]
输出:2
解释:3 是峰值元素,你的函数应该返回其索引 2。
示例 2:
输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5
解释:你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。
🔍【题目讲解】
峰值元素定义为:该元素大于它左右相邻的元素。
基于二分查找的 局部单调性分析
- 比较中点与右侧元素
- 若
nums[mid] < nums[mid+1]
,说明右侧有更大的值,峰值在右侧,收缩左边界到mid+1
。 - 否则(
nums[mid] > nums[mid+1]
),说明峰值在左侧或mid
处,收缩右边界到mid
。
- 若
- 调整循环条件
将while(left <= right)
改为while(left < right)
,确保循环结束时left == right
,避免越界且正确收敛到峰值索引。
💡【核心思路】——一句话总结:
利用二分查找判断中间值与右侧元素的大小关系,不断缩小搜索区间,最终锁定峰值索引。
📌【示例说明】
- 示例1:
[1,2,3,1]
mid = 1 → nums[1]=2 < nums[2]=3 → 搜索右半边
,最后找到索引2的元素3是峰值。 - 示例2:
[1,2,1,3,5,6,4]
多个峰值,比如索引1处的2、索引5处的6,算法返回任意一个都正确。
✅【Java代码实现】:
public class Solution {public int findPeakElement(int[] nums) {int left = 0, right = nums.length - 1;while (left < right) {int mid = left + (right - left) / 2;// 如果中间值比右边大,峰值在左边(包含mid)if (nums[mid] > nums[mid + 1]) {right = mid;} else { // 否则峰值在右边left = mid + 1;}}return left; // 最终left == right,指向一个峰值元素}
}
153. 寻找旋转排序数组中的最小值
https://leetcode.cn/problems/find-minimum-in-rotated-sorted-array/
已知一个长度为 n
的数组,预先按照升序排列,经由 1
到 n
次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7]
在变化后可能得到:
- 若旋转
4
次,则可以得到[4,5,6,7,0,1,2]
- 若旋转
7
次,则可以得到[0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], ..., a[n-1]]
旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], ..., a[n-2]]
。
给你一个元素值 互不相同 的数组 nums
,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。
示例 1:
输入:nums = [3,4,5,1,2]
输出:1
解释:原数组为 [1,2,3,4,5] ,旋转 3 次得到输入数组。
示例 2:
输入:nums = [4,5,6,7,0,1,2]
输出:0
解释:原数组为 [0,1,2,4,5,6,7] ,旋转 4 次得到输入数组。
示例 3:
输入:nums = [11,13,15,17]
输出:11
解释:原数组为 [11,13,15,17] ,旋转 4 次得到输入数组。
提示:
n == nums.length
1 <= n <= 5000
-5000 <= nums[i] <= 5000
nums
中的所有整数 互不相同nums
原来是一个升序排序的数组,并进行了1
至n
次旋转
🔍【题目讲解】
一个原本递增的数组经过多次旋转,形成了一个新的数组。由于数组元素互不相同,我们可以借助二分查找快速找出最小元素的位置。
💡【核心思路】——一句话总结:
通过比较中间值与右端值的大小判断最小值所在区间,使用二分查找逐步缩小范围。
✅【Java代码实现】:
public class Solution {public int findMin(int[] nums) {int left = 0, right = nums.length - 1;while (left < right) {int mid = left + (right - left) / 2;// 如果中间值比右边大,最小值一定在右半区if (nums[mid] > nums[right]) {left = mid + 1;} else { // 否则最小值在左半区(包括mid)right = mid;}}// 循环结束时,left == right,指向最小值return nums[left];}
}
📌【示例说明】
- 输入:
[3,4,5,1,2]
mid=2 nums[mid]=5 > nums[right]=2
,所以最小值在右边;- 缩小范围后最终定位到索引3的
1
。
- 输入:
[4,5,6,7,0,1,2]
- 二分查找不断右移,最终找到最小值为
0
。
- 二分查找不断右移,最终找到最小值为
- 输入:
[11,13,15,17]
- 没有旋转,最小值就在左端,返回
11
。
- 没有旋转,最小值就在左端,返回
33. 搜索旋转排序数组
https://leetcode.cn/problems/search-in-rotated-sorted-array/
整数数组 nums
按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums
在预先未知的某个下标 k
(0 <= k < nums.length
)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]]
(下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7]
在下标 3
处经旋转后可能变为 [4,5,6,7,0,1,2]
。
给你 旋转后 的数组 nums
和一个整数 target
,如果 nums
中存在这个目标值 target
,则返回它的下标,否则返回 -1
。
你必须设计一个时间复杂度为 O(log n)
的算法解决此问题。
示例 1:
输入:nums = [4,5,6,7,0,1,2], target = 0
输出:4
示例 2:
输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1
示例 3:
输入:nums = [1], target = 0
输出:-1
提示:
1 <= nums.length <= 5000
-104 <= nums[i] <= 104
nums
中的每个值都 独一无二- 题目数据保证
nums
在预先未知的某个下标上进行了旋转 -104 <= target <= 104
🔍【题目讲解】
【有序区间判断】:
-
左半有序的条件:nums[left] <= nums[mid]。例如,数组 [4,5,6,7,0,1,2] 中,当 mid=3 时,左半区间 [4,5,6,7] 有序。
-
右半有序的条件:若左半无序,则右半一定有序。例如,当 mid=4 时,右半区间 [0,1,2] 有序。
【目标值范围决策】:
-
左半有序时:若 target 在 [nums[left], nums[mid]) 范围内,则继续搜索左半;否则转向右半。
-
右半有序时:若 target 在 (nums[mid], nums[right]] 范围内,则继续搜索右半;否则转向左半。
💡【核心思路】——一句话总结:
在二分过程中判断哪一边是有序的,再判断 target 是否在该有序区间内,从而决定向哪边搜索。
✅【Java代码实现】:
class Solution {public int search(int[] nums, int target) {int left = 0, right = nums.length - 1;while (left <= right) {int mid = left + (right - left) / 2;if (nums[mid] == target) {return mid; // 直接命中目标值[4,8](@ref)}// 判断左半部分是否有序if (nums[left] <= nums[mid]) { if (nums[left] <= target && target < nums[mid]) {right = mid - 1; // 目标在左半有序区间[4,8](@ref)} else {left = mid + 1; // 目标在右半无序区间}} else { // 右半部分有序if (nums[mid] < target && target <= nums[right]) {left = mid + 1; // 目标在右半有序区间[4,8](@ref)} else {right = mid - 1; // 目标在左半无序区间}}}return -1; // 未找到目标值}
}
📌【示例说明】
- 输入:
nums = [4,5,6,7,0,1,2]
,target = 0
mid = 3, nums[3]=7
,左边有序[4,5,6,7]
,但 0 不在其中 → 继续搜索右半边 → 找到索引 4。
- 输入:
nums = [4,5,6,7,0,1,2]
,target = 3
- 无论怎么查找都不满足条件,最终返回 -1。
- 输入:
nums = [1]
,target = 0
- 只有一个元素且不等于目标值 → 返回 -1。