题解 | 旋转数组的最小数字
旋转数组的最小数字
https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba?tpId=295&tags=&title=&difficulty=0&judgeStatus=0&rp=0&sourceUrl=%2Fexam%2Foj
class Solution {
public:
/**
* 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
*
*
* @param nums int整型vector
* @return int整型
*/
int minNumberInRotateArray(vector<int>& nums) {
int left = 0;
int right = nums.size() - 1;
while (left < right) {
int mid = left + (right - left) / 2;
if (nums[mid] > nums[right]) {
// 最小值在右侧
left = mid + 1;
} else if (nums[mid] < nums[right]) {
// 最小值在左侧或就是mid
right = mid;
} else {
// 当 nums[mid] == nums[right] 时,无法判断,缩小右边界
right--;
}
}
return nums[left];
}
};
算法思路 旋转数组由两个有序部分组成,最小值位于第二个有序部分的开头。 使用二分查找,比较中间元素与右端元素: 如果中间元素大于右端元素,说明最小值在右侧,将左指针移动到中间位置右边。 如果中间元素小于右端元素,说明最小值在左侧或就是中间元素,将右指针移动到中间位置。 如果中间元素等于右端元素,无法判断最小值在哪侧,但通过将右指针减一缩小范围,不会错过最小值。 当左指针和右指针相遇时,指向的元素即为最小值。 1. 旋转数组的特性 旋转数组虽然整体不是有序的,但它具有部分有序性: 由两个有序子数组拼接而成 左半部分的所有元素 ≥ 右半部分的所有元素 最小值正好是两个有序子数组的分界点 2. 二分查找的本质 二分查找并不要求整个数组完全有序,只需要能够通过中间元素判断目标值在哪一侧。旋转数组正好满足这个条件: 通过比较 nums[mid] 和 nums[right],可以确定最小值在哪一半 4. 为什么不是其他方法? 暴力搜索 O(n):太慢,不符合 O(logn) 要求 排序 O(nlogn):破坏了原始信息,且比要求的更慢 归并排序 O(nlogn):虽然能排序,但时间复杂度不满足要求 5. 思维过程 识别模式:旋转数组 = 两个有序子数组 寻找突破口:最小值是分界点 利用有序性:虽然整体无序,但局部有序 设计比较策略:通过与端点比较来确定搜索方向 验证可行性:每次迭代都能将搜索范围减半 6. 类似问题模式 这种思路在很多"部分有序"或"旋转"问题中都适用: 在旋转数组中搜索目标值 寻找峰值元素 在山脉数组中查找目标值 核心思想 二分查找的关键在于:能够通过中间元素确定目标在哪一半,而不要求整个数组完全有序。 在旋转数组问题中,我们找到了一个聪明的比较策略(与右端点比较),使得即使数组不完全有序,也能每次排除一半的搜索空间。
二分查找/排序 文章被收录于专栏
特性 二分查找 二分插入排序 目的 在有序集合中快速查找元素 对一个无序集合进行排序 核心 分治,每次将搜索范围减半 在插入排序中,用二分法找插入点 前提 数据必须有序 无特殊前提 时间复杂度 O(log n) O(n²)(但比较次数为 O(n log n)) 空间复杂度 O(1)(迭代) O(1)

查看1道真题和解析