题目
假设按照升序排序的数组在预先未知的某个点上进行了旋转。
( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。
搜索一个给定的目标值,如果数组中存在这个目标值,则返回它的索引,否则返回 -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
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
题目解读
旋转后的排序数组其实是以旋转点为划分,前后两部分都为升序数组,但是我们并不知道旋转点。所以是否需要找到准确的旋转点,我们需要进行考量。
此外,本题对于时间复杂度要求为O(log n) 级别,所以很快我们就能想到用二分法进行搜索。只是二分法需要在有序的数组中操作,但是我们这里旋转后的排序数组是分段有序。
答题思路
分步将旋转数组恢复,然后二分法
首先这种分步思维有利于我们把问题拆解。有些像是想成为救火员的故事中的数学家的思维。尽管令人啼笑皆非,但是有规律可循,如果遇到火,就灭火;如果没有火,那就先点火,再灭火。
其实这里有个小提示,二分法需要在有序数组中使用,且每次划分是以一半进行。那么我们可以一半一半的判断是否有序。这也就引导我们到了一个更完备的算法。如下所示。
直接二分法
- 如果mid的值大于等于start的值,那么从start到mid这部分为有序数组
- 如果target介于start和mid的值之间,则二分法在前半部分查找
- 反之,如果mid的值小于start的值,那么从mid到end这部分为有序数组
- 如果target介于mid和end的值之间,则二分法在后半部分查找
PHP版本
执行用时:8 ms
内存消耗:15.2 MB
class Solution {
/**
* @param Integer[] $nums
* @param Integer $target
* @return Integer
*/
function search($nums, $target) {
if (count($nums)==0) {
return -1;
}
$start = 0;
$end = count($nums)-1;
while ($start<=$end) {
$mid = $start + floor(($end-$start)/2);
if ($target == $nums[$mid]) {
return $mid;
}
//前半部分有序
if ($nums[$start]<=$nums[$mid]) {
if ($nums[$start]<=$target&&$target<$nums[$mid]) {
$end = $mid-1;
} else {
$start = $mid+1;
}
} else {//后半部分有序
if ($nums[$mid]<$target&&$target<=$nums[$end]) {
$start = $mid+1;
} else {
$end = $mid-1;
}
}
}
return -1;
}
}
Go版本
执行用时 :0 ms
内存消耗 :2.6 MB
func search(nums []int, target int) int {
if (len(nums)==0) {
return -1
}
start,end := 0,len(nums)-1
for start<=end {
mid := start+(end-start)/2
if (target == nums[mid]) {
return mid
}
//前半部分有序
if (nums[start]<=nums[mid]) {
if (target>=nums[start]&&target<nums[mid]) {
end = mid-1
} else {
start = mid+1
}
} else {
if (target>nums[mid]&&target<=nums[end]) {
start = mid+1
} else {
end = mid-1
}
}
}
return -1
}
发现耗时和内存占用此消彼长,由于这一版的耗时是0ms,所以我不禁想减少内存消耗。发现可以在while循环外先声明mid,在里面给mid再修改赋值。这样内存占用就打败100%的go提交记录了。当然耗时增加了。
start,mid,end := 0,0,len(nums)-1
总结
- 直接二分法的最好时间复杂度O(1),最坏时间复杂度O(log n),平均时间复杂度O(log n)。
- 直接二分法的最好空间复杂度O(1),最坏空间复杂度O(1),平均空间复杂度O(1)。
- don’t you quit.