查找之三 , 塊查找

分塊查找又稱爲索引查找,它結合順序查找和二分查找方法。在分塊查找中我們首先需要知道查找表的構造,而查找表構造的要求如下:

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 查找 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章