题目描述:
已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组。例如,原数组 nums = [0,1,2,4,5,6,7] 在变化后可能得到:
若旋转 4 次,则可以得到 [4,5,6,7,0,1,2]
若旋转 7 次,则可以得到 [0,1,2,4,5,6,7]
注意,数组 [a[0], a[1], a[2], …, a[n-1]] 旋转一次 的结果为数组 [a[n-1], a[0], a[1], a[2], …, a[n-2]] 。
给你一个元素值 互不相同 的数组 nums ,它原来是一个升序排列的数组,并按上述情形进行了多次旋转。请你找出并返回数组中的 最小元素 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。
解题思路:
思路1.暴力法(可以完成题目,但不符合题目要求):
由于题目的实质是要找出数组中的最小值,所以我们可以用最容易想到的暴力遍历法(选取数组中第一个元素为最小值,依次遍历数组中的元素并比较大小,如果比当前最小值小,则更新最小值!!!)
代码:
class Solution {
//Brute Force
//Time Complexity: O(N)
//Space Complexity: O(1)
public int findMin(int[] nums) {
int minNum = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i] < minNum) {
minNum = nums[i];
}
}
return minNum;
}
}
我们发现上面的暴力法是完全没有用到题目的一点特性!我们可以根据题目有序,无重复元素的特性将上面暴力法进行优化,具体操作:选取数组第一个元素作为最小元素,从下标为1的元素开始遍历,每次比较下标[ i ] 与 [ i - 1]的大小如果当nums[ i ] < nums[ i - 1]则说明此时nums[ i ]为最小值,若遍历完整个数组都没出现 nums[ i ] < nums[ i - 1]则说明数组是升序(没有旋转的)则直接返回最开始选定的最小值!!!
代码:
class Solution {
//Brute Force
//Tiem Complexity: O(N)
//Space Complexity: O(1)
public int findMin(int[] nums) {
int minNum = nums[0];
for (int i = 1; i < nums.length; i++) {
if (nums[i] < nums[i-1]) {
return nums[i];
}
return minNum;
}
}
思路2.二分查找
若我们使用二分查找法来解决此题目按解题逻辑我们依次应解决如下问题:
每次取数组中间位置的值,我们该如何进行比较操作使指针能进一步去按题目意义移动指针,再次使二分进行下去?
答:我们仔细观察题目可以的到一个规律:
若当前中间位置值nums[mid] < nums[high]则说明最小值在mid处或者mid左边!
若当前中间位置值nums[mid] >= nums[high]时则说明最小值在mid右边
在我们明确了中间值的比较操作分析那么我们接下来就可以进行移动指针:
//如果nums[mid] < nums[high]
//则说明最小值在mid处或mid左处
if (nums[mid] < nums[high]) {
//high直接移动到mid处
high = mid
} else {
//如果nums[mid] >= nums[high]
//则说明最小值在mid右处
low = mid + 1; //将指针移动到mid+1处,确保在mid右处
}
接下来我们再确定循环退出的条件:
1.一方面由于我们在移动指针时存在high = end我们为了在循环中不出现死循环,我们就要求退出条件为: low < high
2.另一方面此二分查找实则为根据直接右邻居索引访问元素的二分查找模板,在剩下最后一个元素时(也就是最后循环退出了时low == high)我们再判断最后剩下的元素是否满足条件!
代码:
class Solution {
//Binary Search
//Time Complexity: O(logN)
//Space Complexity: O(1)
public int findMin(int[] nums) {
int low = 0;
int high = nums.length - 1;
while (low < high) {
int min = low + (high - low) / 2;
//最小值在mid处或者mid左边
if (nums[mid] < nums[high]) {
high = mid;
} else { //最小值在mid右边
low = mid + 1;
}
}
return nums[low]; //or nums[high]
}
}