【LeetCode】154. 寻找旋转排序数组中的最小值 II(JAVA)

原题地址:https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/

题目描述:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

注意数组中可能存在重复的元素。

示例 1:

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

示例 2:

输入: [2,2,2,0,1]
输出: 0

解题方案:

(前面都是废话,可以直接拉到底看最后一个代码ioi)

这道题刚开始写的时候,很多问题没有考虑到,导致后面越写越乱,写到中间甚至连二分查找都蒙了,主要没考虑到的问题有

1、没有考虑到前后两个子数组有相同值存在的情况

2、对于二分查找中mid与它前后值有没有超数组范围,以及right到底能不能小于left 一直没有想得太清楚,

首先若left<right,则mid最大也只能与left相等,所以mid+1不会超

其次,mid-1是可能超出的,因为mid可能为0,这种情况应该为left=0且right=1

在二分查找中right是可能小于left的,如果left与right相邻,则mid=left,若此时对应的数大于查找值,那么下个循环的right就小于left,而且在跳出循环后指向的值为left。

 

所以这道题虽然想到了用二分查找,但是如何应用在一开始并没有想明白。刚开始一直考虑的是nums[0]和nums[length-1]。把这两个数当成了前半数组的最小值和后半数组的最大值,一直用mid和这两个数作比较。第一次写出来的是只排除了两边数组首位的重复值,然后用中间值去作比较。

class Solution {
    public int findMin(int[] nums) {
        int l = nums.length;
        if(l == 0)
        {
            return 0;
        }
        if(l == 1)
        {
            return nums[0];
        }
        if(nums[0]  < nums[l - 1]) //没有旋转
        {
            return nums[0];
        }
        int left = 0, right = l - 1;

//去除首尾重复值
        while(left + 1 <= l - 1 && nums[left] == nums[left + 1])
            left ++;
        while(right - 1 >= 0 && nums[right] == nums[right - 1])
            right --;
//设置左右两数组的最小和最大值
        int l_min = nums[left], r_max = nums[right];
        int mid = (left + right) / 2;
        while(left < right)
        {
            if(mid == 0)
            {
//left为0,right为1的情况,此时分界点只能为1,不可能为0
                return nums[1];
            }
            else //看分界点是在mid的前还是后
            {
                if(nums[mid - 1] > nums[mid])
                    return nums[mid];
                if(nums[mid] > nums[mid + 1])
                    return nums[mid + 1];
                //分界点在mid后
                if(nums[mid] > l_min && nums[mid] > r_max)
                {
                    left = mid + 1;
                    mid = (left + right) / 2;
                }
                //分界点在mid前
                else if(nums[mid] < l_min && nums[mid] < r_max)
                {
                    right = mid - 1;
                    mid = (left + right) / 2;
                }

            }
        }
        return nums[left];
    }
}

但是这样做出来的复杂度很高,所以参考了一下别人的做法,发现不需要和最值作比较,只需要和left和right比较就可以了,因为分界点必定是在left和right之间的。

class Solution {
    public int findMin(int[] nums) {
        if(nums.length == 0)
        {
            return 0;
        }
        if(nums.length == 1 || nums[0]  < nums[nums.length - 1])
        {
            return nums[0];
        }
        int left = 0, right = nums.length - 1;
        int mid;
        while(left < right)
        {
            while(left + 1 <= nums.length - 1 && nums[left] == nums[left + 1])
                left ++;
            while(right - 1 >= 0 && nums[right] == nums[right - 1])
                right --;
            mid = (left + right) / 2;

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

            }
        }
        return nums[left];
    }
}

这样做出来运行时间还不是最好的,然而排第一的方法用了遍历。。不过我跑了一次,时间和我的一样。

https://blog.csdn.net/qq_17550379/article/details/83689829

这是在网上找的答案,思路和方法都比我清晰多了,还是需要多加学习啊。。

又仔细地做了相关的题:

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

【LeetCode】81. 搜索旋转排序数组 II

发现代码和153题完全相同,只不过当mid = high时可以理解为删除该重复值。

class Solution {
    public int findMin(int[] nums) {
        int low = 0, high = nums.length - 1, mid = 0;
        while(low <= high)
        {
            mid = (low + high) / 2;
            if(nums[mid] > nums[high])
                low = mid + 1;
            else if(nums[mid] < nums[high])
                high = mid;
            else
                high --;
        }
        return nums[low];
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值