哈希表

在計算中, 一個哈希表(hash table 或hash map) 是一種實現關聯數組(associative array) 的抽象數據類型, 該結構能夠將 鍵映射到值。node

哈希表使用 哈希函數/散列函數 來計算一個值在數組或桶(buckets)中或槽(slots)中對應的索引,可以使用該索引找到所需的值。數組

理想狀況下,散列函數將爲每一個鍵分配給一個惟一的桶(bucket),可是大多數哈希表設計採用不完美的散列函數,這可能會致使"哈希衝突(hash collisions)",也就是散列函數爲多個鍵(key)生成了相同的索引,這種碰撞必須 以某種方式進行處理。數據結構

經過單獨的連接解決哈希衝突app

複雜度less

時間複雜度dom

獲取:O(1)函數

查詢:O(1)this

插入:O(1)編碼

刪除:O(1)spa

空間複雜度

O(n)

 

HashTable:

import LinkedList from '../linked-list/LinkedList';

// Hash table size directly affects on the number of collisions.
// The bigger the hash table size the less collisions you'll get.
// For demonstrating purposes hash table size is small to show how collisions
// are being handled.
//哈希表的size直接影響衝突的次數。
//哈希表size越大,衝突越少。
//由於示範的目的這裏size設置的很小以便展現如何處理衝突
const defaultHashTableSize = 32;

export default class HashTable {
  /**
   * @param {number} hashTableSize
   */
  constructor(hashTableSize = defaultHashTableSize) {//構造函數,hashTableSize是哈希表的size
    // Create hash table of certain size and fill each bucket with empty linked list.
    //建立肯定大小的哈希表,而後每一個bucket填充一個空的鏈表
    //先建立一個hashTableSize大小的數組,而後裏面每一個元素填充爲null,而後裏面每個元素都是一個鏈表
    this.buckets = Array(hashTableSize).fill(null).map(() => new LinkedList());

    // Just to keep track of all actual keys in a fast way.
    //keys屬性存放鍵,只是爲了快速追蹤全部實際key
    this.keys = {};
  }

  /**
   * Converts key string to hash number.
   *
   * @param {string} key
   * @return {number}
   */
  hash(key) {//將鍵字符串轉換成哈希值
    // For simplicity reasons we will just use character codes sum of all characters of the key
    // to calculate the hash.
    //爲了讓程序簡單一點,咱們只將鍵字符串的字符編碼求和後的總值計算哈希值
    // But you may also use more sophisticated approaches like polynomial string hash to reduce the
    // number of collisions:
    //你也可使用更復雜的方法好比多項式字符串哈希值來減少衝突的次數
    // hash = charCodeAt(0) * PRIME^(n-1) + charCodeAt(1) * PRIME^(n-2) + ... + charCodeAt(n-1)
    //
    // where charCodeAt(i) is the i-th character code of the key, n is the length of the key and
    // PRIME is just any prime number like 31.
    //charCodeAt(i)是第i個字符的編碼,n是key的長度,PRIME是自定義的任意數字
    const hash = Array.from(key).reduce(
      (hashAccumulator, keySymbol) => (hashAccumulator + keySymbol.charCodeAt(0)),
      0,
    );
    //將鍵字符串的每個字符的編碼累加求總和後的數做爲哈希值

    // Reduce hash number so it would fit hash table size.
    //hash和哈希表size取餘,減少hash讓它與之符合
    return hash % this.buckets.length;
  }

  /**
   * @param {string} key
   * @param {*} value
   */
  set(key, value) {//給哈希表裏存新的鍵值
    const keyHash = this.hash(key);//用hash處理key生成key的哈希值
    this.keys[key] = keyHash;//在keys對象裏存下key: keyHash
    const bucketLinkedList = this.buckets[keyHash];//keyHash對應的buckets鏈表
    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
    //調用鏈表的find方法查找看鏈表裏有沒有相同的key的節點
    if (!node) {//若是沒有,在鏈表的結尾插入新節點,節點的值是鍵值對的對象{key: key, value: value}
      // Insert new node.
      bucketLinkedList.append({ key, value });
    } else {//若是已經有相同的key了,就只修改這個節點的value
      // Update value of existing node.
      node.value.value = value;
    }
  }

  /**
   * @param {string} key
   * @return {*}
   */
  delete(key) {//刪除key對應的節點
    const keyHash = this.hash(key);//根據key計算出key哈希值
    delete this.keys[key];//從keys對象裏刪除key: keyHash
    const bucketLinkedList = this.buckets[keyHash];//keyHash對應的buckets鏈表
    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
    //調用鏈表的find方法查找鏈表裏相同的key的節點

    if (node) {//若是找到了,就將這個節點從鏈表裏刪除,並返回被刪的節點
      return bucketLinkedList.delete(node.value);
    }

    return null;//沒有對應key返回null
  }

  /**
   * @param {string} key
   * @return {*}
   */
  get(key) {//獲取key對應的value
    const bucketLinkedList = this.buckets[this.hash(key)];//keyHash對應的buckets鏈表
    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
    //調用鏈表的find方法查找鏈表裏相同的key的節點
    return node ? node.value.value : undefined;//找到了就返回value,沒找到就返回undefined
  }

  /**
   * @param {string} key
   * @return {boolean}
   */
  has(key) {//看哈希表裏有沒有對應的key
    return Object.hasOwnProperty.call(this.keys, key);
  }

  /**
   * @return {string[]}
   */
  getKeys() {//獲取全部key組成的數組
    return Object.keys(this.keys);
  }
}

note:

0.對於getKeys的Object.keys說明

getKeys() {
    return Object.keys(this.keys);
  }

Object.keys舉例:

// simple array
var arr = ['a', 'b', 'c'];
console.log(Object.keys(arr)); // console: ['0', '1', '2']

// array like object
var obj = { 0: 'a', 1: 'b', 2: 'c' };
console.log(Object.keys(obj)); // console: ['0', '1', '2']

// array like object with random key ordering
var anObj = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.keys(anObj)); // console: ['2', '7', '100']

 

1.hash表包括關鍵字以及拉鍊法所組成的鏈表,所包含的數據結構爲:

this.buckets = Array(hashTableSize).fill(null).map(() => new LinkedList());
this.keys = {};

 

2.對元素值的累加代碼爲:

const hash = Array.from(key).reduce(
      (hashAccumulator, keySymbol) => (hashAccumulator + keySymbol.charCodeAt(0)),
      0,
    );

3.拉鍊法鏈表的查找:

    const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });

4.找到之後,拉鍊法鏈表的修改,改關鍵字對應的哈希值:

 
if (!node) {  // Insert new node.  bucketLinkedList.append({ key, value });} else {  // Update value of existing node.  node.value.value = value;}
相關文章
相關標籤/搜索