1. 程式人生 > 其它 >【二分查詢】力扣81:搜尋旋轉排序陣列 II

【二分查詢】力扣81:搜尋旋轉排序陣列 II

已知存在一個按非降序排列的整數陣列 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,4,4,5,6,6,7] 在下標 5 處經旋轉後可能變為 [4,5,6,6,7,0,1,2,4,4] 。
給你 旋轉後 的陣列 nums 和一個整數 target ,請你編寫一個函式來判斷給定的目標值是否存在於陣列中。如果 nums 中存在這個目標值 target ,則返回 true ,否則返回 false 。
你必須儘可能減少整個操作步驟。

進階:這是 搜尋旋轉排序陣列 的延伸題目,本題中的 nums 可能包含重複元素。這會影響到程式的時間複雜度嗎?會有怎樣的影響,為什麼?

示例:

輸入:nums = [2,5,6,0,0,1,2], target = 0
輸出:true

將陣列一分為二,其中一定有一個是有序的,另一個可能是有序,也能是部分有序。此時有序部分用二分法查詢。無序部分再一分為二,其中一個一定有序,另一個可能有序,可能無序。就這樣迴圈.

即使陣列被旋轉過,我們仍然可以利用這個陣列的遞增性,使用二分查詢。對於當前的中點,如果它指向的值小於等於右端,那麼說明右區間是排好序的;反之,那麼說明左區間是排好序的。如果目標值位於排好序的區間內,我們可以對這個區間繼續二分查詢;反之,我們對於另一半區間繼續二分查詢。
注意:

因為陣列存在重複數字,如果中點和左端的數字相同,我們並不能確定是左區間全部相同,還是右區間完全相同。在這種情況下,我們可以簡單地將左端點右移一位,然後繼續進行二分查詢

class Solution:
    def search(self, nums: List[int], target: int) -> bool:
        n = len(nums)
        low, high = 0, n - 1 
        while low <= high:
            mid = (low + high) // 2
            if nums[mid] == target: # 只有此時才True
                return True
            # 如果中點和左端的數字相同,則不能確定是左區間全部相同,還是右區間完全相同。在這種情況下,可以簡單地將左端點右移一位,然後繼續進行二分查詢
            elif nums[mid] == nums[low]: 
                low += 1
            elif nums[mid] <= nums[high]: # 右區間是升序
                if nums[mid] < target and nums[high] >= target: # 如果在右區間範圍內就在右區間繼續二分查詢
                    low = mid + 1
                else: # 如果不在就去左區間繼續找
                    high = mid - 1 
            else: # 左區間升序
                if nums[mid] > target and nums[low] <= target:
                    high = mid - 1
                else:
                    low = mid + 1
        return False

力扣81基於力扣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 。
示例:

輸入:nums = [4,5,6,7,0,1,2], target = 0
輸出:4

法1:
有了前面的思路,其實只需要把 return True改為return mid返回target的下標,False改為-1,即可。

法2比較有意思:
對於旋轉陣列 nums = [4,5,6,7,0,1,2]
首先根據 nums[0] 與 target 的關係判斷 target 是在左段還是右段。
例如 target = 5, 目標值在左半段,因此在 [4, 5, 6, 7, inf, inf, inf] 這個有序數組裡找就行了;
例如 target = 1, 目標值在右半段,因此在 [-inf, -inf, -inf, -inf, 0, 1, 2] 這個有序數組裡找就行了。
如此,我們又雙叒叕將「旋轉陣列中找目標值」 轉化成了「有序陣列中找目標值」
Java 程式碼

class Solution {
    public int search(int[] nums, int target) {
        int lo = 0, hi = nums.length - 1;
        while (lo <= hi) {
            int mid = lo + (hi - lo) / 2;
            if (nums[mid] == target) {
                return mid;
            }
            
            // 先根據 nums[0] 與 target 的關係判斷目標值是在左半段還是右半段
            if (target >= nums[0]) {
                // 目標值在左半段時,若 mid 在右半段,則將 mid 索引的值改成 inf
                if (nums[mid] < nums[0]) {
                    nums[mid] = Integer.MAX_VALUE;
                }
            } else {
                // 目標值在右半段時,若 mid 在左半段,則將 mid 索引的值改成 -inf
                if (nums[mid] >= nums[0]) {
                    nums[mid] = Integer.MIN_VALUE;
                }
            }

            if (nums[mid] < target) {
                lo = mid + 1;
            } else {
                hi = mid - 1;
            }
        }
        return -1;
    }
}

作者:sweetiee
連結:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/duo-si-lu-wan-quan-gong-lue-bi-xu-miao-dong-by-swe/

法3:(暴力解法)一行程式碼
參透本質,可以看出如果target不在nums裡肯定返回的是-1。在的話返回的就是它的下標(而且nums裡的值是互不相同的)。Python說:返回下標我熟呀,所以有nums.index(target)。結束。

作者:NfEOaSTGYy
連結:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/by-nfeoastgyy-kagi/

class Solution:
    def search(self, nums: List[int], target: int) -> int:
        return nums.index(target) if target in nums else -1

作者:zhsama
連結:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/pythonyi-xing-jie-jue-by-zhsama-c98f/