分塊查找又稱爲索引查找,它結合順序查找和二分查找方法。在分塊查找中我們首先需要知道查找表的構造,而查找表構造的要求如下:
1、將查找表分塊,塊與塊之間是有序的,而塊內無序。即第 i 塊內的最大值,小於等於第 i + 1塊的最小值。
2、根據查找表構造一個索引表 , 索引表的結構是按照關鍵字有序的。索引表中每個Item的結構如下:
最大關鍵字 |
起始位置 |
查找思想因爲塊間的數據是有序的,所以構造出來的索引表也是有序的,在索引表比較大時可以利用二分查找,找到需要的關鍵字,然後在查找表的塊內找所需的數據。
如,在查找 R = {2, 4, 1, 9, 10, 20, 38, 24, 78, 99} 其中block_size = 3 ;
在表中查找 2 , 首先定位去第一塊內查找,然後順序遍歷塊內的每一個元素, 得到返回位置0 (第一個元素位置);
如果再表中查找200 , 由於索引表中的最大關鍵字值爲99 , 所以可以判定不存在這樣的一個元素值, 可以直接返回 -1 。
說到塊查找,我們在書中也沒有看到如何對塊進行劃分,網上搜了一圈也沒有找到合適的方法,後來心一橫,管他三七二十一自己寫一個算法把它先搞出來再說 —— 方法類似於冒泡排序(開始給的一組數據,完全按照從小到大的排列,還以爲做成冒泡排序了呢^_^),時間複雜度爲O(N2) .
該劃分的算法思想是對塊 i 內的數據 , 從第一個開始逐一和 i+1 以後的塊內找一個最小值和他交換,從而保證 第 i 塊內的數據小於等於 第 i +1 塊以後的數據 。 然後,塊查找函數對這個查找表進行搜索, 速度就快多了,以下爲Java 實現版本:
public class BlockSearch {
public static int[] DATA_COLLECTION = {
20, 38, 1,4, 2, 78, 99, 24, 10, 9
};
public static int blockSearch(SearchTable st, int key) {
if (st == null)
return -1;
IndexTable[] it = st.getIndexTable();
if (it == null)
return -1;
int[] table = st.getTable();
int i = 0;
for (; i < it.length; i++) {
if (it[i].mMax >= key)
break;
}
if (i >= it.length)
return -1;
for (i = it[i].mIndex; i < SearchTable.BLOCK_SIZE; i++) {
if (table[i] == key)
return i;
}
return -1;
}
public static void main(String[] args) {
BlockSearch.SearchTable st = new BlockSearch().new SearchTable(DATA_COLLECTION);
System.out.println(blockSearch(st, 2));
}
public class SearchTable {
public static final int BLOCK_SIZE = 3;
private IndexTable mIndexTable[];
private int[] mSearchData;
public SearchTable(int[] data) {
if (data == null || data.length == 0)
return;
int n = data.length / BLOCK_SIZE + 1, tmp, k, p = -1, gap = 0;
int iLen = data.length - BLOCK_SIZE + 1;
mIndexTable = new IndexTable[n];
// 分塊
for (int i = 0; i < iLen; i++) {
k = i / BLOCK_SIZE + 1;
for (int j = k * BLOCK_SIZE; j < data.length; j++) {
if ((tmp = data[i] - data[j]) > gap) {
p = j;
gap = tmp;
}
}
if(gap > 0){
tmp = data[i];
data[i] = data[p];
data[p] = tmp;
}
gap = 0;
}
// 構造索引表
k = 0;
for (int i = 0; i < n;i++) {
p = -1;
tmp = data[i];
for (int j = 1; j < BLOCK_SIZE; j++) {
p = i * n + j;
if (p < data.length && data[p] > tmp) {
tmp = data[p];
}
}
mIndexTable[i] = new IndexTable(tmp,i * n);
}
mSearchData = data;
}
public IndexTable[] getIndexTable() {
return mIndexTable;
}
private int[] getTable() {
return mSearchData;
}
}
public class IndexTable {
int mMax;
int mIndex;
IndexTable(int max , int index) {
mMax = max;
mIndex = index;
}
}
}
下面換另一個角度思考:
如果塊查找利用的是數據自身的結構,那麼是否避免了塊構造花費的時間?
比如說,在開始之初肯定是沒有任何數據的,那麼每一次插入數據元素的時候查找表都在維護一個索引表,對於一個固定的 BLOCK_SIZE , 對於前 n (1=< n <= BLOCK_SIZE)條數據,那麼只有一個索引,索引的起始位置爲 0 , 最大值爲塊內的最大值。當n > BLOCK_SIZE後每次插入的數據大於最大值,則直接插入到查找表的後面,但若小於 塊的最大值,就要調整前面塊內的數據,同時也要修改索引表.... ... 這樣看來還是需要一個算法來維護這個索引表,保證塊間有序 ....
這樣看來還是需要一個算法來維護索引表纔行啊,而這個算法還在探索中、、、、、
好了,塊查找算法就寫到這裏,後續找到了塊劃分的算法再補充 。 下一篇 Fibonacci 查找 。