Javascript的數據結構與算法(二)

1集合


1.1集合的實現

集合是由一組無序且惟一(即不能重複)的項組成的。這個數據結構使用了與有限集合相同 的數學概念,但應用在計算機科學的數據結構中。javascript

集合中經常使用方法列表:html

  • add(value):向集合中添加一個新的項。java

  • remove(value):從集合中移除一個值。git

  • has(value):若是在集合中,返回true,不然返回false。github

  • clear():清除集合中的全部項。算法

  • size():返回集合所包含元素的數量。數組

  • values():返回一個包含集合中全部值得數組。數據結構

  • union(otherSet):並集操做,返回一個包含兩個集合中全部元素的新集合。app

  • intersection(otherSet):交集操做,返回一個包含兩個集合中共有元素的新集合。函數

  • difference(otherSet):差集操做,返回一個包含左右存在於第一個集合而且不存在於第二個集合的元素的新集合。

  • subset(otherSet):子集操做,驗證一個給定集合是不是另外一個集合的子集,返回true和false。

function Set() {
        this.items = {};
    }
    Set.prototype = {
        constructor: Set,
        has: function(value) {
            return value in this.items;
        },
        add: function(value) {
            if (!this.has(value)) {
                this.items[value] = value;
                return true;
            }
            return false;
        },
        remove: function(value) {
            if (this.has(value)) {
                delete this.items[value];
                return true;
            }
            return false;
        },
        clear: function() {
            this.items = {};
        },
        size: function() {
            return Object.keys(this.items).length;
        },
        values: function() {
            return Object.keys(this.items);
        },
        union: function(otherSet) {
            var unionSet = new Set();
            var values = this.values();
            for (var i = 0; i < values.length; i++) {
                unionSet.add(values[i]);
            }
            values = otherSet.values();
            for (var i = 0; i < values.length; i++) {
                unionSet.add(values[i]);
            }
            return unionSet;
        },
        intersection: function(otherSet) {
            var intersectionSet = new Set();
            var values = this.values();
            for (var i = 0; i < values.length; i++) {
                if (otherSet.has(values[i])) {
                    intersectionSet.add(values[i]);
                }
            }
            return intersectionSet;
        },
        difference: function(otherSet) {
            var differenceSet = new Set();
            var values = this.values();
            for (var i = 0; i < values.length; i++) {
                if (!otherSet.has(values[i])) {
                    differenceSet.add(values[i]);
                }
            }
            return differenceSet;
        },
        subset: function(otherSet) {
            if (this.size() > otherSet.size()) {
                return false;
            } else {
                var values = this.values();
                for (var i = 0; i < values.length; i++) {
                    if (!otherSet.has(values[i])) {
                        return false;
                    }
                }
            }
            return true;
        },
    };

1.2集合的使用

var set = new Set();
    set.add(1);
    console.log(set.values());//["1"]
    console.log(set.has(1));//true
    console.log(set.size());//1
    set.add(2);
    console.log(set.values());//["1","2"]
    console.log(set.has(2));//true
    console.log(set.size());//2
    set.remove(2);
    console.log(set.values());//["1"]

交集、並集、子集、差集的使用。

//並集測試
    var setA = new Set();
    setA.add(1);
    setA.add(2);
    setA.add(3);
    var setB = new Set();
    setB.add(3);
    setB.add(4);
    setB.add(5);
    setB.add(6);
    var setAB = setA.union(setB);
    console.log(setAB.values()); // ["1", "2", "3", "4", "5", "6"]
    //交集測試
    setA = new Set();
    setA.add(1);
    setA.add(2);
    setA.add(3);
    setB = new Set();
    setB.add(2);
    setB.add(3);
    setB.add(4);
    var intersectionAB = setA.intersection(setB);
    console.log(intersectionAB.values()); // ["2", "3"]
    //差集側事故
    setA = new Set();
    setA.add(1);
    setA.add(2);
    setA.add(3);
    setB = new Set();
    setB.add(2);
    setB.add(3);
    setB.add(4);
    var differenceAB = setA.difference(setB);
    console.log(differenceAB.values()); //["1"]
    //子集測試
    setA = new Set();
    setA.add(1);
    setA.add(2);
    var setB = new Set();
    setB.add(1);
    setB.add(2);
    setB.add(3);
    setC = new Set();
    setC.add(2);
    setC.add(3);
    setC.add(4);
    console.log(setA.subset(setB)); //true
    console.log(setA.subset(setC)); //false

2字典和散列表


集合、字典和散列表能夠存儲不重複的值。在集合中,咱們感興趣的是每一個值自己,並把它 看成主要元素。在字典中,咱們用[鍵,值]的形式來存儲數據。在散列表中也是同樣(也是以[鍵, 值]對的形式來存儲數據)。

2.1字典

集合表示一組互不相同的元素(不重複的元素)。在字典中,存儲的是[鍵,值] 對,其中鍵名是用來查詢特定元素的。字典和集合很類似,集合以[值,值]的形式存儲元素,字 典則是以[鍵,值]的形式來存儲元素。字典也稱做映射。下面是字典須要實現的方法:

  • set(key,value): 向字典中添加新元素。

  • remove(key): 經過使用鍵值來從字典中語出鍵值對應的數據值。

  • has(key): 若是某個鍵值存在於這個字典中,不然返回true,反之則返回false。

  • get(key): 經過鍵值查詢特定的數值而且返回。

  • clear(): 將這個字典中的全部元素所有刪除。

  • size(): 返回字典中包含元素的數量。

  • keys(): 將字典所包含的全部鍵名以數組的形式返回。

  • values(): 將字典所包含的全部數值以數組的形式返回。

  • getItems(): 返回字典。

2.1.1字典的實現

function Dictionary() {
        this.items = {};
    }
    Dictionary.prototype = {
        constructor: Dictionary,
        has: function(key) {
            return key in this.items;
        },
        set: function(key, value) {
            this.items[key] = value;
        },
        remove: function(key) {
            if (this.has(key)) {
                delete this.items[key];
                return true;
            }
            return false;
        },
        get: function(key) {
            return this.has(key) ? this.items[key] : undefined;
        },
        values: function() {
            var values = [];
            for (var key in this.items) {
                if (this.has(key)) {
                    values.push(key);
                }
            }
            return values;
        },
        clear: function() {
            this.items = {};
        },
        size: function() {
            return Object.keys(this.items).length;
        },
        keys: function() {
            return Object.keys(this.items);
        },
        getItems: function() {
            return this.items;
        }
    };

2.1.2字典的基本使用

var dictionary = new Dictionary();
    console.log(dictionary.size()); //0
    dictionary.set('first', 'huang');
    dictionary.set('second', 'cheng');
    dictionary.set('third', 'du');
    console.log(dictionary.has('first')); //true
    console.log(dictionary.get('second')); //cheng
    console.log(dictionary.values()); // ["first", "second", "third"]
    dictionary.remove('second');
    console.log(dictionary.keys()); //["first", "third"]
    console.log(dictionary.getItems()); //{ first="huang",  third="du"}

2.2散列表

HashTable類,也叫HashMap類,是Dictionary類的一種散列表實現方式。散列算法的做用是儘量快地在數據結構中找到一個值。在以前的章節中,你已經知道若是 要在數據結構中得到一個值(使用get方法),須要遍歷整個數據結構來找到它。若是使用散列 函數,就知道值的具體位置,所以可以快速檢索到該值。散列函數的做用是給定一個鍵值,而後 返回值在表中的地址。

2.2.1基本版的散列表實現

在散列表中咱們經過散列函數來肯定鍵值對的關係。基本方法以下:

  • put(key,value): 向散列表增長一個新的選項(也多是更新散列表)。

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

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

對於HashTable類來講,咱們不須要像ArrayList類同樣從table數組中將位置也移除。由 於元素分佈於整個數組範圍內,一些位置會沒有任何元素佔據,並默認爲undefined值。咱們也 不能將位置自己從數組中移除(這會改變其餘元素的位置),不然,當下次須要得到或移除一個 元素的時候,這個元素會不在咱們用散列函數求出的位置上。

//lose-los散列函數
    function loseloseHashCode(key) {
        var hash = 0;
        for (var i = 0; i < key.length; i++) {
            hash += key.charCodeAt(i);
        }
        return hash % 37;
    }

    function HashTable() {
        this.table = [];
    }
    HashTable.prototype = {
        constructor: HashTable,
        put: function(key, value) {
            var position = loseloseHashCode(key);
            console.log(position + '- ' + key);
            this.table[position] = value;
        },
        get: function(key) {
            return this.table[loseloseHashCode(key)];
        },
        remove: function(key) {
            this.table[loseloseHashCode(key)] = undefined;
        }
    };

    var hash = new HashTable();
    hash.put('Gandalf', 'gandalf@email.com');
    hash.put('John', 'johnsnow@email.com');
    hash.put('Tyrion', 'tyrion@email.com');
    console.log(hash.get('Gandalf')); //gandalf@email.com
    console.log(hash.get('Loiane')); //undefined
    hash.remove('Gandalf');
    console.log(hash.get('Gandalf')); //undefined

有時候,一些鍵會有相同的散列值。不一樣的值在散列表中對應相同位置的時候,咱們稱其爲衝突。當這種狀況發生的時候就要去解決它。處理衝突有幾種方法:分離連接、線性探查和雙散列法,咱們會介紹前兩種方法。對於分離連接和線性探查來講,只須要重寫三個方法:put、get和remove。這三個方法在 每種技術實現中都是不一樣的。

2.2.2分離連接版散列表

爲了實現一個使用了分離連接的HashTable實例,咱們須要一個新的輔助類來表示將要加入LinkedList實例的元素。咱們管它叫ValuePair類。LinkedList的實現具體看javascript的數據結構與算法(一).html)。

  • 分離連接:分離連接法包括爲散列表的每個位置建立一個鏈表並將元素存儲在裏面。它是解決衝突的最簡單的方法,可是它在HashTable實例以外還須要額外的存儲空間。

function HashTable() {
        this.table = []; 
        //lose-los散列函數 
        function loseloseHashCode(key) {
            var hash = 0;
            for (var i = 0; i < key.length; i++) {
                hash += key.charCodeAt(i);
            }
            //console.log(key + " - " + (hash % 37));
            return hash % 37;
        }

        function ValuePair(key, value) {
            this.key = key;
            this.value = value;
            this.toString = function() {
                return '[' + this.key + ' - ' + this.value + ']';
            }
        }
        if ((typeof this.put !== 'function') && (typeof this.put !== 'string')) {
            HashTable.prototype.put = function(key, value) {
                var position = loseloseHashCode(key);
                if (this.table[position] === undefined) {
                    this.table[position] = new LinkedList();
                }
                this.table[position].append(new ValuePair(key, value));
            };
            HashTable.prototype.get = function(key) {
                var position = loseloseHashCode(key);
                if (this.table[position] !== undefined) {
                    var current = this.table[position].getHead();
                    while (current.next) {
                        if (current.element.key === key) {
                            return current.element.value;
                        }
                        current = current.next;
                    }
                    //第一個元素或者最後一個元素
                    if (current.element.key === key) {
                        return current.element.value;
                    }
                } else {
                    return undefined;
                }
            };
            HashTable.prototype.remove = function(key) {
                var position = loseloseHashCode(key);
                if (this.table[position] !== undefined) {
                    var 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;
                    }
                    //檢查是不是第一個或者最後一個
                    if (current.element.key === key) {
                        this.table[position].remove(current.element);
                        if (this.table[position].isEmpty()) {
                            this.table[position] = undefined;
                        }
                        return true;
                    }
                }
                return false;
            };
        }
    }
    var hash = new HashTable();
    hash.put('Gandalf', 'gandalf@email.com');
    hash.put('John', 'johnsnow@email.com');
    //下面兩個hash值相同
    hash.put('Aaron', 'huang@gmail.com');
    hash.put('Tyrion', 'tyrion@email.com');
    console.log(hash.get('Gandalf')); //gandalf@email.com
    console.log(hash.get('Loiane')); //undefined
    hash.remove('Gandalf');
    console.log(hash.get('Gandalf')); //undefined

2.2.3線性探查版散列表

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

function HashTable() {
        this.table = []; //lose-los散列函數 
        function loseloseHashCode(key) {
            var hash = 0;
            for (var i = 0; i < key.length; i++) {
                hash += key.charCodeAt(i);
            }
            //console.log(key + " - " + (hash % 37));
            return hash % 37;
        }

        function ValuePair(key, value) {
            this.key = key;
            this.value = value;
            this.toString = function() {
                return '[' + this.key + ' - ' + this.value + ']';
            }
        }
        if ((typeof this.put !== 'function') && (typeof this.put !== 'string')) {
            HashTable.prototype.put = function(key, value) {
                var position = loseloseHashCode(key);
                if (this.table[position] === undefined) {
                    this.table[position] = new ValuePair(key, value);
                } else {
                    var index = position + 1;
                    while (this.table[index] !== undefined) {
                        index++;
                    }
                    this.table[index] = new ValuePair(key, value);
                }
            };
            HashTable.prototype.get = function(key) {
                var position = loseloseHashCode(key);
                if (this.table[position] !== undefined) {
                    if (this.table[position].key === key) {
                        return this.table[position].value;
                    } else {
                        var index = position + 1;
                        //index不超過數組的長度
                        while (((this.table[index] === undefined) || (this.table[index].key !== key)) && (index < this.table.length)) {
                            index++;
                        }
                        if (this.table[index] && (this.table[index].key === key)) {
                            return this.table[index].value;
                        } else {
                            return undefined;
                        }
                    }
                } else {
                    return undefined;
                }
            };
            HashTable.prototype.remove = function(key) {
                var position = loseloseHashCode(key);
                if (this.table[position] !== undefined) {
                    if (this.table[position].key === key) {
                        this.table[position] = undefined;
                        return true;
                    } else {
                        var index = position + 1;
                        while ((this.table[index] === undefined) || (this.table[index].key !== key)) {
                            index++;
                        }
                        if (this.table[index].key === key) {
                            this.table[index] = undefined;
                            return true;
                        }
                    }
                } else {
                    return false;
                }
            };
        }
    }
    var hash = new HashTable();
    hash.put('Gandalf', 'gandalf@email.com');
    hash.put('John', 'johnsnow@email.com');
    //下面兩個hash值相同
    hash.put('Aaron', 'huang@gmail.com');
    hash.put('Tyrion', 'tyrion@email.com');
    console.log(hash.get('Gandalf')); //gandalf@email.com
    console.log(hash.get('Loiane')); //undefined
    console.log(hash.remove('Gandalf')); //true
    console.log(hash.get('Gandalf')); //undefined

源碼地址

Javascript的數據結構與算法(二)源碼

相關文章
相關標籤/搜索