壹 ❀ 引

堕落了一天,那么接着来刷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. [转帖]SCSI、ISCSI、iSER、NVMe、NVMe-oF、NVMe-oF over RDMA

    在存储系统中,上层协议可以泛指"指令",也就是比如"读出从某某开始的多少长度的扇区",指令包含三大关键信息: (1)操作码:Opreation Code,或称为 ...

  2. [转帖]Google SRE 薪水,看看同样作为 SRE 的你相差多少

    https://zhuanlan.zhihu.com/p/566098252 SRE 是确保所有生产环境(Infra/Server/DBS 等)一直正常运行的人.每个网络科技公司基本都有这个部门.但是 ...

  3. [转帖]Jmeter常用配置元件(二):“HTTP Cookie管理器”登录状态保持

    在API接口测试过程中,我们需要传递cookie值作为连接状态的保持,例如登录后状态信息.通过使用Jmeter提供的"HTTP Cookie管理器"来实现. 一般情况下不需要输入什 ...

  4. MySQL in Windows安装以及异名恢复的简单过程

    下载相关 建议获取最新版本的Mysql数据库 可以获取 zip 格式的安装文件 https://dev.mysql.com/downloads/mysql/ 或者获取 msi 格式的安装文件 http ...

  5. 小Min_25筛小记🐤

    这里的小Min_25筛,可以筛出 $10^11$ 以内所有质数的完全积性函数之和 注意事项: 1. cmd 的题解里面下标写得不清楚,应该是 $S'(p_k-1,k-1)$ 而不是 $S'(p_{k- ...

  6. 【JS 逆向百例】W店UA,OB反混淆,抓包替换CORS跨域错误分析

    关注微信公众号:K哥爬虫,持续分享爬虫进阶.JS/安卓逆向等技术干货! 声明 本文章中所有内容仅供学习交流,抓包内容.敏感网址.数据接口均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后 ...

  7. 每日一库:Memcache

    Memcache 是一个高性能.分布式的内存缓存系统,常用于缓存数据库查询结果.API调用结果.页面内容等,以提升应用程序的性能和响应速度.下面详细介绍一些 Memcache 的特点和使用方式: 内存 ...

  8. Fabric配置块结构解析

    本文是区块链浏览器系列的第二篇. 上一篇介绍了交易块中的数据结构,这一篇介绍区块链网络中的配置块数据结构. 这两种区块中数据结构内容的区别主要Payload结构体中的Data域中的内容,接下来将以类图 ...

  9. 快捷方式 ABP——切换MySQL数据库

    当基于ABP开发一段时间后,切换数据库可以使用快捷方式 方法如下: 第一步: 前提是要知道所使用的数据库的版本 1. vs 2017需要点击XXX.XXX.EntityFrameworkCore,右键 ...

  10. AsNoTracking()非跟踪数据 查询

    刚开始学习使用EF ,做项目时需要查询数据将数据显示在datagrid中,使用如下方法: query是IQueryable的 在一次看别人写的代码的时候,发现了AsNoTracking()这个方法,并 ...