做网站开发学什么/seo搜索引擎优化方法
文章目录
- 题目
- 解题思路
- 代码实现(C++)
- 二分查找
- 扩展
题目
题目来源:leetcode 剑指Offer 11:旋转数组的最小数字
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素。
示例1:
输入: [3,4,5,1,2]
输出: 1
示例2:
输入: [2,2,2,0,1]
输出: 0
示例3:
输入: [2,3,5]
输出: 2
解题思路
旋转前的数组是单调不减的,旋转后的数组会出现0个(将数组中的前0个元素移动到后面)或1个断崖。若没有断崖,数组中第一个元素就是最小值;若有,旋转后的数组仅在断崖处是递减的,且较小值(崖底)是数组的最小值。
没有断崖:
有断崖:
使用二分查找在数组numbers
下标[left..right]
序列内寻找断崖(或判断是否存在断崖)。如果中间元素numbers[mid] < numbers[mid - 1]
,则mid
对应着数组的最小值。
如果numbers[mid]
大于numbers[right]
,说明断崖在右半部。
如果numbers[mid]
小于numbers[right]
,说明断崖在左半部。
如果numbers[mid] == numbers[right]
,则无法判断断崖的位置,需要将right
减1
,重新做判断。
断崖在右半部:
断崖(最小值)在左半部:
如果left > right
时还未找到最小值,该数组无断崖。
代码实现(C++)
int minArray(vector<int>& numbers) {if (numbers.size() < 2) {return numbers.front();}int left = 0;int right = numbers.size() - 1;// 二分查找while (left <= right) {int mid = left + ((right - left) >> 1);// 找到了断崖if (mid > 0 && numbers[mid] < numbers[mid - 1]) {return numbers[mid];}if (numbers[mid] == numbers[right]) {--right;}else if (numbers[mid] < numbers[right]) {right = mid - 1;}else {left = mid + 1;}}// 没有断崖// 使用"return numbers[left];"语句也能通过所有的测试用例return numbers.front(); }
以下代码也能通过所有的测试用例。
class Solution {
public:int minArray(vector<int>& numbers) {if (numbers.size() < 2) {return numbers.front();}int left = 0;int right = numbers.size() - 1;while (left < right) { // 去掉了=符号int mid = left + ((right - left) >> 1);if (mid > 0 && numbers[mid] < numbers[mid - 1]) {return numbers[mid];}if (numbers[mid] == numbers[right]) {--right;}else if (numbers[mid] < numbers[right]) {right = mid - 1;}else {left = mid + 1;}}return numbers[left]; }
};
二分查找
如果在数组下标[left..right]
范围内根据某种要求进行二分查找,笔者原以为while
循环控制条件left < right
和left <= right
是等价的,只要最后return left
,那结果就是正确的。通过本题可知,在旋转的有序数组上进行二分查找时循环控制语句left < right
和left <= right
是等价的。
但事实上,如果循环控制条件是left < right
,当left == right
时退出循环,而此时left
所对应的元素未必满足要求。因此正确的循环控制条件应该是left <= right
。
例如要在数组nums=[1,3,5]
中找到第一个大于等于2的元素,开始left = 0,right = 2,mid = 1
,因为nums[1]
符合要求,因此将right
置为0
,此时left == right
。如果循环控制语句是left < right
,那将返回nums[0]
,得到错误的结果;如果循环控制语句是left <= right
,那将进一步对nums[0]
进行判断,最后返回nums[1]
,结果正确。
由此可见,分别在正常的有序数组和旋转后的有序数组上使用二分查找时,两者有一些微妙的区别。
扩展
要在旋转的有序数组上进行查找操作,二分查找是首先要考虑的方法。但由于旋转数组是局部有序的,所以要区分有序段和无序段(存在断崖),还要注意出现重复元素。
leetcode 面试题10.03:搜索旋转数组是在旋转数组中搜索第一个出现的元素,除了二分查找,还用到了递归的思路。