二分查找模板 和 一些實際題目


模板:

class Solution:
    # @param nums: The integer array
    # @param target: Target number to find
    # @return the first position of target in nums, position start from 0 
    def binarySearch(self, nums, target):
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        # 用 start + 1 < end 而不是 start < end 的目的是為了避免死循環
        # 在 first position of target 的情況下不會出現死循環
        # 但是在 last position of target 的情況下會出現死循環
        # 樣例:nums=[1,1] target = 1
        # 為了統一模板,我們就都采用 start + 1 < end,就保證不會出現死循環
        while start + 1 < end:
            # python 沒有 overflow 的問題,直接 // 2 就可以了
            # java和C++ 最好寫成 mid = start + (end - start) / 2
            # 防止在 start = 2^31 - 1, end = 2^31 - 1 的情況下出現加法 overflow
            mid = (start + end) // 2
            
            # > , =, < 的邏輯先分開寫,然后在看看 = 的情況是否能合並到其他分支里
            if nums[mid] < target:
                # 寫作 start = mid + 1 也是正確的
                # 只是可以偷懶不寫,因為不寫也沒問題,不會影響時間復雜度
                # 不寫的好處是,萬一你不小心寫成了 mid - 1 你就錯了
                start = mid
            elif nums[mid] == target:
                end = mid
            else: 
                # 寫作 end = mid - 1 也是正確的
                # 只是可以偷懶不寫,因為不寫也沒問題,不會影響時間復雜度
                # 不寫的好處是,萬一你不小心寫成了 mid + 1 你就錯了
                end = mid
                
        # 因為上面的循環退出條件是 start + 1 < end
        # 因此這里循環結束的時候,start 和 end 的關系是相鄰關系(1和2,3和4這種)
        # 因此需要再單獨判斷 start 和 end 這兩個數誰是我們要的答案
        # 如果是找 first position of target 就先看 start,否則就先看 end
        if nums[start] == target:
            return start
        if nums[end] == target:
            return end
        
        return -1

 

如果是涉及極大值的求解,則是利用nums[mid] nums[mid+1]求解,如下例,注意mid+1是有效的index。

 

75. 尋找峰值

中文
English

你給出一個整數數組(size為n),其具有以下特點:

  • 相鄰位置的數字是不同的
  • A[0] < A[1] 並且 A[n - 2] > A[n - 1]

假定P是峰值的位置則滿足A[P] > A[P-1]A[P] > A[P+1],返回數組中任意一個峰值的位置。

樣例

樣例 1:
	輸入:  [1, 2, 1, 3, 4, 5, 7, 6]
	輸出:  1 or 6
	
	解釋:
	返回峰頂元素的下標


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

挑戰

Time complexity O(logN)

注意事項

  • 數組保證至少存在一個峰
  • 如果數組存在多個峰,返回其中任意一個就行
  • 數組至少包含 3 個數
class Solution:
    """
    @param A: An integers array.
    @return: return any of peek positions.
    """
    def findPeak(self, A):
        # write your code here
        start, end = 0, len(A)-1
        while start+1 < end:
            mid = (start+end)//2
            if A[mid] < A[mid+1]:
                start = mid
            else:
                end = mid
        if A[start] > A[end]:
            return start
        else:
            return end

 

 

457. 經典二分查找問題

中文
English

在一個排序數組中找一個數,返回該數出現的任意位置,如果不存在,返回 -1

樣例

樣例 1:

輸入:nums = [1,2,2,4,5,5], target = 2
輸出:1 或者 2

樣例 2:

輸入:nums = [1,2,2,4,5,5], target = 6
輸出:-1
class Solution:
    """
    @param nums: An integer array sorted in ascending order
    @param target: An integer
    @return: An integer
    """
    def findPosition(self, nums, target):
        # write your code here
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if nums[mid] < target:
                start = mid
            elif nums[mid] == target:
                end = mid
            else: 
                end = mid
                
        if nums[start] == target:
            return start
        if nums[end] == target:
            return end
        
        return -1

 

458. 目標最后位置

中文
English

給一個升序數組,找到 target 最后一次出現的位置,如果沒出現過返回 -1

樣例

樣例 1:

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

樣例 2:

輸入:nums = [1,2,2,4,5,5], target = 6
輸出:-1
class Solution:
    """
    @param nums: An integer array sorted in ascending order
    @param target: An integer
    @return: An integer
    """
    def lastPosition(self, nums, target):
        # write your code here
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if nums[mid] < target:
                start = mid
            elif nums[mid] == target:
                start = mid
            else: 
                end = mid

        if nums[end] == target:
            return end                
        if nums[start] == target:
            return start
        
        return -1        

 

14. 二分查找

中文
English

給定一個排序的整數數組(升序)和一個要查找的整數target,用O(logn)的時間查找到target第一次出現的下標(從0開始),如果target不存在於數組中,返回-1

樣例

樣例  1:
	輸入:[1,4,4,5,7,7,8,9,9,10],1
	輸出: 0
	
	樣例解釋: 
	第一次出現在第0個位置。

樣例 2:
	輸入: [1, 2, 3, 3, 4, 5, 10],3
	輸出: 2
	
	樣例解釋: 
	第一次出現在第2個位置
	
樣例 3:
	輸入: [1, 2, 3, 3, 4, 5, 10],6
	輸出: -1
	
	樣例解釋: 
	沒有出現過6, 返回-1
class Solution:
    """
    @param nums: The integer array.
    @param target: Target to find.
    @return: The first position of target. Position starts from 0.
    """
    def binarySearch(self, nums, target):
        # write your code here
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if nums[mid] < target:
                start = mid
            elif nums[mid] == target:
                end = mid
            else: 
                end = mid

        if nums[start] == target:
            return start
        if nums[end] == target:
            return end 
        
        return -1  

 

74. 第一個錯誤的代碼版本

中文
English

代碼庫的版本號是從 1 到 n 的整數。某一天,有人提交了錯誤版本的代碼,因此造成自身及之后版本的代碼在單元測試中均出錯。請找出第一個錯誤的版本號。

你可以通過 isBadVersion 的接口來判斷版本號 version 是否在單元測試中出錯,具體接口詳情和調用方法請見代碼的注釋部分。

樣例

n = 5:

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

因此可以確定第四個版本是第一個錯誤版本。

挑戰

調用 isBadVersion 的次數越少越好

注意事項

請閱讀代碼編輯框內注釋代碼,獲取對於不同語言正確調用 isBadVersion 的方法,比如java的調用方式是SVNRepo.isBadVersion(v)

#class SVNRepo:
#    @classmethod
#    def isBadVersion(cls, id)
#        # Run unit tests to check whether verison `id` is a bad version
#        # return true if unit tests passed else false.
# You can use SVNRepo.isBadVersion(10) to check whether version 10 is a 
# bad version.
class Solution:
    """
    @param n: An integer
    @return: An integer which is the first bad version.
    """
    def findFirstBadVersion(self, n):
        # write your code here
            
        start, end = 1, n
        while start + 1 < end:
            mid = (start + end) // 2
            if SVNRepo.isBadVersion(mid):
                end = mid
            else: 
                start = mid

        if SVNRepo.isBadVersion(start):
            return start
        if SVNRepo.isBadVersion(end):
            return end 
        
        return -1  

 

460. 在排序數組中找最接近的K個數

中文
English

給一個目標數 target, 一個非負整數 k, 一個按照升序排列的數組 A。在A中找與target最接近的k個整數。返回這k個數並按照與target的接近程度從小到大排序,如果接近程度相當,那么小的數排在前面。

樣例

樣例 1:

輸入: A = [1, 2, 3], target = 2, k = 3
輸出: [2, 1, 3]

樣例 2:

輸入: A = [1, 4, 6, 8], target = 3, k = 3
輸出: [4, 1, 6]

挑戰

O(logn + k) 的時間復雜度

class Solution:
    """
    @param A: an integer array
    @param target: An integer
    @param k: An integer
    @return: an integer array
    """
    def kClosestNumbers(self, A, target, k):
        # write your code here
        if k <= 0:
            return []
            
        nearest_index = self.find_nearest(A, target)
        return self.find_k_elements(A, target, k, nearest_index)
    
    
    def find_nearest(self, nums, target):
        if not nums:
            return -1
            
        start, end = 0, len(nums) - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if nums[mid] < target:
                start = mid
            elif nums[mid] == target:
                end = mid
            else: 
                end = mid
        
        if (end >= len(nums)) or \
            ((start >= 0) and \
            abs(nums[start]-target) <= abs(nums[end]-target)):
            return start
        return end 
    
    
    def find_k_elements(self, A, target, k, nearest_index):
        result = [A[nearest_index]]
        i, j = nearest_index-1, nearest_index+1
        while (i >= 0 or j < len(A)) and len(result) < k:
            if j >= len(A) or (i >= 0 and \
                abs(A[i]-target) <= abs(A[j]-target)):
                result.append(A[i])
                i -= 1
            else:
                result.append(A[j])
                j += 1
        return result
            

 采用的是二分法 + 雙指針
二分法確定一個位置,左側是 < target,右側是 >= target
然后用兩根指針從中間向兩邊走,依次找到最接近的 k 個數

參考代碼:

class Solution:
    """
    @param A: an integer array
    @param target: An integer
    @param k: An integer
    @return: an integer array
    """
    def kClosestNumbers(self, A, target, k):
        # 找到 A[left] < target, A[right] >= target
        # 也就是最接近 target 的兩個數,他們肯定是相鄰的
        right = self.find_upper_closest(A, target)
        left = right - 1
    
        # 兩根指針從中間往兩邊擴展,依次找到最接近的 k 個數
        results = []
        for _ in range(k):
            if self.is_left_closer(A, target, left, right):
                results.append(A[left])
                left -= 1
            else:
                results.append(A[right])
                right += 1
        
        return results
    
    def find_upper_closest(self, A, target):
        # find the first number >= target in A
        start, end = 0, len(A) - 1
        while start + 1 < end:
            mid = (start + end) // 2
            if A[mid] >= target:
                end = mid
            else:
                start = mid
        
        if A[start] >= target:
            return start
        
        if A[end] >= target:            
            return end
        
        # 找不到的情況
        return end + 1
        
    def is_left_closer(self, A, target, left, right):
        if left < 0:
            return False
        if right >= len(A):
            return True
        return target - A[left] <= A[right] - target

 

447. 在大數組中查找

中文
English

給一個按照升序排序的非負整數數組。這個數組很大以至於你只能通過固定的接口 ArrayReader.get(k) 來訪問第k個數(或者C++里是ArrayReader->get(k)),並且你也沒有辦法得知這個數組有多大。

找到給出的整數target第一次出現的位置。你的算法需要在O(logk)的時間復雜度內完成,k為target第一次出現的位置的下標。

如果找不到target,返回-1。

樣例

樣例 1:

輸入: [1, 3, 6, 9, 21, ...], target = 3
輸出: 1

樣例 2:

輸入: [1, 3, 6, 9, 21, ...], target = 4
輸出: -1

挑戰

O(logn)的時間復雜度,n是target第一次出現的下標。

"""
Definition of ArrayReader
class ArrayReader(object):
    def get(self, index):
    	# return the number on given index, 
        # return 2147483647 if the index is invalid.
"""
class Solution:
    """
    @param: reader: An instance of ArrayReader.
    @param: target: An integer
    @return: An integer which is the first index of target.
    """
    def searchBigSortedArray(self, reader, target):
        # write your code here
        end = self.find_first_greater_index(reader, target)
        return self.bin_search(reader, target, end)
    
    
    def find_first_greater_index(self, reader, target):
        i = 1
        while reader.get(i) < target:
            i = i*2
        return i
    
    
    def bin_search(self, reader, target, end):
        start = 0
        while start + 1 < end:
            mid = (start + end) // 2
            if reader.get(mid) < target:
                start = mid
            elif reader.get(mid) == target:
                end = mid
            else: 
                end = mid
        if reader.get(start) == target:
            return start
        if reader.get(end) == target:
            return end
        return -1

 精簡下代碼:

class Solution:
    # @param {ArrayReader} reader: An instance of ArrayReader
    # @param {int} target an integer
    # @return {int} an integer
    def searchBigSortedArray(self, reader, target):
        # write your code here
        index = 0
        while reader.get(index) < target:
            index = index * 2 + 1
        start, end = 0, index
        while start + 1 < end:
            mid = start + (end - start) // 2
            if reader.get(mid) < target:
                start = mid
            else:
                end = mid
        if reader.get(start) == target:
            return start
        if reader.get(end) == target:
            return end
        return -1

 

 

140. 快速冪

中文
English

計算an % b其中a,b和n都是32位的非負整數。

樣例

例如 231 % 3 = 2

例如 1001000 % 1000 = 0

挑戰

O(logn)

class Solution:
    """
    @param a: A 32bit integer
    @param b: A 32bit integer
    @param n: A 32bit integer
    @return: An integer
    """
    def fastPower(self, a, b, n):
        # write your code here
        if n == 0:
            return 1%b
        if n == 1:
            return a%b
            
        if n & 1 == 0: # even
            mod = self.fastPower(a, b, n//2)
            return mod*mod%b
        else:
            mod = self.fastPower(a, b, n//2)
            return mod*mod*a%b

 迭代解法:

class Solution:
    """
    @param a, b, n: 32bit integers
    @return: An integer
    """
    def fastPower(self, a, b, n):
        # a ^ n % b
        # 比如 n=5,可以看做 a^(101)2 % b (5的二進制是101)
        # 拆開也就是 [a^(100)2 * a&(1)2] % b
        # 因此相當於我們把 n 做二進制轉換,碰到 1 的時候,稱一下對應的 a 的冪次
        # 而 a 的冪次我們只需要知道 a^1, a^(10)2, a^(100)2 ... 也就是 a^1, a^2, a^4 ...
        # 因此不斷的把 a = a * a 就可以了
        # 中間計算的時候,隨時可以 % b 避免 overflow 其不影響結果,這是 % 運算的特性。
        ans = 1
        while n > 0:
            if n % 2 == 1:
                ans = (ans * a) % b
            a = a * a % b
            n = n // 2
        return ans % b

 

428. x的n次冪

中文
English

實現 pow(x, n). (n是一個整數)

樣例

樣例 1:

輸入: x = 9.88023, n = 3
輸出: 964.498

樣例 2:

輸入: x = 2.1, n = 3
輸出: 9.261

樣例 3:

輸入: x = 1, n = 0
輸出: 1

挑戰

時間復雜度O(logn)

注意事項

不用擔心精度,當答案和標准輸出差絕對值小於1e-3時都算正確

class Solution:
    """
    @param x {float}: the base number
    @param n {int}: the power number
    @return {float}: the result
    """
    def myPow(self, x, n):
        # write your code here
        if n == 0:
            return 1
        if n < 0:
            return 1/self.myPow(x, -n)
        
        if n & 1 == 0:
            power = self.myPow(x, n//2)
            return power*power
        else:
            power = self.myPow(x, n//2)
            return power*power*x

 




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM