美文网首页
搜索旋转排序数组

搜索旋转排序数组

作者: M_lear | 来源:发表于2019-05-02 22:57 被阅读0次

    一、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

    题目求解

    解法一

    先二分查找数组中最大值的位置,将数组分成两个升序子数组,再在目标值所在的子数组内二分查找目标值。

    class Solution {
        public int search(int[] nums, int target) {
            int len = nums.length;
            if(len == 0) return -1;
            int low = 0, high = len-1, mid = 0;
            while(low < high) {
                mid = (low+high) >>> 1;
                if(mid+1 <= high && nums[mid] > nums[mid+1]) break;
                if(nums[mid] > nums[high]) low = mid+1;
                else high = mid;
            }
            // 未旋转的情况
            if(low == high) return binarySearch(nums, 0, len-1, target);
            if(nums[len-1] < target) return binarySearch(nums, 0, mid, target);
            else return binarySearch(nums, mid+1, len-1, target);
        }
        private int binarySearch(int[] nums, int low, int high, int target) {
            int mid;
            while(low <= high) {
                mid = (low+high) >>> 1;
                if(nums[mid] < target) low = mid+1;
                else if(nums[mid] > target) high = mid-1;
                else return mid;
            }
            return -1;
        }
    }
    

    解法二

    直接改造二分查找。

    class Solution {
        public int search(int[] nums, int target) {
            int low = 0, high = nums.length-1, mid;
            while(low <= high){
                mid = (low+high) >>> 1;
                if(nums[mid] < target){
                    // 中值在右区间,目标值在左区间,需左移
                    if(nums[low]>nums[mid] && nums[low]<=target) high = mid-1;
                    // 正常情况,右移
                    else low = mid+1;
                }else if(nums[mid] > target){
                    // 中值在左区间,目标值在右区间,需右移
                    if(nums[low]<=nums[mid] && nums[low]>target) low = mid+1;
                    // 正常情况,左移
                    else high = mid-1;
                }else return mid;
            }
            return -1;
        }
    }
    

    对比来看的话,解法二应该更优一点。

    二、leetcode 81. 搜索旋转排序数组 II

    题目描述(题目难度,中等)

    这是上一题的延伸题目,和上一题的区别是,本题中的 nums 可能包含重复元素。

    题目求解

    由于本题中的 nums 包含重复元素,所以当 nums[mid] == nums[low] == nums[high] 时,nums[mid] 没办法根据 nums[low] 或 nums[high],确定 mid 当前是处于左升序区间还是右升序区间。
    例如,[2, 2, 2, 3, 2] 和 [2, 1, 2, 2, 2]。

    我的解决方案是,首先通过指数搜索,快速找到第一个不等于 nums[nums.length-1] 的值,如果没找到,就老老实实顺序查找,否则,就采用上一题的思路,继续查找。

    class Solution {
        public boolean search(int[] nums, int target) {
            int i = 0, len = nums.length;
            while(i < len && nums[i] == nums[len-1]) i = (i+1) << 1;
            if(i >= len) {
                // 顺序查找
                for(i = 0; i < len; ++i) {
                    if(nums[i] == target) return true;
                }
                return false;
            }else if(i == 0){
                return search0(nums, 0, len-1, target);
            }else {
                if(nums[i] < nums[len-1] && nums[i] > target) {
                    // 这种情况,搜索左边区间即可
                    return search0(nums, (i>>1)-1, i, target);
                }
                if(nums[i] > nums[len-1] && nums[i] < target) {
                    // 这种情况,搜索右边区间即可
                    return search0(nums, i, len-1, target);
                }
                return search0(nums, i, len-1, target)
                        || search0(nums, (i>>1)-1, i, target);
            }
        }
        private boolean search0(int[] nums, int floor, int ceil, int target) {
            int low = floor, high = ceil, mid;
            while(low <= high){
                mid = (low+high) >>> 1;
                if(nums[mid] == target) return true;
                if(nums[mid] < target){
                    // 中值在右区间,目标值在左区间,需要左移
                    if(nums[low] > nums[mid] && nums[low] <= target) high = mid-1;
                    // 正常情况下的右移
                    else low = mid+1;
                }else{
                    // 中值在左区间,目标值在右区间,需要右移
                    if(nums[low] <= nums[mid] && nums[low] > target) low = mid+1;
                    // 正常情况下的左移
                    else high = mid-1;
                }
            }
            return false;
        }
    }
    

    相关文章

      网友评论

          本文标题:搜索旋转排序数组

          本文链接:https://www.haomeiwen.com/subject/mbsonqtx.html