二分查找
給定一個 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]