leetcode练习之数组33.搜索旋转排序数组

题目

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [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) 级别,所以很快我们就能想到用二分法进行搜索。只是二分法需要在有序的数组中操作,但是我们这里旋转后的排序数组是分段有序。

答题思路

分步将旋转数组恢复,然后二分法

首先这种分步思维有利于我们把问题拆解。有些像是想成为救火员的故事中的数学家的思维。尽管令人啼笑皆非,但是有规律可循,如果遇到火,就灭火;如果没有火,那就先点火,再灭火。

其实这里有个小提示,二分法需要在有序数组中使用,且每次划分是以一半进行。那么我们可以一半一半的判断是否有序。这也就引导我们到了一个更完备的算法。如下所示。

直接二分法

  1. 如果mid的值大于等于start的值,那么从start到mid这部分为有序数组
  2. 如果target介于start和mid的值之间,则二分法在前半部分查找
  3. 反之,如果mid的值小于start的值,那么从mid到end这部分为有序数组
  4. 如果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

总结

  1. 直接二分法的最好时间复杂度O(1),最坏时间复杂度O(log n),平均时间复杂度O(log n)。
  2. 直接二分法的最好空间复杂度O(1),最坏空间复杂度O(1),平均空间复杂度O(1)。
  3. don’t you quit.
发布了19 篇原创文章 · 获赞 1 · 访问量 235

猜你喜欢

转载自blog.csdn.net/helen920318/article/details/104822005
今日推荐