每週一練 之 資料結構與演算法(Dictionary 和 HashTable)

pingan8787發表於2019-05-20

這是第五週的練習題,上週忘記發啦,這周是複習 Dictionary 和 HashTable

下面是之前分享的連結:

歡迎關注我的 個人主頁 && 個人部落格 && 個人知識庫 && 微信公眾號“前端自習課”

本週練習內容:資料結構與演算法 —— Dictionary 和 HashTable

這些都是資料結構與演算法,一部分方法是團隊其他成員實現的,一部分我自己做的,有什麼其他實現方法或錯誤,歡迎各位大佬指點,感謝。

一、字典和雜湊表的概念

  1. 字典是什麼?

  2. 字典和集合有什麼異同?

  3. 什麼是雜湊表和雜湊函式?

  4. 雜湊表的特點是什麼?


解析:

  1. 字典是什麼?

字典是一種以 鍵-值對 形式儲存資料的資料格式,其中鍵名用來查詢特定元素。

  1. 字典和集合有什麼異同?

相同:都是用來儲存不同元素的資料格式;
區別:集合是以 值-值 的資料格式儲存,而字典是以 鍵-值 的資料格式儲存。

  1. 什麼是雜湊表和雜湊函式?

雜湊表(Hash table,也叫雜湊表),是根據關鍵碼值(·Key value·)而直接進行訪問的資料結構。也就是說,它通過把關鍵碼值對映到表中一個位置來訪問記錄,以加快查詢的速度。這個對映函式叫做雜湊函式,存放記錄的陣列叫做雜湊表

  1. 雜湊表的特點是什麼?

特點:陣列和連結優點的結合,查詢速度非常的快,幾乎是O(1)的時間複雜度,並且插入和刪除也容易。

二、請實現一個字典

set(key,value):向字典中新增新元素。
delete(key):通過使用鍵值從字典中移除鍵值對應的值。
has(key):如果某個鍵值存在於這個字典中,則返回 true,否則返回 false。
get(key):使用鍵值查詢對應的值並返回。
clear():刪除字典中的所有元素。
size():返回字典包含的元素數量,與陣列的 length 屬性類似。
keys():將字典的所有鍵名以陣列的形式返回。
values():將字典包含的所有數值以陣列形式返回。

使用示例:

let dictionary = new Dictionary();

dictionary.set("Gandalf", "gandalf@email.com");
dictionary.set("John", "johnsnow@email.com");
dictionary.set("Tyrion", "tyrion@email.com");

console.log(dictionary.has("Gandalf"));
console.log(dictionary.size());

console.log(dictionary.keys());
console.log(dictionary.values());
console.log(dictionary.get("Tyrion"));

dictionary.delete("John");

console.log(dictionary.keys());
console.log(dictionary.values());

複製程式碼

提示:Web 端優先使用 ES6 以上的語法實現。


解析:

// 二、請實現一個字典
class Dictionary {
    constructor(){
        this.items = []
    }
    /**
     * 向字典中新增新元素
     * @param {*} key 新增的鍵名
     * @param {*} value 新增的值
     */
    set (key, value) {
        if ( !key ) return new Error('請指定插入的key')
        this.items[key] = value
    }

    /**
     * 查詢某個鍵值存在於這個字典
     * @param {*} key 查詢的鍵名
     * @return {Boolean} 是否存在
     */
    has (key) {
        return key in this.items 
    }

    /**
     * 通過使用鍵值從字典中移除鍵值對應的值
     * @param {*} key 移除的鍵名
     * @return {Boolean} 是否移除成功
     */
    delete (key) {
        if(!key || !this.has(key)) return false
        delete this.items[key]
        return true
    }

    /**
     * 使用鍵值查詢對應的值並返回
     * @param {*} key 查詢的鍵名
     * @return {*} 查詢的結果
     */
    get (key) {
        return this.has(key) ? this.items[key] : undefined
    }

    /**
     * 刪除字典中的所有元素
     */
    clear () {
        this.items = {}
    }

    /**
     * 將字典的所有鍵名以陣列的形式返回
     * @return {Array} 所有鍵名的陣列
     */
    keys () {
        return Object.keys(this.items)
    }

    /**
     * 將字典的所有鍵值以陣列的形式返回
     * @return {Array} 所有鍵值的陣列
     */
    values () {
        let result = []
        for(let k in this.items){
            if(this.has[k]){
                result.push(this.items[k])
            }
        }
        return result
    }

    /**
     * 返回字典包含的元素數量
     * @return {Number} 元素數量
     */
    size () {
        const values = this.values()
        return values.length
    }
}
複製程式碼

三、請實現一個雜湊表

put(key,value):向雜湊表增加/更新一個新的項。
remove(key):根據鍵值從雜湊表中移除值。
get(key):根據鍵值檢索到特定的值。
print():列印雜湊表中已儲存的值。

雜湊表內部的雜湊演算法:

function hashCode(key) {
    let hash = 0;
    for (let i = 0; i < key.length; i++) {
      hash += key.charCodeAt(i);
    }
    return hash % 37;
}
複製程式碼

使用示例:

const hashTable = new HashTable();

hashTable.put("Gandalf", "gandalf@email.com");
hashTable.put("John", "johnsnow@email.com");
hashTable.put("Tyrion", "tyrion@email.com");
hashTable.print();
複製程式碼

解析:

// 三、請實現一個雜湊表
class HashTable {
    constructor(){
        this.table = []
    }
    /**
     * 雜湊函式
     * @param {*} key 鍵名
     */
    hashCode(key) {
        let hash = 0;
        for (let i = 0; i < key.length; i++) {
          hash += key.charCodeAt(i);
        }
        return hash % 37;
    }
    /**
     * 向雜湊表增加/更新一個新的項
     * @param {*} key 新增的鍵名
     * @param {*} value 新增的值
     */
    put (key, value) {
        let position = this.hashCode(key)
        this.table[position] = value
    }

    /**
     * 根據鍵值從雜湊表中移除值
     * @param {*} key 移除的鍵名
     * @return {Boolean} 是否成功移除
     */
    remove (key) {
        if ( !key ) return false
        let position = this.hashCode(key)
        this.table[position] = undefined
        return true
    }

    /**
     * 根據鍵值檢索到特定的值
     * @param {*} key 查詢的鍵名
     * @return {*} 查詢的值
     */
    get (key) {
        let position = this.hashCode(key)
        return this.table[position]
    }

    /**
     * 列印雜湊表中已儲存的值
     * @return {*} 雜湊表的值
     */
    print () {
        return this.table
    }
}
複製程式碼

四、請利用之前已實現的連結串列,實現一個分離連結的雜湊表

分離連結是為雜湊表的每一個位置建立一個連結串列儲存元素的方式來處理雜湊表中的衝突:

separate-chaining.png

請實現新的雜湊表方法:

put(key,value):將 key 和value存在一個ValuePair物件中(即可定義一個包含keyvalue屬性的ValuePair` 類),並將其加入對應位置的連結串列中。

get(key):返回鍵值對應的值,沒有則返回 undefined

remove(key):從雜湊表中移除鍵值對應的元素。

print():列印雜湊表中已儲存的值。

提示:先找到元素儲存位置所對應的連結串列,再找到對應的值。

const hashTable = new HashTable();

hashTable.put("Gandalf", "gandalf@email.com");
hashTable.put("Tyrion", "tyrion@email.com");
hashTable.put("Aaron", "aaron@email.com");
hashTable.put("Ana", "ana@email.com");
hashTable.put("Mindy", "mindy@email.com");
hashTable.put("Paul", "paul@email.com");

hashTable.print();

console.log(hashTable.get("Tyrion"));
console.log(hashTable.get("Aaron"));

hashTable.remove("Tyrion");

hashTable.print();
複製程式碼

解析:

// 連結串列的實現程式碼省略 可以檢視之前的程式碼
let ValuePair = function (key, value){
    this.key = key
    this.value = value
    this.toString = function(){
        return `[${this.key} - ${this.value}]`
    }
}
class HashTable {
    constructor(){
        this.table = []
    }
    /**
     * 雜湊函式
     * @param {*} key 鍵名
     */
    hashCode(key) {
        let hash = 0;
        for (let i = 0; i < key.length; i++) {
          hash += key.charCodeAt(i);
        }
        return hash % 37;
    }
    /**
     * 向雜湊表增加/更新一個新的項
     * @param {*} key 新增的鍵名
     * @param {*} value 新增的值
     */
    put (key, value) {
        let position = this.hashCode(key)
        if(this.table[position] == undefined){
            this.table[position] = new LinkedList()
        }
        this.table[position].append(new ValuePair(key, value))
    }

    /**
     * 根據鍵值從雜湊表中移除值
     * @param {*} key 移除的鍵名
     * @return {Boolean} 是否成功移除
     */
    remove (key) {
        let position = this.hashCode(key)
        if ( !key || this.table[position] === undefined ) return false
        let current = this.table[position].getHead()
        while(current.next){
            if(current.element.key === key){
                this.table[position].remove(current.element)
                if(this.table[position].isEmpty){
                    this.table[position] = undefined
                }
                return true
            }
            current = current.next
        }
    }

    /**
     * 根據鍵值檢索到特定的值
     * @param {*} key 查詢的鍵名
     * @return {*} 查詢的值
     */
    get (key) {
        let position = this.hashCode(key)
        if(!key || this.table[position] === undefined) return undefined
        let current = this.table[position].getHead()
        while(current.next()){
            if(current.element.key === key){
                return current.element.value
            }
            current = current.next
        }
    }

    /**
     * 列印雜湊表中已儲存的值
     * @return {*} 雜湊表的值
     */
    print () {
        return this.table
    }
}

複製程式碼

五、實現一個線性探查的雜湊表

線性探查是解決雜湊表中衝突的另一種方法,當向表中某一個位置加入新元素的時候,如果索引為 index 的位置已經被佔據了,就嘗試 index+1 的位置。如果 index+1 的位置也被佔據,就嘗試 index+2,以此類推。

separate-chaining.png

請實現雜湊表:

put(key,value):將 keyvalue 存在一個 ValuePair 物件中(即可定義一個包含 keyvalue 屬性的 ValuePair 類)並分配到雜湊表。

get(key):返回鍵值對應的值,沒有則返回 undefined

remove(key):從雜湊表中移除鍵值對應的元素。

提示:移除一個元素,只需要將其賦值為 undefined。

使用示例:

const hashTable = new HashTable();

hashTable.put("Gandalf", "gandalf@email.com");
hashTable.put("Tyrion", "tyrion@email.com");
hashTable.put("Aaron", "aaron@email.com");
hashTable.put("Ana", "ana@email.com");
hashTable.put("Mindy", "mindy@email.com");
hashTable.put("Paul", "paul@email.com");

hashTable.print();

console.log(hashTable.get("Tyrion"));
console.log(hashTable.get("Aaron"));

hashTable.remove("Tyrion");

hashTable.print();
複製程式碼

解析:

let ValuePair = function (key, value){
    this.key = key
    this.value = value
    this.toString = function(){
        return `[${this.key} - ${this.value}]`
    }
}
class HashTable {
    constructor(){
        this.table = []
    }
    /**
     * 雜湊函式
     * @param {*} key 鍵名
     */
    hashCode(key) {
        let hash = 0;
        for (let i = 0; i < key.length; i++) {
          hash += key.charCodeAt(i);
        }
        return hash % 37;
    }
    /**
     * 向雜湊表增加/更新一個新的項
     * @param {*} key 新增的鍵名
     * @param {*} value 新增的值
     */
    put (key, value) {
        let position = this.hashCode(key)
        if(this.table[position] == undefined){
            this.table[position] = new ValuePair(key, value)
        }else{
            let index = ++position
            while(this.table[index] !== undefined){
                index ++
            }
            this.table[index] = new ValuePair(key, value)
        }
    }

    /**
     * 根據鍵值從雜湊表中移除值
     * @param {*} key 移除的鍵名
     * @return {Boolean} 是否成功移除
     */
    remove (key) {
        let position = this.hashCode(key)
        if( !key || this.table[position] === undefined ) return undefined
        if(this.table[position].key === key){
            this.table[index] = undefined
        }else{
            let index = ++position
            while(
                this.table[index] === undefined ||
                this.table[index].key !== key
            ){
                index ++
            }
            if(this.table[index].key === key){
                this.table[index] = undefined
            }
        }
    }

    /**
     * 根據鍵值檢索到特定的值
     * @param {*} key 查詢的鍵名
     * @return {*} 查詢的值
     */
    get (key) {
        let position = this.hashCode(key)
        if( !key || this.table[position] === undefined ) return undefined
        if(this.table[position].key === key){
            return this.table[position].value
        }else{
            let index = ++position
            while(
                this.table[index] === undefined ||
                this.table[index].key !== key
            ){
                index ++
            }
            if(this.table[index].key === key){
                return this.table[index].value
            }
        }
    }

    /**
     * 列印雜湊表中已儲存的值
     * @return {*} 雜湊表的值
     */
    print () {
        return this.table
    }
}
複製程式碼

下週預告

下週將練習 Tree 的題目。

相關文章