旋转数组的最小数字 / First Bad Version

简单版本:数组中的元素互不相同

LeetCode 153. Find Minimum in Rotated Sorted Array
领扣 153. 寻找旋转排序数组中的最小值

设置两个指针left = 0right = nums.size() - 1

每次循环一开始,判断nums[left] < nums[right]是否成立,如果成立,那么说明数组nums[left..right]的旋转值为0,直接返回nums[left]为最小元素

若数组旋转值不为0,考察中间元素nums[mid]

情况1nums[left] <= nums[mid](取等号是因为mid有可能等于left),此时可以排除掉从leftmid的元素,令left = mid + 1
这里写图片描述

情况2nums[mid] < nums[right],此时可以排除掉从mid+1right的元素,mid元素本身不能排除,令right = mid
这里写图片描述
最终当left等于right时循环终止(我们希望最终剩下一个元素),指向最小的元素,故循环条件写为left < right

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;

        while( left < right )   // 我们希望最终剩下一个元素
        {
            if( nums[left] < nums[right] )
                return nums[left];

            int mid = left + ( right - left ) / 2;

            if( nums[mid] >= nums[left] )
                left = mid + 1;
            else
                right = mid;
        }

        return nums[left];
    }
};

【相似题目】
Leetcode 278. First Bad Version
领扣 278. 第一个错误的版本
在数组[good, good, ..., good, bad, bad, ..., bad]中寻找第一个bad的位置

同样设置两个指针left = 0right = nums.size() - 1,考察中间的mid元素

情况1mid元素是good,则可以排除从leftmid的元素,令left = mid + 1

情况2mid元素是bad,则可以排除从mid + 1right的元素,但mid元素自身不能排除,故令right = mid

最终可以证明leftright会相等(也是因为我们希望剩下最后一个元素),指向第一个bad,因此循环条件要写成left < right

简单的证明方法,举一个简单的例子[good, bad]
left = 1,right = 2,mid = 1
mid元素是good成立,left = mid + 1 = 2
最终left = right,返回left = 1

// Forward declaration of isBadVersion API.
bool isBadVersion(int version);

class Solution {
public:
    int firstBadVersion(int n) {
        int left = 1, right = n;

        while( left < right )   // 我们希望最终剩下一个元素
        {
            int mid = left + ( right - left ) / 2;

            if( !isBadVersion(mid) )
                left = mid + 1;
            else
                right = mid;
        }

        return left;
    }
};

困难版本:数组中的元素有重复

LeetCode 154. Find Minimum in Rotated Sorted Array II
领扣 154. 寻找旋转排序数组中的最小值 II

采用和简单版本一样的思路,只不过对于nums[left]nums[mid]nums[right]三者相等时,无法判断最小元素位于前半部还是后半部,此时将right1即可(因为我们想找到第一个最小元素,而nums[right]一定不会是我们想要的,故可以排除)

class Solution {
public:
    int findMin(vector<int>& nums) {
        int left = 0, right = nums.size() - 1;

        while( left < right )   // 我们希望最终剩下一个元素
        {
            if( nums[left] < nums[right] )
                return nums[left];

            int mid = left + ( right - left ) / 2;

            // 如果三者相等,无法排除前半或后半的元素
            if( nums[left] == nums[mid] && nums[mid] == nums[right] )
                right--;
            else if( nums[mid] >= nums[left] )
                left = mid + 1;
            else
                right = mid;
        }

        return nums[left];
    }
};

猜你喜欢

转载自blog.csdn.net/o0Helloworld0o/article/details/81253896