搜索旋轉排序數組

一、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;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章