LeetCode - 153. Find Minimum in Rotated Sorted Array (旋转数组中的最小值)

LeetCode - 153. Find Minimum in Rotated Sorted Array (旋转数组中的最小值)


LeetCode - 153. Find Minimum in Rotated Sorted Array

题目链接
题目

在这里插入图片描述

解析

这个题目在剑指Offer中也出现过,可以分为递归和非递归的解法。

递归
  • 递归解法中,递归函数先取两边区间的中点mid = (L + R)/2,然后开始递归到左右子区间求解最小值;
  • 递归终止条件就是当区间范围只有1个或2个数,就返回最小值即可;
  • 如果只是上面的程序,就没有优化;
  • 优化就在当区间划分的时候,如果某个区间nums[R] > nums[L]就直接返回nums[L]了;
  • 看下表的绿色部分,就可以知道,这样的优化是有意义的,可以降低复杂度;

在这里插入图片描述

样例递归过程:
在这里插入图片描述

class Solution {
    // no duplicate elements
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        if(nums.length == 1 || nums[nums.length - 1] > nums[0])
            return nums[0];
        return helper(nums, 0, nums.length - 1);
    }
    
    private int helper(int[] nums, int L, int R){
        if(L + 1 >= R)      // must  >= , not == 
            // return nums[R]; // wrong answer
            return Math.min(nums[L], nums[R]);
        if(nums[R] > nums[L])  // judge the sequence is sorted
            return nums[L];
        int mid = L + (R - L)/2;       
        // return Math.min(helper(nums, L, mid-1), helper(nums, mid, R)); //the same result as below
        return Math.min(helper(nums, L, mid), helper(nums, mid+1, R));
    }
}

也可以改进,先判断一下是否是递增序列,然后再看是否只有一个或者两个元素了。这样的话,就不需要返回Math.min(nums[L],nums[R])了,因为既然不是递增的序列,那一定是第二个(右边的部分)更小了。

class Solution {
    // no duplicate elements
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        if(nums.length == 1 || nums[nums.length - 1] > nums[0])
            return nums[0];
        return helper(nums, 0, nums.length - 1);
    }
    
    private int helper(int[] nums, int L, int R){
        if(nums[R] > nums[L])  // judge the sequence is sorted
            return nums[L];
        if(L + 1 >= R)  //  must >= , not == 
            return nums[R];
        int mid = L + (R - L)/2;       
        return Math.min(helper(nums, L, mid-1), helper(nums, mid, R));
        // return Math.min(helper(nums, L, mid), helper(nums, mid+1, R)); //the same result as above
    }
}

如果将递归的边界改成只有一个元素的时候,递归的就只能两边都写成mid了, 因为最后的判断条件是L+1 == R,此时区间一定需要两个元素,所以划分的时候要给至少两个元素。

class Solution {
    // no duplicate elements
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        if(nums.length == 1 || nums[nums.length - 1] > nums[0])
            return nums[0];
        return helper(nums, 0, nums.length - 1);
    }
    
    private int helper(int[] nums, int L, int R){
        if(L + 1 == R)    // not L+1 >= R
            return nums[R];
        if(nums[R] > nums[L]) 
            return nums[L];
        int mid = L + (R - L)/2;       
        // return Math.min(helper(nums, L, mid-1), helper(nums, mid, R));  // err
        // return Math.min(helper(nums, L, mid), helper(nums, mid+1, R));  // err
        return Math.min(helper(nums, L, mid), helper(nums, mid, R)); // both mid 
    }
}
非递归
  • 非递归的解法就是利用二分查找的思想,不断的缩小L、R指向的前面的递增区间和后面的递增区间,最后L、R分别指向第一个区间的最后一个数、第二个区间的第一个数,于是我们返回的是R
  • 具体的过程可以看剑指Offer中前面那部分没有重复元素的分析
class Solution {
    // no duplicate elements
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        if(nums.length == 1 || nums[nums.length - 1] > nums[0])
            return nums[0];
    
        int L = 0, R = nums.length - 1;
        
        while(nums[L] > nums[R]){   // ensure it is rotated
            // if(L + 1 >= R)
            if(L + 1 == R)   // notice this is ok
                return nums[R];
            int mid = L + (R - L)/2;
            if(nums[mid] > nums[L])  // the min value is in the  second half of the array
                L = mid+1;
            else 
                R = mid;
            
            // below is ok, the same result as above
            // if(nums[mid] > nums[L])  // the min value is in the  second half of the array
            //     L = mid;
            // else 
            //     R = mid;
            
            // but below is error, the boundary we should notice
            // if(nums[mid] > nums[L]) 
            //     L = mid;
            // else 
            //     R = mid-1;
        }
        return nums[L];  // nums[L] < nums[R], directly return the nums[L]
    }
}

LeetCode - 154. Find Minimum in Rotated Sorted Array II

题目链接
题目

在这里插入图片描述

解析
  • 递归解法和LeetCode153一样,只是要注意的是,如果nums[R] == nums[L]的时候,就不能判断是Rotated Array ,而上一题也没有判断,所以是可以的;
  • 只是非递归解法的时间复杂度和那个不同了,因为当某个区间L、R中出现nums[L] == nums[L]的时候就不能直接返回nums[L],而需要继续递归,所以上面那个表格中的绿色部分都会消失,所以时间复杂度是O(n) = 2 * T(n/2) = O(n)
  • 非递归解法和上面那个有点不同,也就是在区间端点相同以及nums[mid]相同的情况下,做不同的处理,具体可看剑指Offer中的题解。
class Solution {
    // have duplicate elements
    public int findMin(int[] nums) {
        if(nums == null || nums.length == 0)
            return -1;
        if(nums.length == 1 || nums[nums.length - 1] > nums[0])
            return nums[0];
    
        int L = 0, R = nums.length - 1;
        
        while(nums[L] >= nums[R]){   // ensure it is rotated, notice >= instead >, have duplicate elements
            if(L + 1 >= R)
            // if(L + 1 == R)   //is ok
                return nums[R];
            int mid = L + (R - L)/2;
            
            // this is the different with the The previous question
            if(nums[L] == nums[mid] && nums[mid] == nums[R]){ // can't judge
                for(int i = L+1; i <= R; i++){
                    if(nums[i] < nums[i-1])
                        return nums[i];
                }
            }
            // ok 
            // if(nums[mid] >= nums[L])  // notice >=
            //     L = mid;
            // else 
            //     R = mid;
            
            // below is ok
            if(nums[mid] >= nums[L])  // the min value is in the second half of the array
                L = mid+1;
            else 
                R = mid;
            
            //but below is error
            // if(nums[mid] >= nums[L]) 
            //     L = mid;
            // else 
            //     R = mid-1;
        }
        return nums[L];  // nums[L] < nums[R], directly return the nums[L]
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值