資料結構與演算法 十二 二分查詢和插值查詢

2021-09-21 01:23:20 字數 2687 閱讀 4806

有乙個遊戲最能體現二分查詢的思路:我在紙上已經寫好了100以內的正整數數字,然後請你猜,問最多幾次可以猜出來?

這個遊戲的解法就是每次猜數後折取一半,我們把這種每次取中間記錄查詢的方法叫做折半查詢,或二分查詢。

二分查詢(binary earch),也稱為折半查詢。它的前提是線性表中的記錄必須是關鍵碼有序(通常從小到大有序) ,線性表必須採用順序儲存。

二分查詢的基本思想是:

/*二分查詢演算法*/

int binarysearch(int *a, int length, int key)

return 0;

}

該演算法還是比較容易理解的,同時我們也能感覺到它的效率非常高。但到底高多少?關鍵在於演算法的時閱複雜度分析。

從上面的二叉樹可以看出,二分查詢的最壞情況就是查詢到二叉樹的葉子結點,查詢次數等於二叉樹的深度㏒₂n+1,所以演算法時間複雜度為o(㏒₂n) 與,它顯然遠遠好於順序查詢的o(n)時間複雜度了。資料量越大o(㏒₂n)的效率就越優於o(n)。

不過由於二分查詢的前提條件是需要有序順序表來儲存,且不能用鍊錶作儲存結構,對於靜態查詢表,一次排序後不再變化,這樣的演算法已經非常好了。

但對於需要頻繁執行插入或刪除操作的資料集來說,維護有序的排序就會帶來不小的開銷,那就不建議使用。

沒有bug的二分查詢:

現在請問,上面的二分查詢**有沒有問題?哪段**會出現 bug ?

對於上面這段**而言,問題出在:mid = (low + high) / 2

這句**在 low 和 high 很大的時候,low + high可能會出現大於int_max的溢位的情況,從而導致陣列下標訪問錯誤。

一般的做法是將加法變成減法mid =  low + (high - low) / 2。 

還有一種更高逼格的寫法,也是官方的二分查詢的寫法,使用位運算mid =  low + ((high - low) >> 1)

使用位運算優化乘、除以及模運算

我們可以在二分查詢的基礎上思考,為什麼一定要折一半,而不是折四分之一或者折更多呢?

例如之前提到的斐波那契查詢就是按照**分割比例進行對折,那麼如何折才是最優的?

如果再讓你查 「zoom」,你又怎麼查?很顯然,這裡你絕對不會是從中間開始查起,而是有一定目的直接翻前面或後面。

同理,取值範圍0~10000之間的100個元素從小到大均勻分布在陣列中,如果我們需要查詢5,我們自然會考慮從陣列下標較小的開始查詢,而如果我們需要查詢8000,那自然從陣列下標較大的開始查詢。

我們先對二分查詢的計算中值公式進行變換,這樣mid就等於low加上high和low的差的一半:

我們考慮將1/2這個係數進行改進,下面為改進方案:

key-a[low]是為了得到查詢值比最小值大多少,a[high]-a[low]得到這個有序序列的均勻分布情況,(key-a[low]) / (a[high]-a[low])就可以得到查詢值在這個均勻分布的有序序列中的大致位置的係數,而改進後的查詢方法稱為插值查詢,其核心就在於插值的計算公式

現在有乙個有序序列a = (在0~100之間均勻分布),我們需要查詢key=59,length=10,如果使用斐波那契查詢就需要查詢4次,如果使用二分查詢那麼需要查詢3次。

如果使用插值查詢,第一次:(key-a[low]) / (a[high]-a[low]) = (59-1)/(99-1) = 58/98 = 0.592,mid=low+(high-low)*0.592 = 1+9×0.592 = 6.92 ≈ 6,而a[mid=6] = 59,只查詢1次就找到了。相比斐波那契查詢和二分查詢,此時的插值查詢的效率明顯提高了很多。

雖然這三種查詢方法的時間複雜度都為o(㏒₂n),但可以看出,在分布均勻且表較長的查詢表來說,插值查詢的效率要好太多。但反之查詢表類似這樣極端不均勻的資料而言,插值查詢的效率就不如斐波那契查詢和二分查詢,插值查詢就不是很合適的選擇。

/*插值查詢演算法*/

int binarysearch(int *a, int length, int key)

return 0;

}

注:還有關鍵的一點,二分查詢進行的是簡單的加法和除法運算,斐波那契查詢進行的是最簡單的加減法運算,而插值查詢進行相當複雜的加減乘除都有的四則運算,如果在海量的資料查詢中,這種細微的差別可能也會影響最終的查詢效率,所以使用哪種查詢方法還是需要視情況進行比較後來選擇。

資料結構學習(十二) 二分查詢

首先確定該陣列的中間的下標 然後讓需要查詢的數findval和arr mid 比較 2.1 findval arr mid 說明要查詢的數在mid的右邊,因此需要遞迴地向右查詢 2.2 findval 什麼時候結束遞迴 3.1 找到結束遞迴 3.2 遞迴完整個陣列,仍然沒有找到findval,lef...

重新整理資料結構與演算法 二分查詢法 十二

什麼是二分查詢呢?直接給乙個位址哈,避免誤解。二分法查詢 9751511 1 根據我發的這個鏈結呢?我們知道通過二分查詢一定有乙個硬性要求,那就是說一定要按照某種順序排列,不一定是大小。比如說有乙個陣列為 如果如果你要查詢100,如果按照遍歷的話,那麼要到最後乙個。如果通過二分法,那麼第乙個比較的就...

資料結構與演算法(11)插值查詢

聯想一下自己在字典中查詢單詞的經歷,我們肯定不會使用折半查詢,因為對於分布有規律的單詞而言,我們有更好的方式。我們會使用大腦的自適應計算出單詞的大概位置,一步步縮減範圍。類似的對於分布有規律的資料元素來說,我們可以改進一下折半查詢的軸點,不使用中點,而是使其可以根據lo 和 hi的位置上元素的大小進...