壹 ❀ 引

本来今天(2021.4.7)的每日一题是81. 搜索旋转排序数组 II,但今天工作很忙,下班人基本累个半死,题目别说按照二分法的思路做不出来,连题解看了会都没法沉下心去看,不过得到的信息是,本题属于另一道的变体,而且若先了解另一题,对于本题会有较大的帮助,想了想就还是先记录之前的题,题目来自LeetCode33. 搜索旋转排序数组,题目难度同样是中等,题目描述如下:

整数数组 nums 按升序排列,数组中的值 互不相同 。

在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。

给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。

示例 1:

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

示例 2:

输入:nums = [4,5,6,7,0,1,2], target = 3
输出:-1

示例 3:

输入:nums = [1], target = 0
输出:-1

提示:

1 <= nums.length <= 5000

-10^4 <= nums[i] <= 10^4

nums 中的每个值都 独一无二

题目数据保证 nums 在预先未知的某个下标上进行了旋转

-10^4 <= target <= 10^4

今天就熬个夜先记录和理解本题吧,明天再把升级版的题目再补回来。

贰 ❀ 简单分析与二分法

JS的同学可能看到此题,本能想到的就是findIndex了,不过要是用这种来解题,本身就没什么意义了,所以这种投机取巧的做法就不说了,对于算法也没太大提升,我们直接介绍二分法做法。

我在JS leetcode 寻找旋转排序数组中的最小值 题解分析,你不得不了解的二分法一文中,有简单提及二分法,而且比较巧的是,这道题也是旋转数组。关于二分法查找的优点是,每次条件判断后总能舍弃掉一半的元素,从而大大加快查找的效率,二分法的时间复杂度是O(logn),我们先上一个查找目标元素的二分法模板:

// 查找目标值二分法模板
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;
};
};
return -1;
};

而对于本题来说,较为难受的是数组虽然是有序数组,但是一个经过旋转的有序数组,我们不知道在哪个点进行了旋转,所以一般的二分法在这行不通。

对于常规二分法,我们是根据目标值与mid对比,从而确定目标值在mid的左侧或者右侧(或者运气好直接相等找到了),从而不断缩小范围。但事实上,对于旋转的有序数组依旧有规律可循。

我们以数组[1,2,3,4,5]为例,我们要找到3,它可能存在旋转情况如下:

如上图,第一行为为旋转,下面四行为此数组可能旋转的所有情况,我们找出mid,根据与nums[0]的大小对比,可以得知:

  1. 若mid<nums[0],那么mid在右侧有序序列,比如[2,3,4]
  2. 若mid>=nums[0],那么mid在左侧有序序列,比如[3,4,5]注意,为什么是>=后面会解释

这样我们就已经对于区域做了一次划分,但既然是二分法,自然得舍弃掉一半的元素,此时就得依赖target了,判断依据其实很简单。

假设我们的数组是[5,1,2,3,4]找3,我们先得知有序序列在右侧,也就是[2,3,4],我们将这个范围理解为[mid,end],那么只要target>mid&&target<=end,那就说明3一定在右侧有序序列中,左边的[5,1]可以直接舍弃。

接下来我们可以调整左侧边界为mid+1继续搜索,为什么加1呢?因为如果mid===target已经返回了,能走到这一步自然mid不会相等,下次调整边界自然可以舍弃掉,用图表示这个过程如下:

有同学可能就想到,你这样解释太过于理想了,如果target在无序那边呢?其实也不冲突,我们还是假设[5,1,2,3,4]中找1。

很明显由于mid<5,所以有序部分在右侧,但因为target并不满足target>mid&&target<=end,因此右边界调整为mid-1,于是我们舍弃了右边部分,得到了[5,1]

接下来怎么办?当然还是重复判断哪边为有序部分,哪边不是,我们同样还是找到mid,也就是5,由于此时mid>=nums[0],也就是5>=5,因此mid在左侧有序部分,所以得到了有序部分[5]以及无序部分[1],哎,到这里你是不是知道了为什么是>=了?找出有序部分的目的,其实就是为了方便我们利用target>mid && target<=end(假设有序有右侧)来决定放弃哪一部分,如果你的数组不是有序的,target>mid&&target<=end这个公式你根本没法满足。

那么上面这个过程用图就是下面这样:

解释的够清楚了,直接上代码:

/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var search = function (nums, target) {
let l = 0;
let r = nums.length - 1; while (l <= r) {
const mid = Math.floor((l + r) / 2);
if (nums[mid] === target) {
return mid;
};
if (nums[mid] >= nums[l]) {
//target 在 [l, mid] 之间
if (target >= nums[l] && target < nums[mid]) {
r = mid - 1;
} else {
//target 不在 [l, mid] 之间
l = mid + 1;
};
} else {
// [mid, r]有序
// target 在 [mid, r] 之间
if (target > nums[mid] && target <= nums[r]) {
l = mid + 1;
} else {
// target 不在 [mid, r] 之间
r = mid - 1;
}
}
}
return -1;
};

总结下解题的核心,第一点,根据mid与nums[0](第一位是可变的,所以其实是nums[左边界])决定哪一边是有序序列,对有序序列套用target>mid && target<=r从而得知target在不在有序序列这一边,不在自然在无序那一边,继续循环上述步骤,找到最终答案。

真的累了,2点了....睡觉。这题就说到这里了。

嗯....图片貌似有点大,确实困了...就这样吧。

JS Leetcode 33. 搜索旋转排序数组题解,图解旋转数组中的二分法的更多相关文章

  1. Java实现 LeetCode 33 搜索旋转排序数组

    33. 搜索旋转排序数组 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值, ...

  2. 力扣Leetcode 33. 搜索旋转排序数组

    33. 搜索旋转排序数组 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值, ...

  3. [LeetCode]33. 搜索旋转排序数组(二分)

    题目 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值,如果数组中存在这个目 ...

  4. [leetcode] 33. 搜索旋转排序数组(Java)

    33. 搜索旋转排序数组 说实话这题我连题都没有看懂....真是醉了 二分,没意思,直接交了- - https://www.jiuzhang.com/solutions/search-in-rotat ...

  5. leetcode 33. 搜索旋转排序数组 及 81. 搜索旋转排序数组 II

    33. 搜索旋转排序数组 问题描述 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定 ...

  6. LeetCode 33 - 搜索旋转排序数组 - [二分]

    假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值,如果数组中存在这个目标值, ...

  7. LeetCode 33 搜索旋转排序数组

    题目: 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] ). 搜索一个给定的目标值,如果数组中存在这个 ...

  8. [LeetCode] 33. Search in Rotated Sorted Array 在旋转有序数组中搜索

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

  9. LeetCode 33——搜索旋转排序数组

    1. 题目 2. 解答 2.1. 方法一 直接进行二分查找,在判断查找方向的时候详细分类. 当 nums[mid] < target 时, 若 nums[left] <= nums[mid ...

  10. leetcode 33搜索旋转排序数组

    暴力解法:O(n) 想办法用二分查找Ologn

随机推荐

  1. maven 工程pom依赖优化及常用命令

    本文为博主原创,转载请注明出处: 1. mvn dependency:list ---- 列出项目的所有jar包 mvn dependency:list -Dverbose 该命令可以列出项目依赖的所 ...

  2. Ubuntu解决Github无法访问的问题

    技术背景 由于IP设置的问题,有时候会出现Github无法访问的问题,经过一番的资料检索之后,发现如下的方案可以成功解决在Ubuntu下无法正常访问Github的问题(有时候可以打开,有时候又不行). ...

  3. Manjaro Linux永久修改主机名

    技术背景 Linux系统中默认的主机名可以通过hostname指令进行查询,一般默认的是一串的随机字符串: [dechin@dechin-20n2s01200 numba]$ hostname dec ...

  4. Go-值传递&引用传递

    值类型和引用类型 值类型关注其值 引用类型关注其指针 值类型和引用类型区别在于传递值的时候拷贝的对象不同,值传递拷贝是变量的值,引用传递拷贝的是变量的指针 拷贝 -- 传递值 赋值 函数调用 初始化 ...

  5. 百度网盘(百度云)SVIP超级会员共享账号每日更新(2023.11.20)

    百度网盘会员账号共享(11.20更新) 账号:zqv29268 密码:7003wayb 账号:rpz75364 密码:riyk1648 账号:0580quqn 密码:148hxhe 账号:132620 ...

  6. [转帖]一图胜千言 -- SQL Server 基准测试

    https://blog.51cto.com/ultrasql/2130487 文章标签基准测试文章分类SQL Server数据库阅读数1116  

  7. [转帖]ESXi下查看CPU 频率

    https://www.jianshu.com/p/8943a4223ed7 查看CPU的固定频率 [root@localhost:/bin] esxcli hardware  cpu list|gr ...

  8. [转帖]浅谈redis采用不同内存分配器tcmalloc和jemalloc

    http://www.kaotop.com/it/173669.html 我们知道Redis并没有自己实现内存池,没有在标准的系统内存分配器上再加上自己的东西.所以系统内存分配器的性能及碎片率会对Re ...

  9. [转帖]Linux系统:page fault

    Linux进程如何访问内存 Linux下,进程并不是直接访问物理内存,而是通过内存管理单元(MMU)来访问内存资源,原因后面会讲到. 为什么需要虚拟内存地址空间 假设某个进程需要4MB的空间,内存假设 ...

  10. Mysql数据库部分管理命令极简学习总结

    背景 今天遇到一个得很奇怪的问题. Mysql一个运行时间很长的select阻塞了对select里面左连接表做create index 操作的SQL 当时感觉不应该, 一直以为读锁不会与独占更新锁互斥 ...