資料結構 二分查詢(python實現)

2021-09-29 10:25:25 字數 4105 閱讀 4853

最近開始學習王爭老師的《資料結構與演算法之美》,通過總結再加上自己的思考的形式記錄這門課程,文章主要作為學習歷程的記錄。

課前問題:假設有1000萬個整數資料,每個資料佔8個位元組,如何設計資料結構與演算法,快速判斷某個整數是否出現在這1000萬資料中,同時又希望這個功能記憶體不要超過100m?

這個問題就引入了二分查詢,舉個例子來理解二分查詢:假設乙個陣列為0-99,要查詢元素23在陣列中的位置

次數猜測範圍

中間數對比大小

10-99

4949>23

20-48

2424>23

30-23

1111<23

412-23

1717<23

518-23

2020<23

621-23

2222<23723

√利用二分思想,每次都與區間中間資料比對大小,縮小查詢區間的範圍,二分查詢針對的是乙個有序的資料集合,查詢思想有點類似分治思想,每次都通過跟區間的中間元素對比,將待查詢的區間縮小為原來的一半,直到找到要查詢的元素,或者區間被縮小為0。

二分查詢查詢效率非常高。假設資料大小為n,每次查詢後資料都會縮小為原來的一半,也就是會除以2,最壞的情況下,直到查詢區間被縮小為空才停止。

被查詢區間的大小變化:

​ n

nn , n/2

n/2n/

2 , n/4

n/4n/

4 , n/8

n/8n/

8 , … , n/2

kn/2^k

n/2k

當n /2

kn/2^k

n/2k

=1時,k的值就是縮小的次數。

​ 時間複雜度為o(k) = o(log

2n

log_2

log2​n

),故時間複雜度為o(logn)

最簡單的二分查詢:

最簡單的二分查詢就是有序陣列中不存在重複元素,**:

def binary_search(l,n):

low = 0

high = len(l)-1

while(low<=high):

mid = low+((high-low)>>1)

if n == l[mid]:

return mid

elif nl[mid]:

low = mid+1

下面有幾點需要注意:

1.迴圈退出條件:

​ 注意是low<=high,而不是low2.mid的取值:

​ mid = (low+high)/2的寫法是有問題的,low+high可能會發生溢位,改進的方法是將mid的計算方式寫出low+(high-low)/2.需要進一步優化的話,可將除以2的操作轉化為位運算,即low+((high-low)>>1),速度要快得多。

3.low和high的更新

​ low = mid+1, high = mid -1,如果直接寫成low = mid或者high = mid就會發生死迴圈。

實際上,二分查詢除了可以用迴圈,還可以採用遞迴實現。

def binary_search(l,n,low,high):   

while(high>=low):

mid = ((high-low)>>1)+low

if l[mid]==n:

return mid

elif l[mid]>n:

return binary_search(l,n,low,mid-1)

elif l[mid]二分查詢應用場景的侷限性

一、二分查詢依賴的是順序表結構,即陣列

​ 二分查詢不適合用於鍊錶,主要原因在於二分查詢演算法需要按照下標隨機訪問元素。陣列按照下標隨機訪問資料的時間複雜度為o(1),而鍊錶是o(n)。因此資料使用鍊錶儲存的話,二分查詢時間複雜度就會很高。

二、二分查詢針對的是有序陣列

​ 二分查詢要求資料必須是有序的。排序的時間複雜度最低是o(nlogn)。因此,針對的是一組靜態的資料,沒有頻繁地插入、刪除,我們可以進行一次排序,多次二次查詢,這樣排序的成本可以被均攤,二次查詢的邊際成本就會比較低。

三、資料量太小不適合採用二分查詢

​ 資料量很小,直接進行順序遍歷。只有資料量大時,二分查詢的優勢才比較明顯。但若是資料之間的比較操作非常耗時,不管資料量大小,都更推薦使用二分查詢。

四、資料量太大也不適合二分查詢

​ 二分查詢的底層需要依賴陣列,而陣列為了支援隨機訪問的特性,要求記憶體空間連續,對記憶體的要求比較苛刻。因此太大的資料用陣列儲存就比較吃力,不能用二分查詢。

補充乙個思考題,程式設計實現「求乙個數的平方根,保留6位有效小數」

def sqrt(t):

low = 0

high = t

mid = t/2

while(abs(t-mid**2)>0.00001):

if mid**2>t:

high = mid

elif mid**2一、查詢第乙個值等於給定值的元素

這個問題的難度在於當查詢到對應的值時,不一定是第乙個值,需要進行判斷。若mid=0,則元素已經是第乙個值了,即我們查詢的。若mid不等於0,但s[mid]的前乙個元素s[mid-1]不等於t,那麼s[mid]就是我們要找的第乙個等於給定值的元素。如果發生s[mid-1]也等於t,則令high = mid - 1,因為要找的元素肯定在[low,mid-1]中。

def bin_find(s,t): #查詢第乙個等於給定值的元素

low = 0

high = len(l)-1

while(high>=low):

mid = low + ((high-low)>>1)

if s[mid]t:

high = mid-1

elif s[mid]==t:

if mid==0 or s[mid-1]!=t:

return mid

else:

high = mid-1

二、查詢最後乙個等於給定值的元素

與變體一類似

def bin_find(s,t): #查詢最後乙個等於給定值的元素

low = 0

high = len(l)-1

while(high>=low):

mid = low + ((high-low)>>1)

if s[mid]t:

high = mid-1

elif s[mid]==t:

if mid==0 or s[mid+1]!=t:

return mid

else:

low = mid+1

三、查詢第乙個大於等於給定值的元素

難度在於s[mid]>=t。我們要首先看一下這個s[mid]是不是我們要找的第乙個大於等於給定值的元素。如果s[mid]前面無元素或前乙個元素小於要查詢的t,那s[mid]即為要找的元素。如果s[mid-1]也大於等於要找的t,說明要查詢的元素在[low,mid-1]之間,所以要將high更新為mid-1.

def bin_find(s,t): #查詢第乙個大於等於給定值的元素

low = 0

high = len(l)-1

while(high>=low):

mid = low + ((high-low)>>1)

if s[mid]=t:

if mid==0 or s[mid-1]四、查詢最後乙個小於等於給定值的元素

與變體三類似

def bin_find(s,t): #查詢最後乙個小於等於給定值的元素

low = 0

high = len(l)-1

while(high>=low):

mid = low + ((high-low)>>1)

if s[mid]<=t:

if mid == 0 or s[mid+1]>t:

return mid

else:

low = mid+1

elif s[mid]>t:

high = mid-1

python資料結構 二分查詢

二分查詢 有序列表對於我們的比較是很有用的。在順序查詢中,當我們與第乙個項進行比較時,如果第乙個項不是我們要查詢的,則最多還有 n 1 個專案。二分查詢從中間項開始,而不是按順序查詢列表。如果該項是我們正在尋找的項,我們就完成了查詢。如果它不是,我們可以使用列表的有序性質來消除剩餘項的一半。如果我們...

資料結構 二分查詢

二分查詢演算法也稱為折半搜尋 二分搜尋,是一種在有序陣列中查詢某一特定元素的搜尋演算法。搜素過程從陣列的中間元素開始,如果中間元素正好是要查詢的元素,則搜素過程結束 如果某一特定元素大於或者小於中間元素,則在陣列大於或小於中間元素的那一半中查詢,而且跟開始一樣從中間元素開始比較。如果在某一步驟陣列為...

資料結構 二分查詢

總共有n個元素,漸漸跟下去就是n,n 2,n 4,n 2 k 接下來操作元素的剩餘個數 其中k就是迴圈的次數。由於你n 2 k取整後 1,即令n 2 k 1,可得k log2n,是以2為底,n的對數 所以時間複雜度可以表示o o logn public class binarysearch else...