旋转数组最小值的本质是二分查找变种而非传统分治,关键依据nums[mid]与nums[right]比较:大于则最小值在右半,小于则在左半(含mid),等于则right--缩小范围。

旋转数组最小值的分治本质是什么
旋转数组的最小值不是靠“分治”这个名词唬人,而是利用数组局部有序的性质做二分裁剪——它本质是二分查找的变种,不是传统意义的分治(比如归并排序那种递归拆两半再合并)。强行套用递归分治不仅没优势,还容易绕晕。关键判断依据是:nums[mid] 和 nums[right] 的大小关系,它能告诉你最小值在左半还是右半。
为什么nums[mid] > nums[right]就说明最小值在右半
因为旋转数组由两个升序段拼成,右端点 nums[right] 是后半段的末尾,也是整个数组的最小值候选者之一。当 nums[mid] > nums[right],说明从 mid 到 right 这段必然跨过旋转点(否则这段该是升序,nums[mid] 不可能大于 nums[right]),所以最小值一定落在 [mid + 1, right] 区间内。
反之,若 nums[mid] < nums[right],说明 [mid, right] 完全有序,最小值不可能在 mid 右侧(因为 nums[mid] 已比 nums[right] 小),只能去左半找,但注意:mid 本身可能是最小值,所以右边界缩到 mid,不能跳过。
-
nums[mid] == nums[right]是唯一模糊情况,无法判断哪边有序,只能right--缩小范围(最坏退化为 O(n)) - 切记不要用
nums[left]做比较基准——左端可能远大于最小值,信息量不足 - 边界更新必须严格:左边界用
left = mid + 1,右边界用right = mid(不是mid - 1),否则可能漏掉mid
标准实现中容易写错的三处细节
很多人照着二分模板改,结果死循环或越界。核心陷阱都在边界和终止条件:
立即学习“C++免费学习笔记(深入)”;
- 循环条件必须是
left < right,不是<=—— 当left == right时已定位到答案,多跑一次会出错 -
mid计算要用left + (right - left) / 2,避免left + right溢出(尤其数组很大时) - 当
nums[mid] > nums[right],更新left = mid + 1;当nums[mid] < nums[right],更新right = mid;相等时只动right,不动left
示例代码片段(无递归,纯迭代):
int findMin(vector<int>& nums) {
int left = 0, 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]) {
right = mid;
} else {
right--;
}
}
return nums[left];
}有重复元素时性能会掉到 O(n),但无法避免
当数组全是相同数字(如 [2,2,2,0,2,2,2]),每次 nums[mid] == nums[right],只能靠 right-- 一步步试,最坏遍历全部元素。这不是写法问题,是信息缺失导致的理论下限——你无法仅凭局部比较确认旋转点在哪。如果业务场景重复率极高,且对最坏性能敏感,得考虑预处理去重或换数据结构,而不是硬优化这个分支。
真正要注意的是:别为了“看起来更分治”而写递归版本,它既不简洁也不提速,还增加栈开销和理解成本。原地迭代+正确边界控制,才是这题的务实解法。


















