【刷题之路Ⅱ】LeetCode 3381.搜索旋转排序数组ⅠⅡ

news2025/1/15 23:18:49

【刷题之路Ⅱ】LeetCode 33&81.搜索旋转排序数组Ⅰ&Ⅱ

  • 一、题目描述
  • 二、解题
    • 1、方法1——暴力法
      • 1.1、思路分析
      • 1.2、代码实现
    • 2、方法2——二分法
      • 2.1、思路分析
      • 2.2、代码实现
      • 2.3、升级到81题
        • 2.3.1、改进思路分析
        • 2.3.1、改进代码实现
    • 3、改进二分法
      • 3.1、思路分析
      • 3.2、代码实现

一、题目描述

原题连接: 33. 搜索旋转排序数组 81. 搜索旋转排序数组 II
题目描述:
33题的描述如下:

整数数组 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 。
你必须设计一个时间复杂度为 O(log n) 的算法解决此问题。

示例 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
-104 <= nums[i] <= 104
nums 中的每个值都 独一无二
题目数据保证 nums 在预先未知的某个下标上进行了旋转
-104 <= target <= 104

而81题只是在33题的基础上增加了有重复元素的条件。

二、解题

1、方法1——暴力法

1.1、思路分析

顺序遍历数组中所有的元素,遇到nums[i] == target返回i即可。

1.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

int search1(int* nums, int numsSize, int target) {
    assert(nums);
    int i = 0;
    for (i = 0; i < numsSize; i++) {
        if (nums[i] == target) {
            return i;
        }
    }
    return -1;
}

时间复杂度:O(n),n为数组长度。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

当然啦,暴力法是万能的,所以对于81题这个方法根本不用做任何修改也能直接通过。

2、方法2——二分法

2.1、思路分析

看到题目中给的“有序”我们就应该想到要用二分查找法,但这里的有序并不是完全有序,而是部分有序。
而我们知道,对于完全有序的序列,我们是可以百分百的确定一个数是否在这个序列中的,那我们就可以用二分法的变种——二分搜索,该算法可以每次淘汰一半的数据,具体思路如下:
先判断nums[left]和nums[mid]和nums[right]中有没有等于target的的,如果有直接返回下标即可;
若nums[mid] != target,则应判断mid两边的区间[left, mid] 和 [mid, right]主要看有序的那边,这里优先判断左端是否有序。如果target在有序的那边,那就转而搜索有序的那边:
在这里插入图片描述
就将搜索区间改成[left + 1, mid - 1] (因为两端和中点都被判断过):
在这里插入图片描述
否则,转而判断另一端:
在这里插入图片描述
这里给出该方法的递归版本。

2.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

// 先写一个递归的二分搜索算法
int binary_search(int* nums, int left, int right, int target) {
    assert(nums);
    if (left > right) {
        return -1;
    }
    int mid = left + (right - left) / 2;
    if (nums[left] == target) {
        return left;
    }
    else if (nums[mid] == target) {
        return mid;
    }
    else if (nums[right] == target) {
        return right;
    }
    else {
        if (nums[left] < nums[mid]) {
            if (target > nums[left] && target < nums[mid]) {
                return binary_search(nums, left + 1, mid - 1, target);
            }
            else {
                return binary_search(nums, mid + 1, right - 1, target);
            }
        }
        else {
            if (target > nums[mid] && target < nums[right]) {
                return binary_search(nums, mid + 1, right - 1, target);
            }
        }
    }
    return binary_search(nums, left + 1, mid - 1, target);
}

int search2(int* nums, int numsSize, int target) {
    assert(nums);
    return binary_search(nums, 0, numsSize - 1, target);
}

时间复杂度:O(logn),n为数组长度。
空间复杂度:O(1)。

2.3、升级到81题

2.3.1、改进思路分析

我们看到81题的描述中其实就只是增加了一个条件,就是可能有重复元素:
在这里插入图片描述
但就是因为增加了这个条件才导致了一个不怎么好解决的问题,那就是可能会出现nums[left]和nums[mid]和nums[right]都相同的情况,例如:
在这里插入图片描述
这时候如果再用像33题那样的判断方法,就无法判断出哪一端是有序或无序的。
这个时候我们其实可以继续递归判断区间[left + 1, right - 1]的,因为在nums[left]和nums[right]相同的时候,在区间[left, right]和在区间[left + 1, rihgt - 1]中查找是等价的:
在这里插入图片描述
然后其他地方只需要改变一处,就是在判断左端是否有序时将nums[left] < nums[mid]改成nums[left] <= nums[mid]即可。

2.3.1、改进代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

// 先写一个递归版的二分查找法
bool binary_search(int* nums, int left, int right, int target) {
    assert(nums);
    if (left > right) {
        return false;
    }
    int mid = left + (right - left) / 2;
    if (nums[left] == target) {
        return true;
    }
    else if (nums[mid] == target) {
        return true;
    }
    else if (nums[right] == target) {
        return true;
    }
    else if (nums[left] == nums[mid] && nums[mid] == nums[right]) {
        return binary_search(nums, left + 1, right - 1, target);
    }
    else if (nums[left] <= nums[mid]) {
        if (target > nums[left] && target < nums[mid]) {
            return binary_search(nums, left + 1, mid - 1, target);
        }
        else {
            return binary_search(nums, mid + 1, right - 1, target);
        }
    } else {
        if (target > nums[mid] && target < nums[right]) {
            return binary_search(nums, mid + 1, right - 1, target);
        }
    }
    return binary_search(nums, left + 1, mid - 1, target);
}
bool search(int* nums, int numsSize, int target){
    assert(nums);
    return binary_search(nums, 0, numsSize - 1, target);
}

时间复杂度:O(logn),n为数组长度。
空间复杂度:O(1)。

3、改进二分法

3.1、思路分析

对于33题,其实我们可以这样来改进算法:
其实我们可以通过nums[0]和nums[mid]的大小关系来判断mid所在的区间范围,当nums[mid]>nums[0]时:
在这里插入图片描述
说明mid所在的区间为较大的那部分升序,而当nums[mid] < nums[0]时,则说明mid所在的区间为较小的那一部分升序:
在这里插入图片描述
所以,我们就可以这样来改进我们的算法:
当nums[mid] >= nums[0]且nums[0] < target < nums[mid]时,说明target在范围为[left, mid ]“大部分"区间里,所以执行right = mid - 1:
在这里插入图片描述
当nums[mid] < nums[0]但target < nums[mid]时,则说明mid所在的区间为"小部分”,而target比nums[mid]更小,所以执行right = mid - 1:
在这里插入图片描述
当nums[mid] < nums[0]且target >= nums[0]时,说明mid所在的区间为"小部分",而target所在的区间为"大部分"所以我们还是要执行right = mid - 1:
在这里插入图片描述

其他情况都执行left = mid + 1;

3.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

int search3(int* nums, int numsSize, int target) {
    assert(nums);
    int left = 0;
    int right = numsSize - 1;
    int mid = 0;
    while (left < right) {
        mid = left + (right - left) / 2;
        if (nums[mid] == target) {
            return mid;
        }
        else if (nums[mid] >= nums[0] && nums[0] <= target && target  <= nums[mid]) {
            right = mid;
        }
        else if (nums[mid] < nums[0] && target < nums[mid]) {
            right = mid;
        }
        else if (nums[mid] < nums[0] && target >= nums[0]) {
            right = mid;
        }
        else {
            left = mid + 1;
        }
    }
    return left == right && nums[left] == target ? left : -1;
}

时间复杂度:O(logn),n为数组的长度。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/435483.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

STM32F407ZIT6 国产替代者NS32F407ZIT6 NS32F407ZGT6 软硬件通用 无需修改任何程序代码

NS32F407ZIT6 NS32F407ZGT6 器件基于高性能的 ARM Cortex-M4 32位 RISC 内核&#xff0c;工作频率高达 168MHz。 Cortex-M4 内核带有单精度浮点运算单元(FPU)&#xff0c;支持所有 ARM 单精度数据处理指令和数据类型。它还 具有一组DSP指令和提高应用安全性的一个存储器保护…

Windows环境下调试DAB-DETR与Deformable-DETR

先前都是在服务器上运行DETR的相关程序&#xff0c;服务器使用的是Linux&#xff0c;所以运行较为简单&#xff0c;但如果想要简单的debug的话就没必要使用服务器了&#xff0c;今天便来在Winodws环境下调试DETR类项目&#xff0c;这里以Deformable-DETR与DAB-DETR为例。 首先是…

深度测评全新大模型「天工」,这些AI体验太香了

ChatGPT火了后&#xff0c;很多人都在关注“国产ChatGPT”的名号究竟花落谁家。 事实上&#xff0c;名号不重要&#xff0c;体验才是王道。ChatGPT能够火成“史上增长最快的消费者应用”&#xff0c;关键在于把体验提升到了新层次。毕竟对于用户来说&#xff0c;并不清楚产品背…

【WSN定位】加权质心定位算法-Matlab代码

文章目录 1. 质心定位算法2. 加权质心定位算法3. 部分代码展示4. 效果图展示5. 资源获取说明 摘要&#xff1a;质心定位算法(Centroid Algorithm)是 Nirupama Bulusu等提出的一种无需测距的粗精度定位算法。质心算法的基本思路&#xff1a;利用未知节点通信范围内的所有锚节点进…

树莓派下编译 PyMiniRacer

文: fasiondog 因需要在树莓派&#xff08;及其其他各类派&#xff09;下使用 akshare&#xff0c;但 akshare 的依赖库 PyMiniRacer 缺少 arm64 架构的包&#xff08;该包已经不再维护&#xff09;&#xff0c;故在此记录下在 linux arm64 架构下编译 PyMiniRacer 的过程。有…

【微信小程序】 权限接口梳理以及代码实现

​ 1、权限接口说明 官方权限说明   部分接口需要经过用户授权统一才能调用。我们把这些接口按使用范围分成多个scope&#xff0c;用户选择对scope进行授权&#xff0c;当授权给一个scope之后&#xff0c;其对应的所有接口都可以直接使用。 此类接口调用时&#xff1a; 如…

小红书报告:2023美妆个护白皮书解读

导语 面对风云变幻的市场&#xff0c;消费行业始终是增长的重要引擎之一&#xff0c;其中美妆个护行业在过去六年里&#xff08;2016-2022年&#xff09;仍保持高增速与消费潜力 。 新的一年&#xff0c;市场呈现什么新变化&#xff0c;品牌又有哪些新动作&#xff1f;本期为…

20-HTML

目录 1.概念 2.创建HTML页面 3.运行HTML程序 4.HTML结构 4.1.HTML文件基本结构 4.2.标签层次结构 PS&#xff1a;开发者工具简单介绍 PS&#xff1a;快速生成代码框架&#xff1a; 5.HTML常见标签 5.1.注释标签 5.2.标题标签&#xff1a;h1 - h6 5.3.段落标签&…

Doris(8):数据导入(Load)之Insert Into

Insert Into 语句的使用方式和 MySQL 等数据库中 Insert Into 语句的使用方式类似。但在 Doris 中&#xff0c;所有的数据写入都是一个独立的导入作业。所以这里将 Insert Into 也作为一种导入方式介绍。 主要的 Insert Into 命令包含以下两种&#xff1b; INSERT INTO tbl S…

PX4控制云台的两种方式

1、配置MNT_MODE_IN参数后重启飞控&#xff0c;释放出mount的设置参数 控制信号输入为RC 则接收机必须连接对应的通道至AUX辅助通道&#xff0c;并通过MNT_MAN_*参数设置pitch/roll/yaw对应的AUX通道 &#xff08;遥控器摇杆 → 接收机通道输出 → 飞控AUX通道输入 → AUX通道…

Spring IOC 入门简介【自定义容器实例】

目录 前言 Spring简介 Spring体系结构 一、IOC控制反转思想 二、IOC自定义对象容器 1. 创建实体类&#xff0c;Dao接口&#xff0c;实现类 2. 创建配置文件bean.properties 3. 创建容器管理类 4. 创建StudentService类 5. 测试方法 6. 测试结果 前言 Spring简介 Spr…

Android 详解基本布局以及ListView和RecyclerView

前言 一个丰富的界面有很多个控件组成&#xff0c;那么就需要布局来实现控件有条不紊的摆放。 一 LinearLayout 线性布局 LinearLayout 又称作线性布局&#xff0c;是一种常用的布局。有横向和竖向两个方向。 通过下面的属性进行设置 android:orientation"vertical"…

【科研】Michaelis–Menten 曲线(米氏曲线)中反应速率的计算

参考文献&#xff1a;Standardized assays for determining the catalytic activity and kinetics of peroxidase-like nanozymes | Nature Protocols SCI-Hub链接&#xff1a; https://sci-hub.se/10.1038/s41596-018-0001-1 科研需要&#xff0c;需要绘制所制备纳米酶的Mi…

亿发软件:中小企业定制一体化管理解决方案,全面提升数据价值

亿发软件&#xff1a;中小企业定制一体化管理解决方案&#xff0c;全面提升数据价值 一体化信息管理系统是为中小企业提供简化运营、提高效率和生产力的工具。随着经济的快速发展&#xff0c;企业细分管理的多样化需求显著增加。很多企业都在为找不到适合自己的一体化信息管理…

一文看懂数据云平台的“可观测性”技术实践

背景 这是一家大型制造集团。为监控及预测工厂设备运行情况&#xff0c;IT部门在数据云平台DataSimba上按天执行数据作业&#xff0c;每24小时对工厂设备的日志数据进行分析&#xff0c;发现能对业务起到很好的辅助作用&#xff0c;效果不错。 “要不升级为每1个小时跑一次&am…

android hook(Toast BadTokenException案例)

什么是Hook? hook 技术又叫做钩子函数&#xff0c;在系统没有调用该函数之前&#xff0c;钩子程序先捕捉该消息&#xff0c;钩子函数先得到控制权&#xff0c;这时钩子函数即可以加工处理&#xff08;改变&#xff09;该函数的执行行为&#xff0c;还可以强制结束消息的传递。…

(详细)HTTP协议(应用层重点协议)

目录 一、HTTP协议工作过程 二、 协议格式 1、抓包工具Fiddler 2、HTTP请求格式 3、HTTP响应格式 4、HTTP协议格式总结 三、HTTP请求&#xff08;Request&#xff09; 1、URL &#xff08;1&#xff09;URL基本格式 &#xff08;2&#xff09;URL实例 &#xff08;…

什么是伪原创?SEO伪原创该怎么做

伪原创是指在原有的文章或内容基础上进行修改或调整&#xff0c;以产生看起来是全新内容的文章&#xff0c;但实际上并没有创造新的价值。多数情况下&#xff0c;伪原创的目的是为了在文章相对原创的情况下&#xff0c;提高搜索引擎的排名。 一、高质量伪原创 做好伪原创&#…

SpringCloud消息总线——Bus

Bus 本专栏学习内容来自尚硅谷周阳老师的视频 有兴趣的小伙伴可以点击视频地址观看 在SpringCloud Config学习过程中&#xff0c;还遗留下来一个问题&#xff1a;当运维更新git上的配置信息时&#xff0c;要想更改所有的客户端服务&#xff0c;必须得手动给客户端服务发送post请…

ESP32学习笔记14-mqtt-连接官方mqtt,onenet,thingsboard物联网平台

12.MQTT 12.0工程里的WiFi密码和ssid设置 工程的WiFi配置 ssid password 打开配置 配置ssid和密码 工程配置文件sdkconfig IP和端口配置 乐鑫服务器mqtt 12.1数据结构和配置函