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

问题分析

首先个人认为题目中的“旋转”可能并不直观,不利于理解,在这里旋转也可以认为是数组向右循环移动,何为循环移动,看下面

1, 2, 3, 4, 5 循环向右移动一位 为 5, 1, 2, 3, 4
1, 2, 3, 4, 5 循环向右移动两位 为 4, 5, 1, 2, 3

题目要求时间复杂度O(logN),而且数组是排序的,肯定是二分法了。但是这个数组却并不是一个完全递增的数组,他的特点是

  1. 数组中存在一个位置index,A = [0, index)递增序列,B = [index, size)是一个递增序列
  2. 递增序列A中的任意一个元素均大于递增序列B的任意一个元素,用数学表示的话就是

对于特点2可能不是很明显,理由如下:

  1. 题中的旋转可以认为进行了X次的向右循环移动
  2. 每次向右循环移动都会将最后一位数移动到最左边
  3. 根据 特点1理由2 可知每次被移动到左边的数字为序列B中最大的元素,移动过后这个元素进入了序列A的最左边,也就是说这个元素成为了序列A中最小的元素
  4. 根据上述三条理由确定一个在每次循环右移之后都成立的条件:

    每次循环右移到数组最左边的元素都是大于递增序列B的任何一个数的,且这个元素是递增序列A中最小的一个元素

或者可以表述为

每次循环右移到递增序列A最左边的元素都是大于递增序列B中的任何一个元素,且这个元素是递增序列A中最小的一个元素

  1. 所以进行了X次向右循环移动的数组依旧满足 理由4 的结论,所以 特点2 成立

解法一:数组中的最小值,对左右两边各进行一次二分查找

很明显,数组的最小值就是递增序列B的第一项。最小值的左边(不包括旋最小值)是一个递增序列,最小值的右边(包括最小值)是一个递增数列。

不是很清楚的读者可以在下图,红线圈住的值就是最小值,也可以很轻易地看出上述规律。

由于题目要求时间复杂度O(lgn),所以我们必须在O(lgn)的时间内找到最小值,具体如何找呢?肯定还是二分搜索的思想,不过要分情况考虑了。

当 nums[mid] > nums[0] 时

应该是下图所示的情况,很明显,最小是在二分点的右边,所以应该是移动左指针,即

left = mid + 1;

当 nums[mid] < nums[0] 时

应该是下图所示的情况,很明显,最小值在二分点的左边,应该移动右指针,即

right = mid - 1;

不过我们少考虑的一种情况,也就是如果整个数组是一个完全递增的数组,也就是并没有经过任何变化,那么上述方法的到的最终结果就是会出出错,所以在二分结果后需要再做一个判断,判断数组的第一位和二分查找得到的结果那个小,即

if nums[0] < nums[mid]minIndex = 0;
elseminIndex = mid;

所以寻找旋转点的最终伪代码为:

findMin(nums)// nums is a arrayleft = 0;right = nums.length - 1;while left <= rightmid = (left + right) / 2;// 判断是否是最小值,也就是左右都比它大if nums[mid] is smaller than left and right valuesbreak;else if nums[mid] > nums[0]left = mid + 1;elseright = mid - 1;if nums[0] < nums[mid]minIndex = 0;elseminIndex = mid;return midIndex;

现在我们找到了最小值了,就可以以最小值为分界点分别对左右两个递增序列进行二分查找了,所以整个程序的伪码如下:

search(nums,target)/*nums is a arraytarget is a number*/minIndex = findMin(nums);left = 0;right = minIndex - 1;while left <= rightmid = (left + right) / 2;if nums[mid] == targetreturn mid;else if target < nums[mid]right = mid - 1;elseleft = mid + 1;left = minIndex;right = nums.length - 1;while left <= rightmid = (left + right) / 2;if nums[mid] == targetreturn mid;else if target < nums[mid]right = mid - 1;elseleft = mid + 1;

时空复杂度分析

找出旋转点O(lgn),两次二分查找均为O(lgn),算法总时间复杂度为O(lgn)

算法使用了常数空间,空间复杂度为O(1)

解法二:直接通过二分法找出目标值

这里会稍微复杂一些,依旧先分情况讨论

当 nums[mid] > nums[0] 且 nums[mid] < target

从图上来看,二分点在目标值的左边,所以应该移动左指针,即

left = mid + 1;

当 nums[mid] > nums[0] 且 nums[mid] > target 且 nums[0] < target

从图上看,二分点在目标值右边,所以应该移动右指针,即

right = mid - 1;

当 nums[mid] > nums[0] 且 nums[mid] > target 且 nums[0] > target

从图上来看,二分点在目标值的左边,应该移动左指针,即

left = mid + 1;

当 nums[mid] < nums[0] 且 nums[mid] > target

从图上来看,二分点在目标值的右边,应该移动右指针,即

right = mid - 1;

当 nums[mid] < nums[0] 且 nums[mid] < target 且 nums[0] > target

从图上来看,二分点在目标值的左边,应该移动左指针,即

left = mid + 1;

当 nums[mid] < nums[0] 且 nums[mid] < target 且 nums[0] < target

从图上来看,二分点在目标值的右边,应该移动右指针,即

right = mid - 1;

当 nums[mid] = nums[0]

从图上来看,目标值肯定在二分点的右边(暂不考虑目标值在最左端的情况),应该移动左指针,即

left = mid + 1;

当目标值位于数组两端

在程序最开始时做判断即可

所以整个程序的伪代码应该为:

search(nums, target)/*nums is a arraytarget is a number*/left = 0;right = nums.length - 1;if nums[0] == targetreturn 0;if nums[nums.length - 1] == targetreturn nums.length - 1;while left <= rightmid = (left + right) / 2;if nums[mid] == targetreturn mid;elseif nums[mid] > nums[0]if target > nums[mid]left = mid + 1;elseif target > nums[0]right = mid - 1;elseleft = mid + 1;else if nums[mid] < nums[0]if target < nums[mid]right = mid - 1;elseif target < nums[nums.size() - 1]left = mid + 1;elseright = mid - 1;elseleft = mid + 1;return -1;

时空复杂度分析

时间复杂度为O(lgn),空间复杂度O(1)

转载于:https://www.cnblogs.com/FDProcess/p/10614145.html

[LeetCode] 搜索旋转排序数组相关推荐

  1. 《LeetCode力扣练习》第33题 搜索旋转排序数组 Java

    <LeetCode力扣练习>第33题 搜索旋转排序数组 Java 一.资源 题目: 整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下 ...

  2. LeetCode (二分小专题)33搜索旋转排序数组34在排序数组中查找元素的第一个和最后一个位置35搜索插入位置

    前言 国庆前最后一次打卡,国庆后继续开启,公众号bigsai回复进群欢迎加入打卡,如有帮助记得点赞收藏. 近期打卡记录: LeetCode 32最长有效括号(困难) (本周) LeetCode 30串 ...

  3. LeetCode 79单词搜索80删除排序数组中的重复项Ⅱ81.搜索旋转排序数组Ⅱ

    新人公众号(求支持):bigsai 专注于Java.数据结构与算法,一起进大厂不迷路! 算法文章题解全部收录在github仓库bigsai-algorithm,求star! 关注回复进群即可加入力扣打 ...

  4. LeetCode.M33.搜索旋转排序数组

    LeetCode.M33 题目: 题目大意: ​ 将一个严格升序且没有重复元素的数组的后半部分(从k处开始)移动到前半部分,进过这个操作变成一个新的数组,然后在这个一个新的数组中查找某个元素targe ...

  5. ​LeetCode刷题实战81:搜索旋转排序数组 II

    算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试.所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 ! 今天和大家 ...

  6. LeetCode高频题33. 搜索旋转排序数组

    LeetCode高频题33. 搜索旋转排序数组 提示:本题是系列LeetCode的150道高频题,你未来遇到的互联网大厂的笔试和面试考题,基本都是从这上面改编而来的题目 互联网大厂们在公司养了一大批A ...

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

    33. 搜索旋转排序数组 题目描述:整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length ...

  8. LeetCode 33. 搜索旋转排序数组 golang

    33. 搜索旋转排序数组 三个测试用例, 这个题的难度是二分法的左侧条件很难写出来. 4 5 6 0 1 2 3 1 2 3 4 5 6 0 5 1 2 3 4 ( 例如,数组 [0,1,2,4,5, ...

  9. LeetCode(81): 搜索旋转排序数组 II

    Medium! 题目描述: 假设按照升序排序的数组在预先未知的某个点上进行了旋转. ( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] ). 编写一个函数来判断给 ...

最新文章

  1. Mysql:AVG()函数如何去除0值做平均值
  2. mysql 闪回查询 sql_利用闪回查看Oracle表历史时刻数据
  3. 数据结构_链表_单向链表
  4. Java反序列s ysoserial Spring
  5. 5G产业(一):5G超低延时噱头?
  6. Qlikview 数据加载方法罗列
  7. 应用计算机测定线性电阻伏安特性实验报告,电阻伏安特性曲线实验报告.docx
  8. 哪款蓝牙耳机音质好?内行推荐四款高音质蓝牙耳机
  9. visio画等分树状图
  10. 01背包问题(动态规划)
  11. VUE3 reactive与toRefs函数
  12. command_execution
  13. OpenCV二值图像连通域分析
  14. 从数据库取String的值存放到Map中 按汉字首字母排序并输出
  15. uni-app图片显示
  16. python多线程爬取段子_Python爬虫实战之一秒爬取内涵段子
  17. JavaWeb开发:从购买服务器到简单demo运行
  18. CUDA:在NPP中直方图均衡化实例
  19. java 双声道音频_两分钟双声道,16位采样精度,22.05KHz采样频率声音的不压缩数据量为...
  20. 申通快递机器人上岗_申通快递机器人效率惊人:70%人工下岗

热门文章

  1. 一个SAP加拿大实习生在当地观察到的美景
  2. 往ABAP gateway system上和Cloud Foundry上部署HTML5应用
  3. Win10计算机首次使用时间,如何查看Win10开机运行了多长时间
  4. 透过率和反射率的关系_全国本科率只有不到百分之五?!醒醒吧!
  5. 超低内阻mos管_FHU100N03低压MOS管保障榨汁机电路正常运行!
  6. c++小项目_编程初学者的练手小项目(Pythonamp;C/C++)
  7. 并查集路径压缩_并查集(UnionFind)技巧总结
  8. mysql 分区索引失效_分区表的本地索引竟然失效了——ORA-01502
  9. 如何用python绘制灰度直方图_Python Opencv任意形状目标检测并绘制框图实例
  10. matlab组织的培训讲义,matlab培训讲义.doc