结论:用二分查找,利用旋转数组局部有序性实现o(log n)时间复杂度;关键是比较nums[mid]与nums[right]判断最小值所在区间,避免暴力o(n)扫描。

直接说结论:用二分查找,但判断逻辑和普通有序数组不同;关键在于利用“旋转点左侧元素都 ≥ 右侧最小值”这一性质,每次排除掉不可能含最小值的那一半。
为什么不能直接调用 std::min_element
虽然能跑通,但时间复杂度是 O(n),浪费了数组“局部有序”的结构。旋转排序数组本质是两个升序段拼接(如 [4,5,6,0,1,2]),存在 O(log n) 解法。暴力扫描在面试或大规模数据下会被质疑设计意识。
- 面试中写
std::min_element基本等于放弃考察点 - 实际工程中若数组极小(
size ),倒可接受;否则二分更稳 - 注意:数组可能未旋转(即单调升序),此时最小值在首位置,二分仍要兼容
std::lower_bound 不能直接用,得手写二分逻辑
std::lower_bound 要求整个范围严格升序,而旋转数组不满足该前提,强行传入会导致未定义行为(常见表现是返回错误迭代器或越界)。必须自己实现判断条件:
- 比较
nums[mid]和nums[right](不是nums[left])——因为右端能稳定反映“哪边有断点” - 若
nums[mid] > nums[right],说明最小值在右半段(left = mid + 1) - 若
nums[mid] ,说明最小值在左半段(<code>right = mid) - 若相等(
nums[mid] == nums[right]),无法判断,只能收缩右边界(right--)——这是唯一需要线性退化的场景(如[1,1,1,0,1])
int findMin(vector<int>& nums) {
int left = 0, right = nums.size() - 1;
while (left nums[right]) {
left = mid + 1;
} else if (nums[mid] <h3>容易忽略的边界与重复元素处理</h3>
<p>很多实现卡在重复元素上,比如 <code>[2,2,2,0,1]</code> 或 <code>[1,0,1,1,1]</code>。问题出在把 <code>nums[mid] == nums[left]</code> 当作分支依据——这不可靠,因为左端可能在长平台段里。必须依赖右端做判断,且允许 <code>right--</code>。</p>
<ul>
<li>永远不要用 <code>nums[mid]</code> 和 <code>nums[left]</code> 比较来决定方向</li>
<li>当 <code>nums[mid] == nums[right]</code>,只安全的做法是缩小右边界,不能跳过 <code>mid</code>
</li>
<li>循环条件用 <code>left (非 <code>),避免死循环;退出时 <code>left == right</code>,直接返回</code></code>
</li>
<li>空数组需提前检查,题目通常保证非空,但生产代码建议加 <code>assert(!nums.empty())</code>
</li>
</ul>
<p>最麻烦的其实是相等情况的处理——它让最坏时间复杂度退化到 <code>O(n)</code>,但平均仍是 <code>O(log n)</code>。如果业务明确无重复,可以去掉 <code>else</code> 分支,逻辑就干净得多。</p></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











