leetcode - [33]搜索旋转排序数组

202 阅读1分钟

1. 题目描述

[33] 搜索旋转排序数组
	整数数组 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 。
    
示例 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
 -10^4 <= nums[i] <= 10^4
 nums 中的每个值都 独一无二
 nums 肯定会在某个点上旋转
 -10^4 <= target <= 10^4
    
 进阶:你可以设计一个时间复杂度为 O(log n) 的解决方案吗?
 Related Topics 数组 二分查找

2. 思路分析

​ 这道题目与正常的二分查找区别在于,存在一个节点之后的值,都比之前的值小。

​ 提取一下关键点,这个节点之后的值,都小于 nums[0]。我们一开始就可以把目标值与 nums[0]做比较,可以得到目标值在旋转节点左边和右边。根据这个,我们来做二分查找。有四种情况

  1. num[mid] < target 的情况下nums[mid] < num[0]并且 目标值在旋转节点左边,那么目标值肯定在 mid下标的左边,即 high = mid - 1;
  2. num[mid] < target 的情况下除1以外的情况,都与正常二分一样处理
  3. 类似1,2。 num[mid] > target 的情况下,如果 nums[0] > nums[mid] 或者 选节点在左边,那么目标值肯定在 mid下标的左边,即 high = mid - 1;
  4. num[mid] > target 的情况下,除了3以外的情况,都给正常二分不同,即 low = mid + 1

​ 这道题的主要是把上面几个情况分开,道理我都是懂的,就是调的时候有一堆毛病....

3. AC代码

public class No33 {

    public int search(int[] nums, int target) {
        return method1(nums, target);
    }

    private int method1(int[] nums, int target) {
        int low = 0;
        int high =  nums.length - 1;

        // 判断目标值在旋转值左边还是右边
        boolean targetInLeft = nums[0] <= target?true:false;

        while (low <= high) {
            // 找到中间值
            int mid =low + ((high-low)>>1);

            if(nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                if(targetInLeft) {
                    // 目标在旋转点左边,且在mid之前出现了旋转点
                    if(nums[0] > nums[mid]) {
                        high = mid -1;
                    } else {
                        low = mid + 1;
                    }
                } else {
                    low = mid + 1;
                }
            } else {
                // 如果目标在旋转点左边,且 nums[mid] > target
                if(targetInLeft) {
                    high = mid -1;
                } else {
                    if(nums[0] > nums[mid]) {
                        high = mid - 1;
                    } else {
                        low =  mid + 1;
                    }
                }
            }
        }
        return -1;
    }
	
    // 把method1该稍微好看一点点
    private int method2(int[] nums, int target) {
        int low = 0;
        int high =  nums.length - 1;

        // 判断目标值在旋转值左边还是右边
        boolean targetInLeft = nums[0] <= target?true:false;

        while (low <= high) {
            // 找到中间值
            int mid =low + ((high-low)>>1);

            if(nums[mid] == target) {
                return mid;
            } else if (nums[mid] < target) {
                // 目标在旋转点左边,且在mid之前出现了旋转点,, 目标值都在左边
                if(targetInLeft && nums[0] > nums[mid]) {
                    high = mid -1;
                } else {
                    low = mid + 1;
                }
            } else {
                // 如果目标在旋转点左边 或者 nums[mid] > target , 目标值都在左边
                if(targetInLeft || nums[0] > nums[mid]) {
                    high = mid - 1;
                } else {
                    low =  mid + 1;
                }
            }
        }
        return -1;
    }
}

4. 总结

这道题也对应<极客时间-数据结构与算法之美>二分查找的课后练习,推荐大家去看