分块查找又称为索引查找,它结合顺序查找和二分查找方法。在分块查找中我们首先需要知道查找表的构造,而查找表构造的要求如下:
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 查找 。