二分查詢的變形實現

2021-10-01 01:40:40 字數 3213 閱讀 3196

如圖:

這個功能並不複雜,它是通過維護乙個很大的 ip 位址庫來實現的。位址庫中包括 ip 位址範圍和歸屬地的對應關係。

當我們想要查詢 202.102.133.13 這個 ip 位址的歸屬地時,我們就在位址庫中搜尋,發現這個 ip 位址落在 [202.102.133.0, 202.102.133.255] 這個位址範圍內,那我們就可以將這個 ip 位址範圍對應的歸屬地 「 山東東營市 」 顯示給

使用者了。

[202.102.133.0, 202.102.133.255] 山東東營市

[202.102.135.0, 202.102.136.255] 山東煙台

[202.102.156.34, 202.102.157.255] 山東青島

[202.102.48.0, 202.102.48.255] 江蘇宿遷

[202.102.49.15, 202.102.51.251] 江蘇泰州

[202.102.56.0, 202.102.56.255] 江蘇連雲港

現在我的問題是,在龐大的位址庫中逐一比對ip位址所在的區間,是非常耗時的。假設我們有12萬條這樣的ip區間與歸屬地的對應關係,如何快速定位出乙個ip位址的歸屬地呢?

上一節我講了二分查詢的原理,並且介紹了最簡單的一種二分查詢的**實現。今天我們來講幾種二分查詢的變形問題。

上一節中的二分查詢是最簡單的一種,即有序資料集合中不存在重複的資料,我們在其中查詢值等於某個給定值的資料。如果我們將這個問題稍微修改下,有序資料集合中存在重複的資料,我們希望找到第乙個值等

於給定值的資料,這樣之前的二分查詢**還能繼續工作嗎?

比如下面這樣乙個有序陣列,其中, a[5] , a[6] , a[7] 的值都等於 8 ,是重複的資料。我們希望查詢第乙個等於 8 的資料,也就是下標是 5 的元素

如果我們用上一節課講的二分查詢的**實現,首先拿 8 與區間的中間值 a[4] 比較, 8 比 6 大,於是在下標 5 到 9 之間繼續查詢。下標 5 和 9 的中間位置是下標 7 , a[7] 正好等於 8 ,所以**就返回了。

儘管 a[7] 也等於 8 ,但它並不是我們想要找的第乙個等於 8 的元素,因為第乙個值等於 8 的元素是陣列下標為 5 的元素。我們上一節講的二分查詢**就無法處理這種情況了。所以,針對這個變形問題,我們可以稍微改造一下上一節的**。

public

static

intbsearch

(int

a,int n,

int value)

else

if(a[mid]

< value)

else

else}}

return-1

;}

我來稍微解釋一下這段**。 a[mid] 跟要查詢的 value 的大小關係有三種情況:大於、小於、等於。對於 a[mid]>value 的情況,我們需要更新 high= mid-1 ;對於 a[mid]我們重點看第 11 行**。如果 mid 等於 0 ,那這個元素已經是陣列的第乙個元素,那它肯定是我們要找的;如果 mid 不等於 0 ,但 a[mid] 的前乙個元素 a[mid-1] 不等於 value ,那也說明 a[mid] 就是我們要找的第乙個值等於給定值的元素。

如果經過檢查之後發現 a[mid] 前面的乙個元素 a[mid-1] 也等於 value ,那說明此時的 a[mid] 肯定不是我們要查詢的第乙個值等於給定值的元素。那我們就更新 high=mid-1 ,因為要找的元素肯定出現在 [low, mid-1] 之間。

public

static

intbsearch

(int

a,int n,

int value)

else

if(a[mid]

< value)

else

else}}

return-1

;}

public

static

intbserach

(int

a ,int n,

int value)

else}}

return-1

;}

如果 a[mid] 小於要查詢的值 value ,那要查詢的值肯定在 [mid+1, high] 之間,所以,我們更新 low=mid+1 。

對於 a[mid] 大於等於給定值 value 的情況,我們要先看下這個 a[mid] 是不是我們要找的第乙個值大於等於給定值的元素。如果 a[mid] 前面已經沒有元素,或者前面乙個元素小於要查詢的值 value ,那 a[mid] 就是我們要找的元素。這段邏輯對應的**是第 7 行。

如果 a[mid-1] 也大於等於要查詢的值 value ,那說明要查詢的元素在 [low, mid-1] 之間,所以,我們將 high 更新為 mid-1 。

public

static

intbserach

(int

a ,int n,

int value)

else}}

return-1

;}

上一節我說過,凡是用二分查詢能解決的,絕大部分我們更傾向於

用雜湊表或者二叉查詢樹。即便是二分查詢在記憶體使用上更節省

,但是畢竟記憶體如此緊缺的情況並不多。那二分查詢真的沒什麼用處了嗎?

實際上,上一節講的求 「 值等於給定值 」 的二分查詢確實不怎麼會被用到,

二分查詢更適合用在 「 近似 」 查詢問題,在這類問題上,二分查詢的優勢更加明顯。

比如今天講的這幾種變體問題,用其他資料結構,比如雜湊表、二叉樹,就比較難實現了。

變體的二分查詢演算法寫起來非常燒腦,很容易因為細節處理不好而產生bug,

這些容易出錯的細節有:終止條件、區間上下界更新方法、返回值選擇。

所以今天的內容你最好能用自己實現一遍,對鍛鍊編碼能力、邏

輯思維、寫出 bug free **,會很有幫助。

變形二分查詢

title 資料結構與演算法 專案 主題 二分查詢 description 分析 1 查詢的是乙個有序的資料集合 2 每次查詢都是與區間的中間元素進行對比,將待查詢的區間縮小為之前的一半,直到找到要查詢的元素,或者區間被縮小為0 date 2021 version 0.1版本 author coff...

二分查詢及其變形

一 把乙個陣列最開始的若干個元素搬到陣列的末尾,我們稱之為陣列的旋轉。輸入乙個非遞減排序的陣列的乙個旋轉,輸出旋轉陣列的最小元素。例如陣列為的乙個旋轉,該陣列的最小值為1。note 給出的所有元素都大於0,若陣列大小為0,請返回0。方法一 o n public int minnumberinrota...

二分查詢及其變形

最基本的二分查詢模版 在有序陣列a中查詢key,如果找到,返回位置索引,否則,返回 1 int binarysearch int a,int n,int key else if a mid key else return 1 變種1 如果a有多個key元素,返回最大的,否則,返回 1 int bin...