查找之三 , 块查找

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

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;
        }
    }
}


在写块划分算法的时候查找了很多资料,结果发现没有多少有用的信息,相信网络上牛 B 的人是很多很多的,不可能没有一个分享出该划分算法的啊 .... ... 是不是自己方向弄错了?


下面换另一个角度思考:

如果块查找利用的是数据自身的结构,那么是否避免了块构造花费的时间?

比如说,在开始之初肯定是没有任何数据的,那么每一次插入数据元素的时候查找表都在维护一个索引表,对于一个固定的 BLOCK_SIZE , 对于前 n (1=< n  <= BLOCK_SIZE)条数据,那么只有一个索引,索引的起始位置为 0 , 最大值为块内的最大值。当n > BLOCK_SIZE后每次插入的数据大于最大值,则直接插入到查找表的后面,但若小于 块的最大值,就要调整前面块内的数据,同时也要修改索引表.... ... 这样看来还是需要一个算法来维护这个索引表,保证块间有序 ....

这样看来还是需要一个算法来维护索引表才行啊,而这个算法还在探索中、、、、、


好了,块查找算法就写到这里,后续找到了块划分的算法再补充微笑 。 下一篇 Fibonacci 查找 。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值