Skip to content

LeetCode: 81-Search in Rotated Sorted Array II 解題紀錄

Last Updated on 2022-03-28 by Clay

題目

There is an integer array nums sorted in non-decreasing order (not necessarily with distinct values).

Before being passed to your function, nums is rotated at an unknown pivot index k (0 <= k < nums.length) such that the resulting array is [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]] (0-indexed). For example, [0,1,2,4,4,4,5,6,6,7] might be rotated at pivot index 5 and become [4,5,6,6,7,0,1,2,4,4].

Given the array nums after the rotation and an integer target, return true if target is in nums, or false if it is not in nums.

You must decrease the overall operation steps as much as possible.

Example 1:

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

Example 2:

Input: nums = [2,5,6,0,0,1,2], target = 3
Output: false

Constraints:

  • 1 <= nums.length <= 5000
  • -104 <= nums[i] <= 104
  • nums is guaranteed to be rotated at some pivot.
  • -104 <= target <= 104

Follow up: This problem is similar to Search in Rotated Sorted Array, but nums may contain duplicates. Would this affect the runtime complexity? How and why?

題目給定一純數值陣列,本來陣列應是由小到大排序,但會在某個特定樞點pivot)將後續的所有數值列表嫁接到前排。比方說原本的陣列 [1, 2, 3, 4] 可能會在 index=2 時為樞點,讓陣列變成 [3, 4, 1, 2]

另外我們還會得到一個目標值 target。如果目標值存在於陣列,回傳 true,反之回傳 false


解題思路

這題可以很單純地使用遍歷暴力法(也可以利用排序特性做點優化)、也可以用二元搜索binary search)來減低複雜度。

不過或許是我有沒考慮到的地方,我在使用優化過的遍歷搜尋上比二元搜索來得效率更好。明明時間複雜度應該是二元搜索佔優才是。


Brute Force 解法

首先,我們可以理所當然地使用遍歷來搜索目標值 target。不過若當前搜索的值大於 target、且前一個值小於 target,則代表 target 一定不存在於這個陣列,可以直接回傳 false

要注意的是反過來則不一定:如果當前值小於 target 且前一個值大於 target,有可能只是碰上 pivot 轉換的點。


Brute Force 複雜度

因為理論上我們需要遍歷列表,故時間複雜度為 O(n);不需要儲存儲了讀取索引以外的值,故空間複雜度為 O(1)。

Time complexityO(n)
Space complexityO(1)


Brute Force 解法 C++ 範例程式碼

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        for (int i=0; i<nums.size(); ++i) {
            if (nums[i] == target) return true;
            
            if (i >= 1 && nums[i-1] < target && nums[i] > target) {
                return false;
            }
        }
        
        return false;
    }
};



Brute Force 解法 Python 範例程式碼

        for i in range(len(nums)):
            if nums[i] == target: 
                return True
            
            if i >= 1 and nums[i-1] < target and nums[i] > target:
                break
        
        return False



二元搜索法

跟通常性的二元搜索法相比,我們需要注意(以下用 left 當作左側邊界值、right 當作右側邊界值、mid 當作中間值):

  • 如果 left == mid、且 right == midleft 需要加一、right 需要減一(這是因為列表中可能存在相同的值,而我們在找不到目標值時不需要考慮相同值
  • 如果 left <= mid 則代表此段仍在遞增中,如果 left < target < mid,則可以將 right 賦值成 mid-1,以此縮小搜尋 target 的範圍(反之則 left = mid + 1
  • 如果 left > mid 則代表此處是遞減段落,如果 mid < target < right,這次則將 left = mid + 1 來縮小搜尋範圍(反之則 right = mid – 1

如果到最後 left > right 我們都還沒在搜尋邊界中找到目標值,則代表目標值不存在於陣列中。


二元搜索複雜度

Time complexityO(log n)
Space complexityO(1)


二元搜索解法 C++ 範例程式碼

class Solution {
public:
    bool search(vector<int>& nums, int target) {
        // Init
        int left = 0;
        int right = nums.size() - 1;
        
        // Finding
        while (left <= right) {
            int mid = left + (right-left) / 2;
            if (nums[mid] == target) return true;
            
            if ((nums[left] == nums[mid]) && (nums[right] == nums[mid])) {
                ++left;
                --right;
            }
            else if (nums[left] <= nums[mid]) {
                if ((nums[left] <= target) && (nums[mid] > target)) {
                    right = mid - 1;
                }
                else {
                    left = mid + 1;
                }
            }
            else {
                if ((nums[right] >= target) && (nums[mid] < target)) {
                    left = mid + 1;
                }
                else {
                    right = mid - 1;
                }
            }
        }
        
        return false;
    }
};



二元搜索解法 Python 範例程式碼

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        # Init
        left = 0
        right = len(nums) - 1
        
        # Finding
        while left <= right:
            mid = left + (right-left) // 2
            
            # If hit the target
            if nums[mid] == target: 
                return True
            
            if nums[left] == nums[mid] and nums[right] == nums[mid]:
                left += 1
                right -= 1
            elif nums[left] <= nums[mid]:
                if nums[left] <= target and nums[mid] > target:
                    right = mid - 1
                else:
                    left = mid + 1
            else:
                if nums[right] >= target and nums[mid] < target:
                    left = mid + 1
                else:
                    right = mid - 1

        return False

References


Read More

Leave a Reply