LeetCode--二分法(python)

二分查找

給定一個 n 個元素有序的(升序)整型數組 nums 和一個目標值 target ,寫一個函數搜索 nums 中的 target,如果目標值存在返回下標,否則返回 -1。

示例 1:

輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中並且下標爲 4
示例 2:

輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1

提示:
你可以假設 nums 中的所有元素是不重複的。
n 將在 [1, 10000]之間。
nums 的每個元素都將在 [-9999, 9999]之間。

解法
最經典的二分查找題

class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        if not nums:
            return -1
        l = 0
        r = len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                return mid
            elif nums[mid] < target:
                l = mid + 1
            else:
                r = mid - 1
                
        return -1
        

x 的平方根

實現 int sqrt(int x) 函數。

計算並返回 x 的平方根,其中 x 是非負整數。

由於返回類型是整數,結果只保留整數的部分,小數部分將被捨去。

示例 1:
輸入: 4
輸出: 2

示例 2:
輸入: 8
輸出: 2
說明: 8 的平方根是 2.82842…, 由於返回類型是整數,小數部分將被捨去。

解法
對經典二分法的簡單變種,注意處理0,1情況即可。

class Solution(object):
    def mySqrt(self, x):
        """
        :type x: int
        :rtype: int
        """
        if x in [0, 1]:
            return x
        l, r = 1, x // 2
        while l <= r:
            mid = (l + r) // 2
            if mid * mid == x:
                return mid
            elif mid * mid > x:
                r = mid - 1
            elif (mid + 1) * (mid + 1) > x:
                return mid
            else:
                l = mid + 1

猜數字大小

我們正在玩一個猜數字遊戲。 遊戲規則如下:
我從 1 到 n 選擇一個數字。 你需要猜我選擇了哪個數字。
每次你猜錯了,我會告訴你這個數字是大了還是小了。
你調用一個預先定義好的接口 guess(int num),它會返回 3 個可能的結果(-1,1 或 0):

-1 : 我的數字比較小
1 : 我的數字比較大
0 : 恭喜!你猜對了!

示例 :
輸入: n = 10, pick = 6
輸出: 6

# The guess API is already defined for you.
# @param num, your guess
# @return -1 if my number is lower, 1 if my number is higher, otherwise return 0
# def guess(num):

class Solution(object):
    def guessNumber(self, n):
        """
        :type n: int
        :rtype: int
        """
        l, r = 1, n
        while l <= r:
            mid = (l + r) // 2
            if guess(mid) == 0:
                return mid
            elif guess(mid) == -1:
                r = mid - 1
            else:
                l = mid + 1
      

搜索旋轉排序數組

假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組 [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

解法
題意:在兩段遞增序列上找目標值
步驟:

  • 複雜度要求O(log n),所以只能是二分法。用兩次二分法分別找數組的旋轉開始位置和target的位置。
  • 因爲數組內沒有重複值,所以只要發生了旋轉,數組左邊的值一定大於右邊的,所以可以判斷nums[mid]和nums[r]。
  • 在找target的時候,如果找到了,返回的是在原數組上的索引,所以要讓(mid + pt) % len
class Solution(object):
    def search(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: int
        """
        pt = len(nums) 
        l, r = 0, len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] < nums[mid - 1]:
                pt = mid
                break
            elif nums[mid] > nums[r]:
                l = mid + 1
            else:
                r = mid - 1
                
        nums = nums[pt:] + nums[:pt]
        #print(pt, nums)
        
        l, r = 0, len(nums) - 1
        while l <= r:
            mid = (l + r) // 2
            if nums[mid] == target:
                return (mid + pt) % len(nums)
            elif target > nums[mid]:
                l = mid + 1
            else:
                r = mid - 1
                
        return -1

第一個錯誤的版本

你是產品經理,目前正在帶領一個團隊開發新的產品。不幸的是,你的產品的最新版本沒有通過質量檢測。由於每個版本都是基於之前的版本開發的,所以錯誤的版本之後的所有版本都是錯的。

假設你有 n 個版本 [1, 2, …, n],你想找出導致之後所有版本出錯的第一個錯誤的版本。

你可以通過調用 bool isBadVersion(version) 接口來判斷版本號 version 是否在單元測試中出錯。實現一個函數來查找第一個錯誤的版本。你應該儘量減少對調用 API 的次數。

示例:

給定 n = 5,並且 version = 4 是第一個錯誤的版本。

調用 isBadVersion(3) -> false
調用 isBadVersion(5) -> true
調用 isBadVersion(4) -> true

所以,4 是第一個錯誤的版本。

解法
題意:找到第一個返回爲True的索引
步驟:首先想到用二分法,該題的查找條件需要訪問mid的直接右鄰居,所以使用第二種二分查找模板,並需要進行後處理。
初始條件:left = 0, right = length
終止:left == right
向左查找:right = mid
向右查找:left = mid+1

# The isBadVersion API is already defined for you.
# @param version, an integer
# @return a bool
# def isBadVersion(version):

class Solution(object):
    def firstBadVersion(self, n):
        """
        :type n: int
        :rtype: int
        """
        l, r = 1, n
        while l < r:
            mid = (l + r) // 2
            if isBadVersion(mid) == False:
                l = mid + 1
            else:
                r = mid
                
        return l

尋找峯值

峯值元素是指其值大於左右相鄰值的元素。
給定一個輸入數組 nums,其中 nums[i] ≠ nums[i+1],找到峯值元素並返回其索引。
數組可能包含多個峯值,在這種情況下,返回任何一個峯值所在位置即可。
你可以假設 nums[-1] = nums[n] = -∞。

示例 1:

輸入: nums = [1,2,3,1]
輸出: 2
解釋: 3 是峯值元素,你的函數應該返回其索引 2。
示例 2:

輸入: nums = [1,2,1,3,5,6,4]
輸出: 1 或 5
解釋: 你的函數可以返回索引 1,其峯值元素爲 2;
或者返回索引 5, 其峯值元素爲 6。

說明:
你的解法應該是 O(logN) 時間複雜度的。

解法
同上題思路相同。雖然這個數組沒有排序,但是因爲左右端點都是負無窮,所以有個大致的順序:從小變大再變小。所以可以用二分法找。

class Solution(object):
    def findPeakElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        
        l = 0
        r = len(nums) -1
        while l < r:
            mid = (l + r) // 2
            
            if nums[mid] <= nums[mid + 1]:
                l = mid + 1
            else:
                r = mid
                
        return l
        

尋找旋轉排序數組中的最小值

假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組 [0,1,2,4,5,6,7] 可能變爲 [4,5,6,7,0,1,2] )。
請找出其中最小的元素。
你可以假設數組中不存在重複元素。

示例 1:
輸入: [3,4,5,1,2]
輸出: 1

示例 2:
輸入: [4,5,6,7,0,1,2]
輸出: 0

解法
同上

class Solution(object):
    def findMin(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        l, r = 0, len(nums) - 1
        while l < r:
            mid = (l + r) // 2
            if nums[mid] > nums[r]:
                l = mid + 1
            else:
                r = mid
                
        return nums[l]
                
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章