题目描述
把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。
输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。
例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。
NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。
题目来源: 牛客网
思路:
题目告诉给定的是一个非递减排序的数组的一个旋转, 例如数组{3,4,5,1,2}为{1,2,3,4,5}, 这也就是说, 最为简单直接的就是暴搜, 遍历数组, 当后一个值小于前一个值时, 后一个值就是最小值, 时间复杂度为O(N)
由于数组可以看作是两段有序的序列, 因此可以采用二分, 复杂度为O(logN)
举例来看二分的具体操作
一个别旋转过的数组可以看做两段有序的序列
分为
3, 4, 5 和 1, 2
此时我们需要寻找的是数组中的最小值, 也就是右半序列的第一个值
首先将a[left]与a[mid]比较, 如果 a[left] < a[mid], 这说明mid仍然处于左半部分递增序列当中, 也就是说, 数组最小值处于区间 [mid + 1, right]之间, 因此 此时修改 left = mid + 1;
同理, 接下来比较 a[right] 与 a[mid], 如果 a[mid] < a[right], 这说明数组最小值处于区间[left, mid], 也就是更新 right = mid;
注意 mid 不需要减 1, 可以想一下, 假如 mid 正好处于数组最小值的位置, 这时满足 a[mid] < a[right], 接下来更新right的值为 mid - 1, 这样接下来的搜索范围区间就变成了[left, mid - 1], 就错过了最小值.
上面所举例子, 更新 left 的值之后, a[mid] > a[right], 因此不需要更新 right的值
更新mid的值, mid = (left + right) / 2;
此时, 不满足 a[mid] > a[left], 不更新 left 的值
满足, a[mid] < a[right], 更新 right = mid;
此时 left = right, 跳出循环, 返回 a[left];
当然此时还并不完整, 因为题目给出的是 非递减序列, 比如这样一种特殊情况
1, 0, 1, 1, 1, 1
此时就会发现, a[mid] > a[left] 不成立, 不需要更新left
a[mid] < a[right] 也不成立, 也不需要更新right, 这样程序陷入死循环, 因此, 在上面条件判断的基础上, 再加一个条件
当上面两种情况都不满足是, left++;
与此同时, 在更新 mid 之前, 我们判断 a[left] < a[right]是否成立, 成立则直接返回a[left];
代码示例:
class Solution {
public:
int minNumberInRotateArray(vector<int> rotateArray) {
int n = rotateArray.size();
if (n == 0)
return 0;
int left = 0, right = n - 1;
while (left < right)
{
if (rotateArray[left] < rotateArray[right])
return rotateArray[left];
int mid = (left + right) >> 2;
if (rotateArray[left] < rotateArray[mid])
left = mid + 1;
else if (rotateArray[right] > rotateArray[mid])
right = mid;//防止mid处于递增序列部分的第一个值
else
left++;
}
return rotateArray[left];
}
};