GuilinDev

Lc0033

05 August 2008

33 - Search in Rotated Sorted Array

原题概述

Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand.

(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2] ).

You are given a target value to search. If found in the array return its index, otherwise return ‘-1’.

You may assume no duplicate exists in the array.

Your algorithm’s runtime complexity must be in the order of O(log n).

Example 1:

1
2
Input: nums = [4,5,6,7,0,1,2], target = 0
Output: 4

Example 2:

1
2
Input: nums = [4,5,6,7,0,1,2], target = 3
Output: -1

题意与分析

一个已经升序排好的数组以某个轴rotated调转左右的元素,然后在调转后的array里寻找到指定的元素,找到就返回index,没找到则返回-1,数组里的元素是没有重复的。

这道题是上一道题35 - Search Insert Position的变体,看似有点麻烦,其实理清一下还是比较简单的。 因为rotate的缘故,当切取一半的时候要做进一步的判断。具体来说,假设数组是nums,每次左边缘为left,右边缘为right,还有中间位置是 mid。在每次迭代中,分三种情况:

(1)如果nums[mid] == target,那么mid就是我们要的结果,直接返回;

(2)如果nums[mid] < nums[right],那么说明从mid到right一定是有序的(没有受到rotate的影响),那么这时候判断target是不是在mid到right之间,如果是则把左边缘移到mid+1,如果不是就右边缘移到mid-1。

(3)如果nums[mid] >= nums[right],那么说明mid到right收到了rotate的影响,而从left到mid一定是有序的,这时候同样判断target在哪个范围,然后相应的移动边缘即可。

所以先根据nums[mid]的值跟nums[left]或者nums[right]比较,可以先知道mid左右两边哪边是有序的,然后再先判断target是否在有序的部分,so on and so forth,每次都可以去掉一半的数据,Time是O(logn),Space是O(1)。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        int left = 0, right = nums.length - 1;
        // 利用模板,1. 先跟right比较,判断有序的是左侧还是右侧,2. 再“先”在其中判断target是否在有序的该侧
        while (left + 1 < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            }  if (nums[mid] > nums[right]) { // 左侧有序
                if (nums[left] == target) {
                    return left;
                } else if (nums[left] < target && nums[mid] > target) { // 哪边有序则double check target是否在有序的这一侧
                    right = mid;
                } else { // 在乱序的右侧
                    left = mid;
                }
            } else { // 右侧有序
                if (nums[right] == target) { 
                    return right;
                } else if (nums[right] > target && nums[mid] < target) {// 哪边有序则double check target是否在有序的这一侧
                    left = mid;
                } else { // 在乱序的左侧
                    right = mid;
                }
            }
        }
        
        if (nums[left] == target) { // post-processing
            return left;
        } else if (nums[right] == target) { 
            return right;
        }
        return -1;
    }
}

没那么多if statements

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Solution {
    public int search(int[] nums, int target) {
        if (nums == null || nums.length == 0) {
            return -1;
        }
        
        int left = 0;
        int right = nums.length - 1;
        
        while (left + 1 < right) {
            int mid = left + (right - left) / 2;
            if (nums[mid] == target) {
                return mid;
            } else if (nums[mid] > nums[right]) { //左侧有序
                if (nums[left] <= target && nums[mid] > target) { // 判定target是否在有序的左侧
                    right = mid;
                } else { // 在乱序的右侧
                    left = mid;
                }
            } else { // nums[mid] < nums[right],右侧有序
                if (nums[right] >= target && nums[mid] < target) { // 判定target是否在有序的右侧
                    left = mid;
                } else {// 在乱序的左侧
                    right = mid;
                }
            }
        }
        
        if (nums[left] == target) {
            return left;
        }
        return nums[right] == target ? right : -1;
    }
}