【LeetCode-查找】寻找旋转排序数组中的最小值

题目描述

假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
请找出其中最小的元素。
你可以假设数组中不存在重复元素。
示例:

输入: [3,4,5,1,2]
输出: 1

输入: [4,5,6,7,0,1,2]
输出: 0

题目链接: https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array/
做这题之前要先把二分查找做了,并理解二分查找两种写法的不同的原因。

思路

旋转数组可以看做是两个升序数组的拼接,而且左边的数组元素均大于等于右边的数组元素,右边数组的第一个元素就是最小值。例如,旋转数组[3,4,5,1,2]可以分为两部分[3,4,5]和[1,2],左边数组[3,4,5]中的元素均大于右边数组的元素[1,2]。使用二分查找的思想来做,令left=0, right=nums.size()-1,mid=left+(right-left)/2,比较nums[left]、nums[right]和nums[mid]之间的大小:

  • 如果nums[mid]>nums[left],说明nums[mid]在左边的数组当中,最小值在mid右边,所以调整搜索范围为[mid+1, right];
  • 如果nums[mid]<nums[right],说明nums[mid]在右边的数组当中,最小值在mid左边,所以调整搜索范围为[left, mid-1];

还有一个考虑的点是终止条件,因为两个数组是递增的,也就是在两个数组当中存在nums[i]<nums[i+1],在两个数组连接的地方有nums[i]>nums[i+1],所以在循环的过程中,如果nums[mid]>nums[mid+1]或者nums[mid]<nums[mid-1],我们返回两个数字之间右边的那个较小值。
代码如下:

class Solution {
public:
    int findMin(vector<int>& nums) {
        if(nums.size()==1) return nums[0];  // 注意数组长度为1的情况

        int left = 0, right = nums.size()-1;
        if(nums[right] > nums[left]) return nums[left]; // 数组没有旋转,直接返回最小值nums[left]

        int mid = 0;
        while(left<=right){
            mid = left+(right-left)/2;
            if(nums[mid] > nums[mid+1]) return nums[mid+1];
            if(nums[mid-1] > nums[mid]) return nums[mid];

            if(nums[left]<nums[mid]) left = mid+1;     // 这里也可以写成nums[left]<=nums[mid]
            else if(nums[mid]<nums[right]) right = mid-1; // 但由于数组中没有重复元素,所以不可能等于,写<就行  
        }
        return nums[mid];
    }
};
  • 时间复杂度:O(logn)
  • 空间复杂度:O(1)
posted @ 2020-04-29 21:30  Flix  阅读(391)  评论(0编辑  收藏  举报