leetcode刷题日记-【33. 搜索旋转排序数组】

137 阅读2分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

题目描述

整数数组 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

题目元素

  • 给定一个无重复元素的数组,数组中某一个节点前后都成升序排列,且节点之前的元素都大于节点之后的元素;
  • 给定target,求target在数组中的位置,如果不存在则返回-1;
  • 要求时间复杂度为O(log n),看见logn的复杂度就要联想到二分法,这道题可以用二分法解决。

解题思路

逆转后的数组,在节点k前后一定是呈升序的两个数组,且第一个数组的值都要大于第二个数组。 所以可以将数组进行二分,数组最左边的值叫left,数组最右边的值叫right,找到数组中点mid,然后将任意一边的值和mid的值作比较,假定我们取left和mid作比较,则有两种情况:

  • mid >= left,此时数组nums中[left,mid]的值一定是升序的,即它一定是有序的数组。这里又有两种情况:一种是正好target在这个有序范围内,则可以继续对这个有序数组进行二分,知道找到target的位置或者二分后的数组大小为1,但是仍未找到target,则返回-1;另一种是target不在这个区间内,则需要在[mid,right]数组中寻找有序数组,且target在这个有序数组范围内。
  • mid < left,次数数组nums中的[mid,right]的所有元素一定是升序的,处理方式和上一种情况如出一辙,不一样的是,这里需要查找的范围是[mid,right]。

例如给定数组nums[5,6,7,8,9,1,2,3,4],target为2。

第一次mid=4,nums[4] = 9,此时9>5,所以数组[0,4]一定是有序的,然后判断target在不在此范围内,不在。此时数组范围缩小到[1,2,3,4](二分);

第二次mid=2,mid==target,所以直接返回2的位置。

记住一定要先判断数组是有序的,然后才能将target和这个数组的首尾元素进行比较。

代码实现

public static int search(int[] nums, int target) {
    // 获取数组中点
    int left = 0;
    int right = nums.length-1;
    while (left <= right) {
        int mid = left+(right-left)/2;
        if (target == nums[right]) {
            return right;
        } else if (target == nums[left]) {
            return left;
        } else if (target == nums[mid]) {
            return mid;
        }
        if (nums[mid] >= nums[left] ) {
            if (nums[left] < target && target < nums[mid]) {
                // 呈线性关系,且目标target在左边
                right = --mid;
            } else {
                // 查找右边数组
                left = ++mid;
            }
        }else {
            if (nums[mid] <= nums[right] && nums[mid] < target && target < nums[right]) {
                // 右边呈线性关系,且目标在右边
                left = ++mid;
            } else {
                //查找左边数组
                right = --mid;
            }
            //
        }

    }
    return -1;
}