当前位置: 首页 > news >正文

leetcode153 寻找旋转排序数组中的最小值 思考过程

本篇文章主要想展示自己在做这道题的思路过程,并不想
往常使用二分法来寻找数组中的特定值,但这个题目有所不同,是寻找部分有序数组的最小值,难点在于如何确认最小值处于左右哪一个区间。

思考

普通二分法的思路

在普通的二分法中,当前位置是不是目标值很好确定,当前值与目标值判等即可;目标值位于当前位置的左方还是右方也很好确定,将当前值与目标值比较即可;我们要找的位置具有显著的两点特征:

1)该位置的值 = 目标值
2)前一个位置的值 <= 该位置的值 <= 后一个位置的值

根据这两个特征就很容易确定目标值所在区间。

当前题目的思路

在当前题目中,最小值所在位置同样有特征:

1)前一个位置的值 > 该位置的值
2)该位置的值 < 后一个位置的值

整个数组除了最大值和最后一个元素都满足第2个特征,没有什么特别大的区分度,不好划分区间。

再看看第1个特征,仔细思考一下,满足这一特征的位置一定就是最小值所在位置。那么在二分中,如果不满足该特征,该如何确定最小值所在区间呢?答案是没法确定,因为除了最小值所在位置,其他位置都不满足该特征。这样看来,两个特征都不能解决我们的问题,需要寻找其他特征了。

其实可以把眼光放宽阔一些,不仅仅是最小值周围的值大于它,数组中的所有元素都大于它,挑选两个最特殊位置作为最小值所在位置的特征:

1)数组第一个元素的值 >= 该位置的值
2)该位置的值 <= 数组最后一个元素的值

思路1

假如以特征1作为划分区间的依据,则分为三种情况:
1)当前位置值 > nums[0];
2)当前位置值 < nums[0];
3)当前位置值 = nums[0]。

情况1(当前位置值 > nums[0]):

最小值此时可能在右:

[4,5,6,7,0,1,2]
       ^

也可能在左(此时最小值一定在数组第一个位置):

[0,1,2,4,5,6,7]
       ^

这两种子情况可以通过当前值与nums[n-1]比较来划分,总结一下就是

if 当前值 > nums[0] && 当前值 > nums[n-1]:
	l = mid + 1      // 从右区间找
if 当前值 > nums[0] && 当前值 < nums[n-1]:
	return nums[0]

情况2(当前位置值 < nums[0]):

此时最小值一定在左区间或者当前位置

[6,7,0,1,2,4,5]
       ^

情况3(当前位置值 = nums[0]):

说明此时只剩下两个元素,返回两者中的较小值就可以

[1,0]
 ^

代码实现

class Solution {
    public int findMin(int[] nums) {
        /**
        最小元素应该满足的性质:
        1)小于它左边的元素(假如左边有元素)
        2)小于它右边的元素(假如右边有元素)
        假如二分法找到了中间元素,可能有几种情况需要讨论
        1)大于nums[0]
            1. 且大于nums[n-1] -> l = mid + 1
            2. 且小于nums[n-1] -> ans = nums[0]
        2)小于nums[0] -> r = mid
        3)等于nums[0] -> ans = min(nums[0], nums[1])
         */   
        int n = nums.length;
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = (l + r) / 2;
            if (nums[mid] > nums[0]) {
                if (nums[mid] > nums[n-1]) {
                    l = mid + 1;
                } else {
                    return nums[0];
                }
            } else if (nums[mid] < nums[0]) {
                r = mid;
            } else {
                return Math.min(nums[0], nums[1]);
            }
        } 
        return nums[l];
    }
}

思路2

思路1的代码分支判断有些复杂,容易写错,假如使用特征2作为划分依据,代码就可以简洁多了

情况1:当前位置值 > nums[n-1]

这个时候最小值一定在右区间

[4,5,6,7,0,1,2]
       ^

情况2:当前位置值 < nums[n-1]

这个时候最小值一定在左区间,包括当前位置

[6,7,0,1,2,4,5]
       ^
[0,1,2,4,5,6,7]
       ^

情况3:当前位置值 = nums[n-1]

不存在这种情况,因为这种情况只可能发生在区间内只剩下nums[n-1]这一个元素的时候,此时已经找到了最小值的位置

代码实现

class Solution {
    public int findMin(int[] nums) {
        /**
        最小元素应该满足的性质:
        1)小于它左边的元素(假如左边有元素)
        2)小于它右边的元素(假如右边有元素)
        假如二分法找到了中间元素,可能有几种情况需要讨论
        1) 大于nums[n-1] -> l = mid + 1
        2) 小于nums[n-1] -> r = mid
         */   
        int n = nums.length;
        int l = 0, r = n - 1;
        while (l < r) {
            int mid = (l + r) / 2;
            if (nums[mid] > nums[n-1]) {
                l = mid + 1;
            } else {
                r = mid;
            }
        } 
        return nums[l];
    }
}

是不是看起来比思路1的代码精简了很多,可见同一个问题采用不同思路实现的复杂度差别是很大的,如果能再一开始分析比较选择最合适的思路,就能够为之后的实现和维护提供极大的方便。

相关文章:

  • BambuStudio学习笔记:MultiMaterialSegmentation
  • Docker 入门与实战指南
  • 视频推拉流:EasyDSS平台直播通道重连转推失败原因排查与解决
  • Python 逆向工程:2025 年能破解什么?
  • 云上特权凭证攻防启示录:从根账号AK泄露到安全体系升级的深度实践
  • 扫雷小游戏
  • 汇川EASY系列之以太网通讯(套接字socket做主站)
  • 蓝桥杯javaB组备战第二天 题目 区间次方和 编号3382
  • Linux中grep指令
  • yum修改阿里云
  • 致远互联FE协作办公平台 存在SQL注入漏洞(DVB-2025-8942)
  • WHAT - 前端性能监控和错误追踪(Sentry 篇)
  • 爬取动态数据,爬取持久化数据
  • docker-compose Install m3e(fastgpt扩展) GPU模式
  • 官宣 | Fluss 0.6 发布公告
  • Vue 实现AI对话和AI绘图(AIGC)人工智能
  • redux_旧版本
  • Matlab 多项式拟合点法线(二维)
  • 【每日学点HarmonyOS Next知识】防止重复点击、对话框收拾拦截、自定义键盘焦点、页面层级、自定义对话框创建
  • mysql-8.0.41-winx64 手动安装详细教程(2025版)
  • 如何快速做单页面网站/可以直接打开网站的网页
  • 学而思的网站哪里做的/品牌运营
  • 网站怎么做域名/重大军事新闻最新消息
  • 企业网站管理系统cms源码下载/网络软文营销
  • wordpress 导入文章/如何做优化排名
  • 网站语言切换前端可以做么/推广网上国网