学习JavaScript数据结构与算法(五)— 集合

427 阅读4分钟

前言

本人是一个刚入行的菜鸡前端程序员,写这个文章的目的只是为了记录自己学习的笔记与成果,如有不足请大家多多指点。
数组(列表)、栈、队列和链表这些顺序数据结构我们在前面的文章已经一起学习过了。本篇文章我们一起来学习集合,这是一种不允许重复的顺序数据结构。

构建数据集合

集合是由一组无序且唯一(即不能重复)的项组成的。我们可以把集合想象成一个既没有重复元素,也没有顺序概念的数组。

创建集合类
class Set {
    constructor() {
        this.item = {};
    }
}

我们也可以使用数组来表示集合,此处我们用对象来表示。JavaScript 的对象不允许一个键指向两个不同的属性,也保证了集合里的元素都是唯一的。
接下来,需要声明一些集合可用的方法。

has(element) - 检验某个元素是否存在于集合中
has(element) {
    return element in items;
}

既然我们使用对象来存储集合,就可以使用JavaScript的 in 运算符来验证给定元素是否使 items 对象的属性。
这个方法还有更好的实现方式 。

has (element) {
    return Object.prototype.hasOwnProperty.call(this.items, element);
}

也可以写成

has (element) {
    return this.items.hasOwanProperty(element);
}
add(element) - 向集合添加一个元素
add(element) {
    if(!this.has(element)) {
        this.items[element] = element;
        return trur;
    }
    return false;
}

只有当集合中不存在这个元素的时候,我们才把它添加进来。

delete(element) - 从集合移除一个元素
delete(element) {
    if(this.has(element)) {
        delete this.items[element];
        return true;
    }
    return false;
}
clear() - 移除集合中所有的元素
clear() {
    this.items = {};
}
size() - 返回集合中有多少元素

此方法有三种实现的方式 。
第一种方式是使用一个 length 变量。每当使用 add或delete 方法时就控制它,就像在之前文章中使用 LinkedList、Stack、Queue 类一样。
第二种方式是使用 JavaScript 中 Object 类的一个内置方法。

size() {
    return object.keys(this.items).length;
}

使用 object.keys() 方法,它返回一个包含给定对象所有属性的数组,可以通过这个数组的length属性来返回items对象属性的个数。
第三种方法是手动提取itmes对象的每一个属性,记录属性的个数并返回个数。

size() {
    let count = 0;
    for(let key in this.items) {
        if(this.items.hasOwnProperty(key)) {
            count ++;
        }
    }
    return count;
}

迭代 items 对象的所有属性,检查它们是否是对象自身的属性(避免重复计数)。

values() - 返回集合中包含所有值的数组
values () {
    return this.object.values(this.items)
}

也可以这样做

values() {
    let values = [];
    for (let key in this.items) {
        if(this.items.hasOwnProperty(key)) {
            values.push(key)
        }
    }
    return values;
}
集合运算

我们可以对集合进行如下运算 。

  • 并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合
  • 交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合
  • 差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合
  • 子集:验证一个给定集合是否是另一集合的子集。
并集

对于给定的两个集合,返回一个包含两个集合中所有元素的新集合

union(otherSet) {
    const unionSet = new Set();
    this.values().forEach(value => unionSet.add(value));
    otherSet.values().forEach(value => unionSet.add(value));
}

同时,我们也可以这样做

union(otherSet) {
    const 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) {
    const intersectionSet = new Set();
    const values = this.values();
    for(let i = 0; i < values.length; i++) {
        if(otherSet.has(values[i])) {
            intersetionSet.add(values);
        }
    }
    return intersectionSet;
}
差集

对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合

differene(otherSet) {
    const differenceSet = new Set();
    this.values.forEach( value => {
        if(!otherSet.has(value)) {
            differenceSet.add(value)
        }
    })
    return differenceSet;
}
子集

验证一个给定集合是否是另一集合的子集。

isSubsetOf(otherSet) {
    if(this.size() > otherSet.size()) {
        return false;
    }
    let isSubset = true;
    this.values().every( value => {
        if(!otherSet.has(value)) {
            isSubset = false;
            return false;
        }
        return true;
    })
    return isSubSet;
}
ECMAScript2015 - Set 类

ECMAScript2015新增了 Set 类作为 JavaScript API 的一部分。
先看看原生的 Set 类怎么用

const set = new Set()
set.add(1);
console.log(set.values()); // @Iteraror
console.log(set.has(1)); // true
console.log(set.size) // 1

delete 方法删除 set 中的元素。

set.delete(1)

clear 方法会重置 set 数据结构,跟我们实现的功能一样。

小结

本篇主要是学习了集合,以及其基本运算。
下一篇,我们将介绍散列表和字典这两种非顺序数据结构。