學習 JavaScript 資料結構(三)——集合

Damonare發表於2016-11-30

前言

總括: 本文講解了資料結構中的[集合]概念,並使用javascript實現了集合。

人生多風雨,何處無險阻。

正文

集合簡介

在上一篇學習javascript資料結構(二)——連結串列中我們說了連結串列這種資料結構,但歸根結底,不論是棧,佇列亦或是連結串列都是線性結構。他們都是一種很規矩的資料結構,就像幼兒園的小朋友排隊乖乖的站在那不會動一樣。


學習 JavaScript 資料結構(三)——集合
幼兒園小朋友排隊


然而紛雜的資料並不會總是排隊站在那裡,幼兒園小朋友一旦下了課那可就撒歡了,亂糟糟一團。可我們的幼兒園老師卻能分辨出這些小朋友,因為啥?因為每個小朋友都在一個班裡,而且每一個小朋友都有自己的名字。老師自然很容易就找到小朋友了。


學習 JavaScript 資料結構(三)——集合
幼兒園小朋友下課


而本篇博文要說的集合正是一堆亂糟糟的資料,唯一的共同點是這些資料隸屬於同一個集合,看下百科給出的解釋:

由一個或多個元素所構成的叫做集合。

此處的元素就是小朋友了,他們所在的集合就是他們的班級。其實我們在高中的時候也接觸過集合的概念。那時候還沒有套路這個名詞,單純的歲月,那個年代對於集合是這麼解釋的:

集合是指具有某種特定性質的具體的或抽象的物件彙總成的集體,這些物件稱為該集合的元素。

然後又是這麼分類的:

  • 空集:{}
  • 有限集:{a,b,4}
  • 無限集:{1,2,3,4...}

不過,資料結構中集合是沒有無限集這個概念的。再然後那時候的集合還有這麼幾個特性:

  • 確定性:給定一個集合,任給一個元素,該元素或者屬於或者不屬於該集合,二者必居其一,不允許有模稜兩可的情況出現。
  • 互異性:一個集合中,任何兩個元素都認為是不相同的,即每個元素只能出現一次。有時需要對同一元素出現多次的情形進行刻畫,可以使用多重集,其中的元素允許出現多次。
  • 無序性:一個集合中,每個元素的地位都是相同的,元素之間是無序的。集合上可以定義序關係,定義了序關係後,元素之間就可以按照序關係排序。但就集合本身的特性而言,元素之間沒有必然的序。

想當年哥還是個數學學霸,如今卻淪落為了一個碼農......真是讓人唏噓啊。咳咳!接著說:

集合還有這幾種常見的基本操作:

  • 並集
  • 交集
  • 差集

而且我們資料結構中的集合基本是也符合高中時候的數學中的概念的。接下來我們是用ES5來實現集合,為啥子這麼說呢......因為在ES6中已經新給出了Set,Map等幾個集合類,更加方便快捷的鎖定鍵值對。

集合的建立

首先我們先宣告一個集合類:

function(){
    var items={};
}複製程式碼

接下來,我們需要給連結串列宣告一些方法:

  • add(value):向集合新增一個新的項
  • remove(value):從集合移除一個值
  • has(value):如果值在集合中,返回true,否則返回false
  • clear():移除集合中的所有項
  • size():返回集合所包含的元素數量,與陣列的length屬性相似
  • values():返回一個集合中所有值的陣列
  • union(setName):並集,返回包含兩個集合所有元素的新集合(元素不重複)
  • intersection(setName):交集,返回包含兩個集合中共有的元素的集合、
  • difference(setName):差集,返回包含所有存在本集合而不存在setName集合的元素的新集合
  • subset(setName):子集,驗證setName是否是本集合的子集

下面是Set類的完整程式碼:

function Set() {
    let items = {};
    this.add = function(value){
        if (!this.has(value)){
            items[value] = value;
            return true;
        }
        return false;
    };
    this.delete = function(value){
        if (this.has(value)){
            delete items[value];
            return true;
        }
        return false;
    };
    this.has = function(value){
        return items.hasOwnProperty(value);
        //return value in items;
    };

    this.clear = function(){
        items = {};
    };
    /**
     * Modern browsers function
     * IE9+, FF4+, Chrome5+, Opera12+, Safari5+
     * @returns {Number}
     */
    this.size = function(){
        return Object.keys(items).length;
    };

    /**
     * cross browser compatibility - legacy browsers
     * for modern browsers use size function
     * @returns {number}
     */
    this.sizeLegacy = function(){
        let count = 0;
        for(let key in items) {
            if(items.hasOwnProperty(key))
                ++count;
        }
        return count;
    };
    /**
     * Modern browsers function
     * IE9+, FF4+, Chrome5+, Opera12+, Safari5+
     * @returns {Array}
     */
    this.values = function(){
        let values = [];
        for (let i=0, keys=Object.keys(items); i<keys.length; i++) {
            values.push(items[keys[i]]);
        }
        return values;
    };
    this.valuesLegacy = function(){
        let values = [];
        for(let key in items) {
            if(items.hasOwnProperty(key)) {
                values.push(items[key]);
            }
        }
        return values;
    };
    this.getItems = function(){
      return items;
    };
    this.union = function(otherSet){
        let unionSet = new Set();
        let values = this.values();
        for (let i=0; i<values.length; i++){
            unionSet.add(values[i]);
        }
        values = otherSet.values();
        for (let i=0; i<values.length; i++){
            unionSet.add(values[i]);
        }
        return unionSet;
    };
    this.intersection = function(otherSet){
        let intersectionSet = new Set();
        let values = this.values();
        for (let i=0; i<values.length; i++){
            if (otherSet.has(values[i])){
                intersectionSet.add(values[i]);
            }
        }
        return intersectionSet;
    };
    this.difference = function(otherSet){
        let differenceSet = new Set();
        let values = this.values();
        for (let i=0; i<values.length; i++){
            if (!otherSet.has(values[i])){    
                differenceSet.add(values[i]);
            }
        }
        return differenceSet;
    };
    this.subset = function(otherSet){
        if (this.size() > otherSet.size()){
            return false;
        } else {
            let values = this.values();
            for (let i=0; i<values.length; i++){
                if (!otherSet.has(values[i])){  
                    return false;
                }
            }
            return true;
        }
    };
}複製程式碼

下面是ES6版本程式碼:

let Set2 = (function () {
    const items = new WeakMap();
    class Set2 {
        constructor () {
            items.set(this, {});
        }
        add(value){
            if (!this.has(value)){
                let items_ = items.get(this);
                items_[value] = value;
                return true;
            }
            return false;
        }
        delete(value){
            if (this.has(value)){
                let items_ = items.get(this);
                delete items_[value];
                return true;
            }
            return false;
        }
        has(value){
            let items_ = items.get(this);
            return items_.hasOwnProperty(value);
        }
        clear(){
            items.set(this, {});
        }
        size(){
            let items_ = items.get(this);
            return Object.keys(items_).length;
        }
        values(){
            let values = [];
            let items_ = items.get(this);
            for (let i=0, keys=Object.keys(items_); i<keys.length; i++) {
                values.push(items_[keys[i]]);
            }
            return values;
        }
        getItems(){
            return items.get(this);
        }
        union(otherSet){
            let unionSet = new Set();
            let values = this.values();
            for (let i=0; i<values.length; i++){
                unionSet.add(values[i]);
            }
            values = otherSet.values();
            for (let i=0; i<values.length; i++){
                unionSet.add(values[i]);
            }
            return unionSet;
        }
        intersection(otherSet){
            let intersectionSet = new Set();
            let values = this.values();
            for (let i=0; i<values.length; i++){
                if (otherSet.has(values[i])){
                    intersectionSet.add(values[i]);
                }
            }
            return intersectionSet;
        }
        difference(otherSet){
            let differenceSet = new Set();
            let values = this.values();
            for (let i=0; i<values.length; i++){
                if (!otherSet.has(values[i])){
                    differenceSet.add(values[i]);
                }
            }
            return differenceSet;
        };
        subset(otherSet){
            if (this.size() > otherSet.size()){
                return false;
            } else {
                let values = this.values();
                for (let i=0; i<values.length; i++){
                    if (!otherSet.has(values[i])){
                        return false;
                    }
                }
                return true;
            }
        };
    }
    return Set2;
})();複製程式碼

後記

集合是一種比較常見的資料結構,在JS中我們已經有了一種類似雜湊表的東西:Object(物件)。但現在我們所說的集合只是以[value,value]形式儲存資料,下一節我們使用[鍵,值]形式儲存資料,和本文集合的實現略有不同。敬請期待:[學習javascript資料結構(四)——雜湊表]

相關文章