壹 ❀ 引

本来今天(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. springboot启动流程 (2) 组件扫描

    SpringBoot的组件扫描是基于Spring @ComponentScan注解实现的,该注解使用basePackages和basePackageClasses配置扫描的包,如果未配置这两个参数,S ...

  2. Python Code_05位运算

    coding:utf-8 author : 写bug的盼盼 development time : 2021/8/28 7:16 print(4&8)#非1即0 print(4|2)#同0即0, ...

  3. Linux复习笔记

    Linux复习笔记 常识说明 目录结构 Linux以树型结构管理文件,其最上层文件夹为 / ,也就是根目录. 如图所示,图中展示了一部分文件夹的结构: 所有的文件夹都属于根目录的子文件夹. 安装好系统 ...

  4. 单元测试中如何Mock HttpContext

    最近团队有小伙伴问在单元测试中如何Mock HttpContext. 这是一个好问题,整理了一个实现方案分享给大家. 在C#中,尤其是在单元测试场景下,模拟(Mocking)HttpContext 是 ...

  5. [转帖]Linux 下rsync命令详细整理

    https://blog.csdn.net/weixin_44052462/article/details/116134761 rsync是一个功能非常强大的工具,其命令也有很多功能特色选项,我们下面 ...

  6. [转帖]深度硬核文:Nginx的301重定向处理过程分析

    https://zhuanlan.zhihu.com/p/84539204 本文首发于公众号:js-mindmap 一,序言 "晚上九点,办公室里烟雾缭绕,工作进度依然没有什么进展.王二胖打 ...

  7. [转帖]061、监控指标之TiKV

    资源相关 Grafana监控 TiKV-Details -> Cluster - Store Size / Available Size Grafana监控 TiKV-Details -> ...

  8. [转帖]Jmeter性能测试:高并发分布式性能测试

    一.为什么要进行分布式性能测试 当进行高并发性能测试的时候,受限于Jmeter工具本身和电脑硬件的原因,无法满足我们对大并发性能测试的要求.基于这种场景下,我们就需要采用分布式的方式来实现我们高并发的 ...

  9. 【小测试】rust中的无符号整数溢出

    作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢! cnblogs博客 zhihu Github 公众号:一本正经的瞎扯 1.在编译阶段就可以识别出来的溢出 fn main(){ ...

  10. 【JS 逆向百例】当乐网登录接口参数逆向

    声明 本文章中所有内容仅供学习交流,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,若有侵权,请联系我立即删除! 逆向目标 目标:当乐网登录 主页:https://oauth.d.cn ...