大資料與算法系列之數值查詢演算法
查詢是指在大量的資料中尋找特定的元素,它是數值計算中常用的運算邏輯,一般情況下,可以按照順序依次查詢,但是在資料量較大的情況下,順序查詢的效能往往會讓人望而卻步,折半查詢和二叉樹查詢可以針對的數值序列做到快速查詢,雜湊查詢則是針對無序的數值序列查詢,它們都具有較好的效能。
二分搜尋演算法
折半查詢(Half-Interval Search)也稱作二分查詢(Binary Search)、對數查詢(Logarhmic Search),是指在有序的數值序列中,從中間開始查詢,如果查詢元素小於中間元素,則在中間元素的左邊區間再進行折半查詢,如果查詢元素大於中間元素,則在中間元素的右邊區間再進行折半查詢,直到取得的中間值等於查詢的值或者查詢的值不存在
折半查詢的最差時間複雜度是O(logn)(將所有的查詢進行到底),最優的時間複雜度是O(1)(第一個就是),平均時間複雜度是O(logn),在採用遞迴的方式進行折半查詢時,最差的空間複雜度是O(logn),總體而言,折半查詢的查詢效能較好,若對有序序列通過遍歷的方式查詢,則時間複雜度為O(n).
同時,折半查詢也可以按照二叉樹的思想來理解,中間值等價於二叉樹的根,根的左子樹是中間值的前半部分,根的右子樹是中間值的後半部分,並且折半查詢在資料中的查詢次數正好等於二叉樹的查詢層次樹,以有序序列{1,3,4,6,9,10,12,13,16}為例,構造一顆二叉樹,使得對它的每個節點的左子樹都比當前值小,右子樹都比當前節點的值大,如圖
當需要查詢元素值11的是否在此有序序列中時,可以依據圖按照如下方式進行查詢:首先與二叉樹的根節點9進行比較,11比9大,則向節點9的右子樹進行比較,與右子樹的根節點12進行比較,12比11大,於是向12的左子樹節點進行比較,10比11小,然後10已經是葉子節點,所以11不存在於該有序序列。
利用二叉樹的資料結構特徵進行折半查詢可以較好的理解整個查詢過程,實質過程與利用資料的折半查詢過程一致,良好的資料結構可以較好的解決資料與演算法的銜接問題。
與折半查詢對應的是順序查詢,順序查詢是一種最為直觀的查詢方式,通過從前往後的一次序列進行對比查詢,由於查詢的過程沒有任何技巧,當被查詢的元素不存在與序列中時,會把所有的元素遍歷一次,順序查詢的時間複雜度為O(n),它的缺點是效率低下,時間複雜度高,折半查詢避免了順序查詢的一一比較問題,使得效能得到一定的提升,對於有序序列的查詢應當儘量採用折半查詢,對於無序序列可以採用順序查詢的方式。
分塊查詢演算法
分塊查詢(Blocking Search)又稱作索引順序查詢,是一種在資料量較大的情況下,進行改進的一種查詢方式,同排序演算法的外排序方式類似,分塊查詢是一種介於順序查詢和二分查詢的演算法,它主要由兩部分組成:索引和有序的塊(塊中可無序),如圖:
資料{18,28,38}屬於索引部分,整個資料部分由三個大塊{16,13,14,18}、{21,25,22,28}、{31,36,38,33}組成,索引部分是由從三個資料部分中獲取的最大值組成,不僅如此,索引還記錄了最大值與最小值所在塊的起始位置。
例如,當需要查詢資料25時,首先對索引表進行查詢(可進行順序查詢),由於第一個塊中最大值為18,因此25一定不在第一個快中,第二個塊的最大值是28,因此25有可能在第二個塊中,28對應塊的起始位置為4,結束位置為第三個塊的起始位置減1,因此,查詢區間為[4,7],對該區間進行順序查詢,確定25是否在該數值序列中。
在資料量較大時,分塊查詢也能應對,例如,有1000MB的資料檔案,而當前系統可用記憶體100MB,一種簡單的方法是每次從資料檔案中讀取100MB到系統記憶體中,然後依次進行比較,此種方法湘桂耗時比較長,而採用分塊查詢則可以只需要構建1-10MB的索引即可,每個塊保持有序,每個檔案內的資料也不必有序,分塊查詢則首先通過訪問記憶體中的索引資料,確定資料對應的檔案快,然後再對該檔案塊內的資料進行查詢,以減少資料的查詢次數。
分塊查詢的演算法效率介於順序查詢和二分查詢只見那,在實際應用匯總,分塊後的資料塊大小不一定是平均的,可以根據實際情況按照數值的特徵進行分塊,分塊查詢不僅可以高效的進行查詢,而且當向資料中插入或刪除某條記錄時,也只需定位到所屬的資料塊,然後對相應的資料塊進行操作即可,而資料塊內的資料本身是無序的,所以插入資料或者刪除資料都不必移動大量的資料塊,它的只要缺陷在於需要一個輔助索引結構儲存塊的最大值和塊的起始位置,以及初始化塊需要的排序開銷。
雜湊查詢演算法
前面介紹的查詢方式均是基於有序序列進行的查詢方式,雜湊查詢是通過計算元素的儲存地址進行的快速查詢方式,它並不是要求序列一定有序,可以通過如下四個步驟完成對元素進行查詢。
- 用雜湊函式構造雜湊表
- 將元素進行雜湊函式過濾,選擇其儲存的地址
- 將需要查詢的元素經過雜湊函式對映到儲存地址
- 在儲存地址中,查詢元素是否存在
雜湊函式和雜湊表的結構是雜湊查詢中最重要的兩個因素,直接影響了雜湊的查詢速度,雜湊表(Hash Table,又稱散列表),是根據Key-Value構建的資料結構,Key由雜湊函式產生,以加快查詢的速度
雜湊函式構造方法有很多,如直接地址法、平方取中法、除數留餘法、隨機數法、數字分析法及摺疊法等。
- 直接地址法,直接地址法是一種線性的函式方法,可以利用公式F(key)=a*key+b表示,其中a、b為常變數,將key的值傳遞到函式中,直接生成在雜湊表中的對映地址
- 平方取中法,平方取中法師一種數值擷取方法,將一個數值進行平方計算後取中間的若干值,例如,數值886的平方為784996,可以去中間四位8499作為886的雜湊值。
- 除數留餘法,除數留餘法是通過將數值,對某值進行求餘,可以用公式F(key)=key%p(p<=N)表示,其中,N是散列表的長度,例如數值為45687,對10000求得餘數5687,則5687為數值45687的雜湊值
- 隨機數法,隨機數法將數值作為隨機種子傳入隨機函式,通過隨機的方法得到相應的雜湊值,用公式表示即F(key)=random(key)
- 數字分析法,數字分析法是根據陣列的特徵進行分析,例如,某公司擁有眾多員工,採用8位數進行員工標號,如(51-58-1396),前兩位是部分編號,中間兩位是員工崗位型別,最後四位是員工程式碼,因此當在某部門內是,只需要最後四位程式碼代替員工編號即可。
- 摺疊法,摺疊法是將關鍵詞按照一定的額位數進行切分,將切分後的若干部分進行數值相加,並根據散列表的長度,取末尾的幾個數值作為雜湊值,例如,將數值123456789切分為三部分,然後疊加相加,即,123+456+789=1368,並將末尾的三位數作為雜湊值
雖然可以通過上述的六種方法得到相應的雜湊值,但是隨著資料量的增加,當超越雜湊表的長度時,就可能發生數值衝突,例如,在除數留餘法中,45687對10000求得的餘數是5687,但是用同樣的方法55687對10000求得的餘數依然是5687,則45687與55687的雜湊值衝突,當兩者同時出現的時候會導致誤判或者誤查詢
通過良好的雜湊函式,可以減少一些衝突,但是衝突是雜湊函式中不可避免的問題,雜湊衝突的解決方法有很多,比如開房定址法,再雜湊法,鏈地址法等,他們的共同特徵是在發生衝突之後,通過其他的資料結構或者方式解決衝突
以鏈地址法為例,它的核心思想是將所有雜湊衝突的元素組成一個單鏈表,並將雜湊表的頭指標存入雜湊表的元素中,例如,一組數值{19,23,3,56,10,17,12,29},雜湊的長度為5,雜湊函式方法為除數留餘法,則用鏈地址法處理衝突如下
通過鏈地址法雖然解決了衝突,但是平均查詢長度也有所增加,上慄中,平均查詢長度為(1+1+1+1+1+2+2+2)/8=1.375