合縱連橫: 二分搜尋法框架_理解背後的本質

閱讀時間約 16 分鐘


這篇文章,會帶著大家複習以前學過的二分搜尋法(Binary Search)框架
並且以二分搜尋法的概念為核心,

貫穿一些相關聯的題目,透過框架複現來幫助讀者理解這個實用的演算法框架。


Binary search 二分搜尋法框架

用途:

已經排序好的數列中尋找目標值

單調遞增、或單調遞減f(x)尋找滿足某個條件目標值

在具有局部最大值,或局部最小值的f(x)中尋找最大值(高峰)、最小值(低谷)。

def search(arr, target):

left, right = 0, len(right):

while left <= right:

# 使用比較安全的計算方式,避免整數溢位 avoid integer overflow​
# 使用C/C++, Java...等語言的讀者請留意
mid = left + (right - left )// 2

# 假如找到目標值,或者滿足某個特定條件,代表成功
if arr[mid] == target:
return mid

elif arr[mid] > target:
#​ 目前的值還太大,下一輪只搜尋左半部。
right = mid - 1

else:
# 目前的值還太小,下一輪只搜尋右半部​
left = mid + 1

return -1

接下來,我們會用這個上面這種結構,貫穿一些同類型,有關聯的題目
(請讀者、或觀眾留意上面的這種 二分搜尋 框架,之後的解說會反覆出現)

剛好以前有錄過其中一題範例Guess Number的教學影片,提供給讀者參考。


Binary search 二分搜尋法 (最普通的應用,也是常見的課本範例)

在一個已經排序好的陣列nums中,尋找目標值target


二分搜尋 框架來解,就變成下面這個樣子

 Solution:
def search(self, nums: List[int], target: int) -> int:

left, right = 0,len(nums)-1

while left <= right:

if not (nums[left] <= target <= nums[right]):
# optimization, early stop unnecessary branch
return-1

mid = left + ( right - left ) // 2

if( nums[mid] == target):
return mid

elif nums[mid] < target:
left = mid+1

else:
right = mid-1

return -1

好,接下來,來看一道相關的變化題

在平移過,已排序的陣列搜尋目標值

假如這個陣列是排序好的,但是被平移過,還可以使用二分搜尋嗎?

其實可以喔,只要多加一個判斷:
目前是左邊已排序而且趨勢連續,還是右邊已排序而且趨勢連續?


假如左邊已排序而且趨勢連續,看看target有沒有落在左邊的範圍,如果有,下次就搜尋左半部;如果沒有,下次就搜尋右半部。

同樣的,另一邊也是對稱的形式。

假如右邊已排序而且趨勢連續,看看target有沒有落在右邊的範圍,如果有,下次就搜尋右半部;如果沒有,下次就搜尋左半部。


示意圖

image

image


二分搜尋 框架來解,就變成下面這個樣子

class Solution:
def search(self, nums: List[int], target: int) -> int:

def helper_binary_search( nums, start, end, target):

while start <= end:


mid = start + (end-start)//2

if nums[mid] == target:
# base case:
# hit
return mid

# Divide-and-conquer
if nums[mid] < nums[end]:
# right half keeps sorted in ascending order after rotation


if nums[end] >= target > nums[mid]:


if not (nums[mid] <= target <= nums[end]):
return -1

# search target in right half
start = mid+1

else:
# search target in left half
end = mid-1

else:
# left half keeps sorted in ascending order after rotation

if nums[start] <= target < nums[mid]:

if not (nums[start] <= target <= nums[mid]):
return -1

# search target in left half
end = mid-1

else:
# search target in right half
start = mid+1

# base case:
# miss
# target does not exist in the list
return -1

# ============================================
return helper_binary_search(nums, 0, len(nums)-1, target)

接下來再看一道常見的變形題

找目標值第一次出現和最後一次出現的位置

在已排序的陣列中,找目標值第一次出現最後一次出現索引位置


怎麼做呢? 其實很直覺!

第一次出現的索引,就是一直往左邊找目標值,直到找不到為止

另一邊,也是對稱的

最後一次出現的索引,就是一直往右邊找目標值,直到找不到為止


二分搜尋 框架來解,就變成下面這個樣子

class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:

# ------------------------------------

def find_first():

left, right = 0, len(nums)-1

index = -1

while left <= right:

mid = left + (right - left)//2

if nums[mid] == target:
index = mid
right = mid - 1

elif nums[mid] < target:
left = mid + 1

else:
right = mid - 1

return index

# ------------------------------------

def find_last():

left, right = 0, len(nums)-1

index = -1

while left <= right:

mid = left + (right - left)//2

if nums[mid] == target:
index = mid
left = mid + 1

elif nums[mid] < target:
left = mid + 1

else:
right = mid - 1

return index

# ------------------------------------

first_idx, last_idx = find_first(), find_last()
return [first_idx, last_idx]


順便介紹,如果用Python 原生內建的bisect二分模組也可以。


示意圖

image

image

使用Python原生內建的bisect模組:

from bisect import bisect_left, bisect_right

class Solution:
def searchRange(self, nums: List[int], target: int) -> List[int]:

# first index is the insertion index on the left
first = bisect_left( nums, target )

# last index is the insertion index on the right - 1
last = bisect_right( nums, target )-1

if first > last:
# target does not exist in nums
return [-1, -1]

else:
# target exist, report indices
return [ first, last ]


除了傳統的尋找目標值以外,還可以解一些具有已排序性質函數的題目喔!

例如 開根號這題。

計算某個整數x的平方根的整數部分


題目開門見山,要求我們計算某個整數x的平方根的整數部分
而且不允許使用內建的sqrt()或者次方計算符號


那麼除了暴力搜索、數值近似的牛頓法,還有沒有別的方法?

其實開根號這題也可以用 二分搜尋法 來解喔

為什麼?

因為y = 根號 x = x ^ (0.5) 本身就是一個嚴格遞增的函數(相當於已排序),如下圖

raw-image


因此,搜索的初始化區間就變成了[0, x]

目標值就用二分搜尋法來找,每次取中央點mid來測試,是否為x的平方根的整數部分。


假如mid * mid <= x,
那表示還可以往上搜索,看看能不能離真正的平方根更靠近一點。

假如mid*mid > x,
則表示mid太大,下一回合只要往下搜索即可。

最後離開迴圈時,right會剛好停在x的平方根的整數部分。


二分搜尋 框架來解,就變成下面這個樣子

class Solution:
def mySqrt(self, x: int) -> int:

left, right = 0, x

while left <= right:

mid = left + (right - left) // 2
square = mid * mid

if square <= x:
left = mid + 1

elif square > x :
right = mid -1


return right

結語

好,今天一口氣介紹了最精華的部分,

通用的Binary search 二分搜索法的框架,還有相關的衍伸變化題與演算法建造流程,


希望能幫助讀者、觀眾徹底理解它的原理,

並且能夠舉一反三,洞察其背後的本質和了解相關的應用領域!


感謝收看囉! 我們下篇文章再見~

86會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
發表第一個留言支持創作者!
找出區間[1, n] 內的軸心點位置。通過介紹直覺法、改良直覺法和二分搜尋等算法,最終給出了解析解(推導軸心點的公式解),提供了對應的程式碼和參考資料。該問題的最優解是使用解析解,能夠在O(1)的時間複雜度內找到答案。
題目會給我們一個山形的輸入陣列,和目標值target,要求我們找出目標值所在的陣列索引。如果出現兩次,返回比較小的那一個,也就是比較靠左的那個索引值。 山形的意思就是說,從最左側到山頂最大值都是遞增,從山頂最大值到右側都是遞減。
題目給定一個已排序的輸入陣列,陣列裡面的數字自分別代表每篇論文的被引用數。 要求我們計算h-index。 h-index的定義: 找一個最大的h值,使得有h篇論文,個別論文的被引用數都 大於等於 h
題目會給定一個2D 二維的矩陣,矩陣內的元素值代表對應的高度,要求我們找出相對最高點,也就是(大樓)高度大於N4 東、南、西、北 四個鄰居的索引值。 題目保證矩陣內相鄰的元素值都不相同,也又是相鄰的兩兩相比較,一定有一個比較高,有一個比較矮。
題目會給定一個陣列,陣列裡面的元素分布就像一座山峰。 最大值的左邊都是上坡段,最大值的右邊都是下坡段。 要求我們找出陣列裡面的絕對極大值(absolute max value)所在的陣列索引
這題的題目在這裡 題目會給定一個輸入整數x, 要求我們返回x的正整數平方根(取無條件捨去小數部分的正整數值)
找出區間[1, n] 內的軸心點位置。通過介紹直覺法、改良直覺法和二分搜尋等算法,最終給出了解析解(推導軸心點的公式解),提供了對應的程式碼和參考資料。該問題的最優解是使用解析解,能夠在O(1)的時間複雜度內找到答案。
題目會給我們一個山形的輸入陣列,和目標值target,要求我們找出目標值所在的陣列索引。如果出現兩次,返回比較小的那一個,也就是比較靠左的那個索引值。 山形的意思就是說,從最左側到山頂最大值都是遞增,從山頂最大值到右側都是遞減。
題目給定一個已排序的輸入陣列,陣列裡面的數字自分別代表每篇論文的被引用數。 要求我們計算h-index。 h-index的定義: 找一個最大的h值,使得有h篇論文,個別論文的被引用數都 大於等於 h
題目會給定一個2D 二維的矩陣,矩陣內的元素值代表對應的高度,要求我們找出相對最高點,也就是(大樓)高度大於N4 東、南、西、北 四個鄰居的索引值。 題目保證矩陣內相鄰的元素值都不相同,也又是相鄰的兩兩相比較,一定有一個比較高,有一個比較矮。
題目會給定一個陣列,陣列裡面的元素分布就像一座山峰。 最大值的左邊都是上坡段,最大值的右邊都是下坡段。 要求我們找出陣列裡面的絕對極大值(absolute max value)所在的陣列索引
這題的題目在這裡 題目會給定一個輸入整數x, 要求我們返回x的正整數平方根(取無條件捨去小數部分的正整數值)
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
藍白合破局後,各黨也陸續公布其總統候選人副手。而國民黨侯友宜與趙少康組成的「侯康配」民調卻讓人難以信服,看看兼任中廣董事長以及總經理的趙少康,再看看國民黨水漲船高的民調,引起外界質疑民調淪為政媒操作下的產物。
Thumbnail
2023年4月5日至8日,❙法國❙ 總統 ❙馬克龍❙ 對 ❙中華人民共和國❙ 進行國事訪問,期間有關應該避免陷入 ❙中美❙ 在 ❙台灣❙ 問題上的對抗的言論在西方政界引起軒然大波。 準確地說,❙馬克龍❙ 的 ❙歐洲❙「戰略自主」論調並非在國事訪問期間發表的,而是在國事訪問後,在飛回 —— 途經廣州
Thumbnail
話說商鞅被車裂後,秦惠王恨他歸恨他,但卻很理智,商鞅的一系列方針政策繼續沿用。 因為他發現商鞅打造的這臺國家機器太好用了,短短二十年的時間,秦國從被看不起的西戎小國到被周天子官方封為西部大總管,這種感覺太美妙了。 一碼歸一碼的處理方法,顯示出了秦惠王的政治水平。 政治,是一種需要極高理性的遊戲,還是
Thumbnail
在台北市中心街弄中有許多藝術機構,這些藝術機構不論是畫廊、美術館、藝術教室、藝文空間、博物館等多種形式存在。從這些藝術機構更扮演著社會大眾與藝術接觸機會。長流美術館算是台灣藝術機構之先進機構,憑藉著長流機構背景讓藝術深入社會大眾心中。 長流美術館相關資訊:: 地址: 台北市大安區仁愛路二段63號B1
Thumbnail
縱貫式中介模型(Longitudinal Mediation Model)是研究隨著時間的改變,變數X如何通過中介變數M影響變數Y的統計模型。它是長期觀察和分析數據的有用工具,可以揭示X和Y之間的關係以及中介變數M在這個關係中扮演的角色。本文將介紹縱貫式中介模型Mplus操作
Thumbnail
唉!這陣子我太常在公司嘆氣,我無法要求別人一定要進步,但每個人的職位就要做好該職位的工作內容,不是嗎? 有些人並不是這樣子想,領多少薪水做多少事是應該的,其實大家都是在工作賺錢,想想你的職位是什麼,該做就要做,不是應該的嗎? 客服工作不就是要熟悉系統,去了解客戶的問題點在哪,進而回答客戶的問題,這不
Thumbnail
時隔多年,看到網飛(Netflix)將這部電影購入旗下著實是一種驚喜! 一個外貌看似九歲其實實際年齡早已三十有餘的偽蘿莉處心積慮機關算盡搶奪當家女主人大位的奮鬥史(無誤),我先把孤兒怨劇情的主體告訴各位,因為這部片的梗其實不難猜而且也不新鮮,比較讓我覺得有趣的是孤兒怨除了有鮮血、屍體、嚇人橋段之外,
Thumbnail
《NBA 2K20》發售以來應該不少玩家發現了一個奇妙的現象,許多充滿權威性的評分媒體都給予了中高以上的分數,但玩家這頭卻連 1 分都捨不得給。當然,我們無法得知或是去揣測那些給高分媒體是不是真的覺得遊戲好玩,至少在玩家們一致性的評論中,可以得知這款遊戲確實出了某種問題。
Thumbnail
歷史記載,所謂「合縱」又稱「合從」也就是聯合幾個較弱的國家對抗強大的秦國,巧合的是 alliance ㄧ字竟然也可能「alliance = a.ll.i.an.ce = 合.人人.以.anti.cina = 合.从.以.反.秦 = 合.從.以.反.秦 = 合從以反秦 = 合縱以反秦」或 ......
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
藍白合破局後,各黨也陸續公布其總統候選人副手。而國民黨侯友宜與趙少康組成的「侯康配」民調卻讓人難以信服,看看兼任中廣董事長以及總經理的趙少康,再看看國民黨水漲船高的民調,引起外界質疑民調淪為政媒操作下的產物。
Thumbnail
2023年4月5日至8日,❙法國❙ 總統 ❙馬克龍❙ 對 ❙中華人民共和國❙ 進行國事訪問,期間有關應該避免陷入 ❙中美❙ 在 ❙台灣❙ 問題上的對抗的言論在西方政界引起軒然大波。 準確地說,❙馬克龍❙ 的 ❙歐洲❙「戰略自主」論調並非在國事訪問期間發表的,而是在國事訪問後,在飛回 —— 途經廣州
Thumbnail
話說商鞅被車裂後,秦惠王恨他歸恨他,但卻很理智,商鞅的一系列方針政策繼續沿用。 因為他發現商鞅打造的這臺國家機器太好用了,短短二十年的時間,秦國從被看不起的西戎小國到被周天子官方封為西部大總管,這種感覺太美妙了。 一碼歸一碼的處理方法,顯示出了秦惠王的政治水平。 政治,是一種需要極高理性的遊戲,還是
Thumbnail
在台北市中心街弄中有許多藝術機構,這些藝術機構不論是畫廊、美術館、藝術教室、藝文空間、博物館等多種形式存在。從這些藝術機構更扮演著社會大眾與藝術接觸機會。長流美術館算是台灣藝術機構之先進機構,憑藉著長流機構背景讓藝術深入社會大眾心中。 長流美術館相關資訊:: 地址: 台北市大安區仁愛路二段63號B1
Thumbnail
縱貫式中介模型(Longitudinal Mediation Model)是研究隨著時間的改變,變數X如何通過中介變數M影響變數Y的統計模型。它是長期觀察和分析數據的有用工具,可以揭示X和Y之間的關係以及中介變數M在這個關係中扮演的角色。本文將介紹縱貫式中介模型Mplus操作
Thumbnail
唉!這陣子我太常在公司嘆氣,我無法要求別人一定要進步,但每個人的職位就要做好該職位的工作內容,不是嗎? 有些人並不是這樣子想,領多少薪水做多少事是應該的,其實大家都是在工作賺錢,想想你的職位是什麼,該做就要做,不是應該的嗎? 客服工作不就是要熟悉系統,去了解客戶的問題點在哪,進而回答客戶的問題,這不
Thumbnail
時隔多年,看到網飛(Netflix)將這部電影購入旗下著實是一種驚喜! 一個外貌看似九歲其實實際年齡早已三十有餘的偽蘿莉處心積慮機關算盡搶奪當家女主人大位的奮鬥史(無誤),我先把孤兒怨劇情的主體告訴各位,因為這部片的梗其實不難猜而且也不新鮮,比較讓我覺得有趣的是孤兒怨除了有鮮血、屍體、嚇人橋段之外,
Thumbnail
《NBA 2K20》發售以來應該不少玩家發現了一個奇妙的現象,許多充滿權威性的評分媒體都給予了中高以上的分數,但玩家這頭卻連 1 分都捨不得給。當然,我們無法得知或是去揣測那些給高分媒體是不是真的覺得遊戲好玩,至少在玩家們一致性的評論中,可以得知這款遊戲確實出了某種問題。
Thumbnail
歷史記載,所謂「合縱」又稱「合從」也就是聯合幾個較弱的國家對抗強大的秦國,巧合的是 alliance ㄧ字竟然也可能「alliance = a.ll.i.an.ce = 合.人人.以.anti.cina = 合.从.以.反.秦 = 合.從.以.反.秦 = 合從以反秦 = 合縱以反秦」或 ......