【LeetCode 系列】LeetCode 33 搜索旋转排序数组

【LeetCode 系列】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

解题思路

因为题目要求时间复杂度为 O(log n) 级别,所以肯定是要用二分查找的。
现在我们来想一下,普通的二分查找是怎么做的?
在一个长度为 l 的有序数组 nums 中,找一个 target 元素。

  1. 将 start 记为 0,end 记为 nums.length,取数组中间的数,记为 nums[mid],mid 记为中间数的下标。
  2. 若 nums[mid] == target,则直接返回该元素。反之下一步。
  3. 若 nums[mid] < target,说明 target 在数组的右边,那么 start = mid + 1,end 不变(也就是在数组右半边找)
  4. 若 nums[mid] > target,说明 target 在数组的左边,那么 end = mid - 1,start 不变(也就是在数组左半边找)
  5. 重复第 2 步到第 4 步,没有匹配出 a[mid] == k 的条件,则返回 -1

现在情况是数组是旋转排序的,不是有序的。举个例子,1 2 3 4 5 6 7 若变成了旋转排序,可以大致分为两类。

  1. 3 4 5 6 7 1 2 这种,nums[start] <= nums[mid],有序的在左边。如果 nums[start] <= target < nums[mid],在左边找,否则去右边找。
  2. 5 6 7 1 2 3 4 这种,nums[start] > nums[mid],有序的在右边。如果 nums[mid] < target <= nums[end],在右边找,否则去左边找。

梳理本题思路

  1. 将 start 记为 0,end 记为 nums.length,将目标数记为 target。
  2. 取数组中间的数,记为 nums[mid],mid 记为中间数的下标。
  3. 若 nums[mid] == target 则直接返回该元素。反之下一步。
  4. 若 nums[start] <= nums[mid] 说明左边有序
    4.1 若 nums[start] <= target < nums[mid],说明是 3 4 5 6 7 1 2 的情况,target 在左边,从左边开始找。
    4.2 反之从右边开始找。
  5. 若 nums[start] > nums[mid] 说明右边有序
    5.1 若 nums[start] 小于等于 num[mid],说明是 5 6 7 1 2 3 4 的情况,target 在右边,从右边开始找
    5.2 反之从左边开始找。
  6. 一直重复 2 -> 5 步骤,直到 start > end。

本题 Accept 代码

class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int start = 0;
        int end = nums.length - 1;
        int mid;
        while (start <= end) {
            // 计算中间下标 (防止溢出)
            mid = start + (end - start) / 2;
            // 若中间位置的数等于 target,立即返回下标
            if (nums[mid] == target) {
                return mid;
            }
            // 若 nums[start] 小于等于 num[mid],说明是 3 4 5 6 7 1 2 的情况,此时左边有序
            if (nums[start] <= nums[mid]) {
                // nums[start] <= target < nums[mid],说明 target 在左边,从左边找
                if (target >= nums[start] && target < nums[mid]) {
                    end = mid - 1;
                } else {
                    // 反之,从右边找
                    start = mid + 1;
                }
            } else {
                // 若 nums[start] 小于等于 num[mid],说明是 5 6 7 1 2 3 4 的情况,此时右边有序
                // nums[mid] < target <= nums[end],说明 target 在右边,从右边找
                if (target > nums[mid] && target <= nums[end]) {
                    start = mid + 1;
                } else {
                    // 反之,从左边找
                    end = mid - 1;
                }
            }

        }
        return -1;
    }
}

你可能感兴趣的:(LeetCode)