成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

學(xué)習(xí)JavaScript數(shù)據(jù)結(jié)構(gòu)與算法 — 散列表

betacat / 1562人閱讀

摘要:定義散列表是字典鍵值對(duì)的一種實(shí)現(xiàn)方式。根據(jù)鍵值從散列表中移除值。分離鏈接分離鏈接法在散列表的每一個(gè)位置創(chuàng)建一個(gè)鏈表并將元素存儲(chǔ)在里面。一個(gè)表現(xiàn)良好的散列函數(shù)應(yīng)該有較好的插入和查找性能且有較低的沖突可能性。

定義

散列表是字典(鍵、值對(duì))的一種實(shí)現(xiàn)方式。每次在字典中獲取一個(gè)值,都需要重復(fù)遍歷字典,如果用散列表,字典中的每個(gè)key都對(duì)應(yīng)一個(gè)確定的位置,從而不再需要遍歷。
以電子郵件地址簿為例,每個(gè)名字(key)對(duì)應(yīng)一個(gè)郵件地址,用散列函數(shù)計(jì)算每個(gè)key在散列表中的位置(這里使用key的所有字符的ASCII碼值相加),如圖:

方法

put(key,value):向散列表增加一個(gè)新的項(xiàng)(也能更新散列表)。

remove(key):根據(jù)鍵值從散列表中移除值。

get(key):返回根據(jù)鍵值檢索到的特定的值。

實(shí)現(xiàn)
function HashTable() {
    // 私有變量table,作為散列表的載體
    var table = [];
    // 散列函數(shù),計(jì)算key對(duì)應(yīng)的hash值
    var loseloseHashCode = function (key) {
        var hash = 0;
        for (var i = 0; i < key.length; i++) {
            hash += key.charCodeAt(i); // 所有字符的ASCII碼值相加
        }
        // 為了將hash值變?yōu)楦〉闹担砸粋€(gè)數(shù)并取余數(shù)
        // 這里除以素?cái)?shù)37是為了降低計(jì)算出重復(fù)hash的概率(后續(xù)會(huì)處理hash重復(fù)的問題)
        return hash % 37;
    };
    // put方法,向散列表增加一個(gè)新的項(xiàng)(也能更新散列表)
    this.put = function(key, value) {
        var position = loseloseHashCode(key); // 計(jì)算key的hash值作為當(dāng)前數(shù)據(jù)在散列表中的位置
        table[position] = value; // 將當(dāng)前數(shù)據(jù)插入散列表
    };
    // get方法,返回根據(jù)鍵值檢索到的特定的值
    this.get = function (key) {
        return table[loseloseHashCode(key)]; //根據(jù)key計(jì)算出的hash取對(duì)應(yīng)位置中的值
    };
    // remove方法,根據(jù)鍵值從散列表中移除值
    this.remove = function(key) {
        table[loseloseHashCode(key)] = undefined;
    };
}

到這里,一個(gè)基本的的散列表已經(jīng)實(shí)現(xiàn)了,但沒有考慮散列函數(shù)計(jì)算出重復(fù)hash值的問題,這會(huì)導(dǎo)致后添加的數(shù)據(jù)覆蓋先添加的數(shù)據(jù),比如:

var table = new HashTable();
// Jamie和Sue的hash值都為5,因此Sue的數(shù)據(jù)會(huì)覆蓋Jamie的數(shù)據(jù)
table.put("Jamie", "[email protected]");
table.put("Sue", "[email protected]");

處理上述沖突的方式主要有:分離鏈接、線性探查,雙散列法,這里使用前兩種。

分離鏈接

分離鏈接法在散列表的每一個(gè)位置創(chuàng)建一個(gè)鏈表并將元素存儲(chǔ)在里面。它的缺點(diǎn)是在HashTable實(shí)例之外還需要額外的存儲(chǔ)空間。如圖,散列表的每一個(gè)位置都是一個(gè)鏈表,鏈表里可以存儲(chǔ)多個(gè)數(shù)據(jù)。

下面,重寫put、get、remove方法,實(shí)現(xiàn)散列表的分離鏈接(其中鏈表類的實(shí)現(xiàn)參照鏈表)。

    // 首先要添加一個(gè)新的輔助類來實(shí)例化添加到鏈表的元素
    var ValuePair = function(key, value){
        this.key = key;
        this.value = value;
    };
    // 改寫put方法
    this.put = function(key, value){
        var position = loseloseHashCode(key);
        if (table[position] == undefined) {
            // 在當(dāng)前位置示例化一個(gè)鏈表
            table[position] = new LinkedList();
        }
        // 在鏈表中添加元素
        table[position].append(new ValuePair(key, value));
    };
    // 改寫get方法
    this.get = function(key) {
        var position = loseloseHashCode(key);
        if (table[position] !== undefined){
            // 獲取鏈表的第一個(gè)元素
            var current = table[position].getHead();
            // 遍歷鏈表(這里不能遍歷到最后一個(gè)元素,后續(xù)特殊處理)
            while(current.next){
                // 如果鏈表中存在當(dāng)前key對(duì)應(yīng)的元素,返回其值
                if (current.element.key === key){
                    return current.element.value;
                }
                // 處理下一個(gè)元素
                current = current.next;
            }
            // 處理鏈表只有一個(gè)元素的情況或處理鏈表的最后一元素
            if (current.element.key === key){
                return current.element.value;
            }
        }
        // 不存在值,返回undefined
        return undefined;
    };
    // 改寫remove方法
    this.remove = function (key) {
        var position = loseloseHashCode(key);
        if (table[position] !== undefined) {
            // 獲取當(dāng)前位置鏈表的第一個(gè)元素
            var current = table[position].getHead();
            // 遍歷鏈表(這里不能遍歷到最后一個(gè)元素,后續(xù)特殊處理)
            while (current.next) {
                if (current.element.key === key) {
                    // 遍歷到對(duì)應(yīng)元素,從鏈表中刪除
                    table[position].remove(current.element);
                    if (table[position].isEmpty()) {
                        // 如果鏈表已經(jīng)空了,將散列表的當(dāng)前位置置為undefined
                        table[position] = undefined;
                    }
                    // 返回true表示刪除成功
                    return true;
                }
                // 處理下一個(gè)元素
                current = current.next;
            }
            // 處理鏈表只有一個(gè)元素的情況或處理鏈表的最后一元素
            if (current.element.key === key) {
                table[position].remove(current.element);
                if (table[position].isEmpty()) {
                    table[position] = undefined;
                }
                return true;
            }
        }
        // 要?jiǎng)h除的元素不存在,返回false
        return false;
    };
線性探查

線性探查法在向散列表中插入元素時(shí),如果插入位置position已經(jīng)被占據(jù),就嘗試插入position+1的位置,以此類推,直到找到空的位置。下面用線性探查的方式重寫put、get、remove方法

    // 重寫put方法
    this.put = function(key, value){
        var position = loseloseHashCode(key);
        // 依次查找,如果當(dāng)前位置不為空,position + 1,直到找到為空的位置為止
        while (table[position] != undefined){
            position++;
        }
        table[position] = new ValuePair(key, value);
    };
    // 重寫get方法
    this.get = function(key) {
        var position = loseloseHashCode(key);
        var len = table.length;
        // 只要當(dāng)前位置小于散列表長度就要查找
        if (position < len){
            // 由于查找的值可能是以 position + 1 的形式類推,找到空位后插入的
            // 因此需要從當(dāng)前位置(position)開始查找,直到找到key相同的位置,或者找完整個(gè)散列表
            while (position < len && (table[position] === undefined || table[position].key !== key)){
                position++;
            }
            // 如果最終position >= len,說明沒找到
            if (position >= len) {
                return undefined
            } else {
                // 否則說明找到了,返回對(duì)應(yīng)值
                return table[position].value;
            }
        }
        // 如果當(dāng)前位置為空,說明添加時(shí)沒有累加position,直接返回undefined
        return undefined;
    };
    // 改寫remove方法
    this.remove = function(key) {
        var position = loseloseHashCode(key);
        var len = table.length;
        if (position < len){
            // 從當(dāng)前位置(position)開始查找,直到找到key相同的位置,或者找完整個(gè)散列表
            while (position < len && (table[position] === undefined || table[position].key !== key)){
                position++;
            }
            // 如果最終position < len,說明找到了,將對(duì)應(yīng)位置數(shù)據(jù)刪除
            if (position < len) {
                table[position] = undefined;
            }
        }
    };
更好的散列函數(shù)

上述散列函數(shù)表現(xiàn)并不好,它極易計(jì)算出相同的hash值,從而導(dǎo)致沖突。一個(gè)表現(xiàn)良好的散列函數(shù)應(yīng)該有較好的插入和查找性能且有較低的沖突可能性。下面的散列函數(shù),被證明是比較合適的。

var djb2HashCode = function (key) {
    var hash = 5381; // 一個(gè)較大的素?cái)?shù)基準(zhǔn)值
    for (var i = 0; i < key.length; i++) {
        hash = hash * 33 + key.charCodeAt(i); // 基準(zhǔn)值乘以33再加ASCII碼值
    }
    return hash % 1013; //除以1013取余
};

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/81888.html

相關(guān)文章

  • 學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)算法之字典和列表

    摘要:小結(jié)實(shí)現(xiàn)了字典和哈希表感覺沒有想象中那么困難,當(dāng)然這還是開始。 本系列所有文章:第一篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之棧與隊(duì)列第二篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之鏈表第三篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之集合第四篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之字典和散列表第五篇文章:學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)與算法之二叉搜索樹 字典 不是新華字典哦,這里的字典也稱作_映射_,是一種數(shù)據(jù)結(jié)構(gòu),跟set集合很相似的一種數(shù)據(jù)結(jié)構(gòu),都可以用來...

    Heier 評(píng)論0 收藏0
  • Javascript數(shù)據(jù)結(jié)構(gòu)算法》筆記-「字典和列表

    摘要:我經(jīng)常在業(yè)務(wù)代碼中把數(shù)據(jù)處理成這種字典的數(shù)據(jù)結(jié)構(gòu)獲取的方法哈希表在學(xué)習(xí)了類之后,我們會(huì)學(xué)習(xí)散列表,也就是哈希表。 《Javascript數(shù)據(jù)結(jié)構(gòu)和算法》筆記-「字典和散列表」 集合、字典、散列表存儲(chǔ)的都是「不重復(fù)」的數(shù)據(jù)結(jié)構(gòu) 集合:我們更關(guān)注每一個(gè)元素的值,并把其作為主要元素 字典:我們用[鍵,值]的形式來存儲(chǔ)數(shù)據(jù) 散列表: 跟字典類似,也會(huì)是用[鍵,值]的形式來存儲(chǔ)數(shù)據(jù) 但是「字...

    wenyiweb 評(píng)論0 收藏0
  • 算法系列——JavaScript中廣度優(yōu)先搜索思想實(shí)現(xiàn)

    摘要:散列表上面的地圖向我們展示了如何用廣度優(yōu)先搜索的思想找到北京到廣州的最短路線。在廣度優(yōu)先搜索中,我們需要用到隊(duì)列的這種思想來實(shí)現(xiàn)查找。建立了下面這個(gè)模型武漢廣州西藏上海上海武漢廣州代碼完整實(shí)現(xiàn),利用遞歸和廣度優(yōu)先搜索的思想實(shí)現(xiàn)。 什么是廣度優(yōu)先搜索? 如果只是是背概念,幼兒園的小朋友都能背下來念給你聽。 假設(shè)看這篇文章的都和我一樣是個(gè)前端工程師,我們要從廣度優(yōu)先搜索(BFS)中學(xué)到什么...

    everfly 評(píng)論0 收藏0
  • CSS技巧

    摘要:技巧使你的更加專業(yè)這是上關(guān)于技巧的一篇譯文,另外你也可以在本項(xiàng)目看到原文。列舉了一些很實(shí)用的技巧,比如給空內(nèi)容的標(biāo)簽添加內(nèi)容,逗號(hào)分隔列表等等。排序算法看源碼,把它背下來吧排序算法的封裝。主要幫助初學(xué)者更好的掌握排序算法的實(shí)現(xiàn)。 成為專業(yè)程序員路上用到的各種優(yōu)秀資料、神器及框架 成為一名專業(yè)程序員的道路上,需要堅(jiān)持練習(xí)、學(xué)習(xí)與積累,技術(shù)方面既要有一定的廣度,更要有自己的深度。 Java...

    DangoSky 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<