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

news/2025/3/5 5:28:48/

【刷题之路Ⅱ】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.ppmy.cn/news/45966.html

相关文章

Python词云

词云图wordcloud 1.安装第三方库 j i e b a 库、 m a t p l o t l i b 、 w o r d c l o u d 库 jieba库、matplotlib、wordcloud库 jieba库、matplotlib、wordcloud库 2.过程 1.使用 j i e b a jieba jieba 库对数据进行分词整理&#xff0c;转为 t x t txt txt文件&#…

如何使用数字示波器

本文介绍以鼎阳SIGLENT SDS1122E数字示波器为例。 带了一根电源线&#xff1b;两根信号线&#xff0c;每根信号线都有几个小配件&#xff0c;如下所示&#xff1a; 使用概述 我们都知道万用表&#xff08;又称欧姆表&#xff09;是工程师最常用的调试电路的工具&#xff0c;但万…

CC2642的GGS使用笔记

一、前言 我们了解BLE的GATT之前需要了解一些基本的概念&#xff1a; &#xff08;1&#xff09;Profile,字面意思简介、概述、形象印象、轮廓、配置文件&#xff0c;在BLE中&#xff0c;我们可能把它理解成配置文件较好&#xff0c;Profile有一些是BLE SIG规定的&#xff0c;有…

Redis 实现限流

Redis 实现限流的三种方式 面对越来越多的高并发场景&#xff0c;限流显示的尤为重要。最近在网上看到几个demo&#xff0c;做一下记录吧。 限流有许多种实现的方式&#xff0c;Redis具有很强大的功能&#xff0c;我用Redis实践了三种的实现方式&#xff0c;可以较为简单的实…

Python接口自动化测试实战详解

接口自动化测试是指通过编写程序来模拟用户的行为&#xff0c;对接口进行自动化测试。Python是一种流行的编程语言&#xff0c;它在接口自动化测试中得到了广泛应用。下面详细介绍Python接口自动化测试实战。 1、接口自动化测试框架 在Python接口自动化测试中&#xff0c;我们…

《MCU》专栏完整目录

MCU专栏完整目录 专栏状态&#xff1a;持续更新中 文章目录 一、 电路二、单片机2.1 GD322.2 STM322.3 51单片机2.4 ZigBee2.5 Arduino 三、Linux四、RTOS五、其他 一、 电路 1、开关电路&#xff1a;单片机引脚驱动电路&#xff08;负载开关电路&#xff09;点击进入 2、二极…

什么是机器学习?

目录 简介 机器学习可以做什么 机器学习未来的趋势 总结 简介 机器学习是一种人工智能领域中的技术&#xff0c;其主要目的是让计算机能够自动进行模式识别、数据分析和预测。 机器学习的起源可以追溯到20世纪50年代&#xff0c;当时美国的Arthur Samuel在一篇论文中提出了相关…

Ubuntu上搭建网站【建立数据隧道,降低开支】

上篇&#xff1a;Ubuntu搭建web站点并发布公网访问 目录 1.安装WordPress 2.创建WordPress数据库 3.安装相对URL插件 4.内网穿透将网站发布上线 1.命令行方式&#xff1a; 2.图形化操作方式 5.图书推荐 cpolar官网 1.安装WordPress 在前面的介绍中&#xff0c;我们为大…