壹 ❀ 引

堕落了一天,那么接着来刷leetcode,今天做的一题不算复杂,题目来自leetcode153. 寻找旋转排序数组中的最小值,题目描述如下:

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

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

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

示例 1:

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

示例 2:

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

在之前JS leetcode 旋转数组 题解分析一文中,我们已经做过旋转数组的题目,所以这里所说的旋转其实只是将数组尾部的元素依次加入数组头部的操作。我们简单分析题目,再说怎么实现。

贰 ❀ 解题思路

贰 ❀ 暴力解题

由题目提供的信息可知,数组为升序排列数组,而且在某个未知的点进行了旋转,所以它可能没转。

不过但站在找出数组中最小元素来说,我们可以不考虑这些条件,直接祭出Math.min()大法:

/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
return Math.min(...nums); //ES5
//return Math.min.apply(null, nums);
};

由于数组原本就是排序好的,只是可能进行了旋转,我们可以可耻的将数组再排序然后取出索引0位元素:

/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function (nums) {
return nums.sort((a, b) => a - b)[0];
};

虽然能达到目的,总觉得差了点意思,提交后发现执行时间排名也很低,说明有更好的做法。

贰 ❀ 什么是二分法?

在看了官方的解题思路后,发现本题用二分法查找会更好,不过对于我来说,二分法是啥都比较疑惑,所以这里先简单说说什么是二分法。

比如,我们要在数组[1,2,3,4,5,6,7,8,9]里面找到目标9的下标,按照常规遍历,我们只有从头遍历到最后一位才能找到,时间复杂度为O(n),使用二分法就不一样,如下:

/**
* @desc 二分法查找目标元素索引
* @param {*} arr 数组
* @param {*} target 目标元素
*/
function binarySearch(arr, target) {
// 数组起始索引
var low = 0;
// 数组最后一项索引
var high = arr.length - 1;
while (low <= high) {
// 获取数组中间项索引
var mid = Math.floor((low + high) / 2);
if (target === arr[mid]) {
// 返回目标元素下标
return mid;
// 根据比较来决定下次查找的数组应该是左还是右
} else if (target > arr[mid]) {
low = mid + 1;
} else {
high = mid - 1;
};
};
// 没找到返回-1
return -1;
};
binarySearch([1, 2, 3, 4, 5, 6, 7, 8, 9], 9); //8

我们一开始就可以按照某个特定规则找到数组中的中间项元素,从而将数组一分为二,然后将中间项与目标元素比较,比如一开始我们找到了5,由于比目标元素9小,所以9肯定在5右边的数组中。

那么下次遍历就从[6,7,8,9]开始,这次中间元素找到7,仍然比9小,继续上述操作,又在[8,9]中寻找。直到最后找到目标元素9,从而获取到索引。

你看,这样对半分的查找,是不是比我们常规从头到尾的遍历要快很多,二分法的时间复杂度为O(logn),注意,二分法适合有序序列,不然我们也不知道下次应该去左边还是右边比较了。

贰 ❀ 通过二分法解决此题

前面说了,二分法适合有序数组,这样我们才好根据特定条件来决定下次应该从哪边开始,比如上文中的target > arr[mid]。而本题虽然也是有序,但很遗憾的是数组经过了旋转操作,所以一般的二分法并不适用。

比较麻烦的是,虽然题目说了做了旋转,不保证数组旋转了一整圈结果并无变化的情况。所以第一步我们可以先判断数组到底有没有被旋转,判断条件很简单

已知数组是有序数组,如果数组发生了旋转,那么数组第一位一定大于最后一位,反之如果数组未发生旋转,那么第一位一定小于最后一位,这种情况我们直接返回第一位即可。

那如果数组已发生了旋转,我们又该怎么判断呢,其实有一个这样的特点:

纵观整个数组,我们以相邻两个元素来看,假设当前中间元素为mid,如果arr[mid-1]大于arr[mid],那么mid为我们想要的元素。或者arr[mid]大于arr[mid+1],那么此时mid+1是我们想要找的元素。为啥这么说,因为数组虽然旋转了,但相邻且满足如上任意条件之一的情况只存在一次,不信大家随便写个有序数组看。

所以每次确定中间元素,我们都得走如下条件,只要满足其一即是我们想要找的元素。

if (nums[mid] > nums[mid + 1]) {
return nums[mid + 1];
}; if (nums[mid - 1] > nums[mid]) {
return nums[mid];
};

这是找到目标元素的条件,那不满足我们如何知道应该查找左边数组还是右边数组呢?其实有这样一个规律:

如上图,我们将9=>1之间称为变化点,变化点左边的元素都一定比数组第一位元素大,变化点右边的所有元素,一定比数组第一位元素小。

所以找到中间元素mid,如果mid比第一位还大,那说明我们应该去数组右边找,如果mid比第一位还小,那么应该去左边找。

你可能在想,万一我这个mid第一次就是最小元素咋办,如果运气真这么好,它早就被我们上面定的两个条件之一给返回了,能走到这一步说明这个mid一定不是我们想找的目标元素,这才要分去哪边找啊。

那么我们上代码:

/**
* @param {number[]} nums
* @return {number}
*/
var findMin = function(nums) {
// 假设数组只有一项,直接返回
if (nums.length == 1) {
return nums[0];
}; var left = 0,
right = nums.length - 1;
// 假设数组最后一项大于第一项,说明数组未旋转,直接返回
if (nums[right] > nums[0]) {
return nums[0];
}; // 既然能走到这一步,那说明数组一定旋转了,套用之前的规则,使用二分法进行查找
while (right >= left) {
// Find the mid element
var mid = Math.floor((left + right) / 2);
// 满足如下条件之一说明就是最小元素,直接返回即可
if (nums[mid] > nums[mid + 1]) {
return nums[mid + 1];
};
if (nums[mid - 1] > nums[mid]) {
return nums[mid];
};
// 比较当前中间元素与第一位
if (nums[mid] > nums[0]) {
// 如果要大,那就去右边找
left = mid + 1;
} else {
// 反之就去左边找
right = mid - 1;
};
};
return -1;
};

执行图解如下:

其实这段代码我分析了很久,对于我觉得比较难的是什么情况返回mid,我觉得mid如果是最小,它一定比mid+1小,但这样是不成立的,例如1比2小,2也比3小,这个条件没法用。

所以官方分析我觉得让我佩服的是,整个数组中,当mid是1时,mid-1一定比mid大,整个数组你找不出第二个这样的情况。同理,当mid是9时,mid一定比mid+1大,你同样找不出第二个这样的情况...

所以变化点相关的两个元素9和1才是解题关键。

那么关于本题就分析到这里了。

相关题型:

JS Leetcode 154. 寻找旋转排序数组中的最小值 II 题解分析

JS Leetcode 33. 搜索旋转排序数组题解,图解旋转数组中的二分法

JS Leetcode 81. 搜索旋转排序数组 II 题解,补救二分法的可行性

另外,二分法参考如下:

算法——二分法查找(binarySearch)

JS leetcode 寻找旋转排序数组中的最小值 题解分析,你不得不了解的二分法的更多相关文章

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

    LeetCode:寻找旋转排序数组中的最小值[153] 题目描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0 ...

  2. Leetcode之二分法专题-154. 寻找旋转排序数组中的最小值 II(Find Minimum in Rotated Sorted Array II)

    Leetcode之二分法专题-154. 寻找旋转排序数组中的最小值 II(Find Minimum in Rotated Sorted Array II) 假设按照升序排序的数组在预先未知的某个点上进 ...

  3. Leetcode之二分法专题-153. 寻找旋转排序数组中的最小值(Find Minimum in Rotated Sorted Array)

    Leetcode之二分法专题-153. 寻找旋转排序数组中的最小值(Find Minimum in Rotated Sorted Array) 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ...

  4. [LeetCode每日一题]153.寻找旋转排序数组中的最小值

    [LeetCode每日一题]153.寻找旋转排序数组中的最小值 问题 已知一个长度为 n 的数组,预先按照升序排列,经由 1 到 n 次 旋转 后,得到输入数组.例如,原数组 nums = [0,1, ...

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

    题目链接:传送门 题目描述 现有一个有序数组,假设从某个数开始将它后面的数按顺序放到了数组前面.(即 [0,1,2,4,5,6,7] 可能变成 [4,5,6,7,0,1,2]). 请找出数组中的最小元 ...

  6. [LeetCode] 154. 寻找旋转排序数组中的最小值 II

    题目链接 : https://leetcode-cn.com/problems/find-minimum-in-rotated-sorted-array-ii/ 题目描述: 假设按照升序排序的数组在预 ...

  7. Java实现 LeetCode 154 寻找旋转排序数组中的最小值 II(二)

    154. 寻找旋转排序数组中的最小值 II 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 请找 ...

  8. Java实现 LeetCode 153 寻找旋转排序数组中的最小值

    153. 寻找旋转排序数组中的最小值 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 请找出其中 ...

  9. lintcode: 寻找旋转排序数组中的最小值

    寻找旋转排序数组中的最小值 假设一个旋转排序的数组其起始位置是未知的(比如0 1 2 4 5 6 7 可能变成是4 5 6 7 0 1 2). 你需要找到其中最小的元素. 你可以假设数组中不存在重复的 ...

  10. [Swift]LeetCode154. 寻找旋转排序数组中的最小值 II | Find Minimum in Rotated Sorted Array II

    Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. (i.e. ...

随机推荐

  1. ASIC 功能验证VTB

    目标 设计流程 验证设计文档和RTL code之间的关系 RTL code(DUT) - 可以当作是一个黑盒,DUT内部是完全不可见的 白盒验证 - DUT内部RTL完全可见 灰盒验证 - DUT内部 ...

  2. AHB-SRAMC Design-03

    SRAMC SRAM CORE 8块memory进行广播信号,例化8片memory

  3. java - 字符串转数字

    Integer.valueOf("str").intValue(): Integer.valueOf("123").intValue():

  4. 《OnJava》——11内部类

    内部类 利用内部类,可以将逻辑上存在关联的类组织在一起,而且可以控制一个类在另一个类中的可见性. 内部类和组合不同,内部类是一种代码隐藏机制:将代码放在其他类的内部. 11.1 创建内部类 创建内部类 ...

  5. 如何看待《李跳跳》APP因被腾讯公司发律师函称“不正当竞争”而无限期停止更新?

    一波未平一波又起,继李跳跳无限期停更后,又一安卓神奇工具被下发律师函!近期各路安卓工具APP,被某讯大厂可谓是尽数剿灭~ 不难看出此次行动是"蓄谋已久"了.与李跳跳.大圣净化类似的 ...

  6. 【面试题精讲】为什么G1收集器不需要调优性能也很优秀

    G1(Garbage-First)收集器是一种面向服务器端应用的垃圾回收器,它在JDK 7u4版本中首次引入,主要用于替代CMS(Concurrent Mark Sweep)收集器.相比于其他垃圾回收 ...

  7. [转帖]Centos使用chrony做时间同步

    https://www.cnblogs.com/lizhaoxian/p/11260041.html Chrony是一个开源的自由软件,在RHEL 7操作系统,已经是默认服务,默认配置文件在 /etc ...

  8. [转帖]【基础】HTTP、TCP/IP 协议的原理及应用

    https://juejin.cn/post/6844903938232156167 前言 本文将持续记录笔者在学习过程中掌握的一些 HTTP .TCP/IP 的原理,以及这些网络通信技术的一些应用场 ...

  9. [转帖]docker使用阿里镜像源

    ps:docker使用阿里镜像源特别快 首先安装docker:参考https://www.jianshu.com/p/2dae7b13ce2f 一.使用阿里镜像地址: dockerd --regist ...

  10. Linux与Windows系统字符集的简要学习

    背景 最近同事反馈公司的产品再更新了mysql-8.0.31的驱动jar包后部分功能报错. 问题核心原因 研发这边石磊老师已经找到了. 结论是Mysql8.0.26之后的数据库驱动好像会识别操作系统的 ...