JavaScript数据机构——集合

来源:互联网 发布:阿里妈妈淘宝客收费吗 编辑:程序博客网 时间:2024/05/17 03:37

集合是由一组无序且唯一(即不能重复)的项组成的。这个数据结构使用了与有限集合相同的概念,但应用于计算机科学的数据结构中。

1,创建一个集合

以下是set类的骨架:

function Set(){    var items = {};}

这里有一个非常重要的细节,我们使用对象而不是数组来表示集合(items),当然也可以用数组来实现。JS的对象不允许一个键指向两个不同的属性,也保证了集合里的元素都是唯一的。

接下来,需要声明一些集合可用的方法(我们会尝试模拟与ECMAScript6实现相同的Set类)。

  • add(value):向集合添加一个新的项。
  • remove(value):从集合移除一个值。
  • has(value):如果值在集合中,返回true,否则返回false。
  • clear():移除集合中的所有项。
  • size():返回集合中包含元素的数量。与数组的length属性类似。
  • values():返回一个包含集合中所有值的数组。

1,1,has(value)方法

首先要实现的是has(value)方法。这是因为它会被add、remove等其他方法调用。下面看看它的实现:

this.has = function(value){    return value in items;}

既然我们使用对象来存储集合的值,就可以用JavaScript的in操作符来验证给定的值是否是items对象的属性。

但这个方法还有更好的实现方式,如下:

this.has = function(value){    return itmes.hasOwnProperty(value);}

所有JavaScript对象都有hasOwnProperty方法。这个方法返回一个表明对象是否具有特定属性的布尔值。

1.2,add方法

实现add方法:

this.add = function(value){    if(!this.has(value)){        items[value] = value;        return true;    }    return false;}

对于给定的value,可以检查它是否存在于集合中。如果不存在,就把value添加到集合中,返回true,表示添加了这个值。如果集合中已经存在这个值,就返回false,表示没有添加它。

tips: 添加一个值时,把它同时作为键和值保存,因为这样有利于查找这个值。

1.3,remove和clear方法

下面要实现remove方法:

this.remove = function(value){    if(this.has(value)){        delete items[value];        return true;    }    return false;}

在remove方法中,我们会验证给定的value中是否存在于集合中。如果存在,那么就移除value,返回true;否则返回false。

既然用对象来存储集合的items对象,就可以简单地使用delete操作符从items对象中移除属性。

使用Set类的示例代码如下:

var set = new Set();set.add(1);set.add(2);

如果想移除集合中的所有值,可以用clear方法:

this.clear = function(){    items = {};}

要重置items对象,需要做的只是把一个空对象重新赋值给它。我们也可以迭代集合,用remove方法依次移除所有的值,但既然有更简单的方法,那样做就显得太麻烦了。

1.4,size方法

下一个要实现的是size方法(返回集合中有多少项)。这个方法有三种实现方式。
第一种方法是使用一个length变量,每当使用add或者remove方法时控制它,就像链表类一样。

第二种方法是使用js内建的Object类的一个内建函数(ECMAScript5以上版本):

this.size = function(){    return Object.keys(items).length;}

JS的Object类有一个keys方法,它返回一个包含给定对象所有属性的数组。在这种情况下,可以使用这个数组的length属性来返回items对象的属性个数。只能在IE9以上版本和其他现代浏览器上使用。

第三种方法是手动提取items对象的每一个属性,记录属性的个数并返回这个数字。这个方法可以在任何浏览器上使用,和之前的代码是等价的:

this.sizeLegacy = function(){    var count = 0;    for(var prop in items){        if(items.hasOwnProperty(prop))        ++count;    }    return count;}

遍历items对象的所有属性,检查他们是否是对象自身的属性。如果是,就递增count变量的值,最后在方法结束时返回这个数字。

1.5,values方法

values方法也应用了相同的逻辑,提取items对象的所有属性,以数组的形式返回:

this.values = function(){    return Object.keys(items);}

以上代码只能在IE9及其他现代浏览器上使用。
如果想让代码在任何浏览器上使用,可以用与之前代码等价的下面这段代码:

this.valuesLegacy = function(){    var keys = [];    for(var key in items){        keys.push(key);    }    return keys;}

遍历items对象的所有属性,把他们添加到一个数组中,然后返回这个数组。

1.6,使用Set类

现在数据结构已经完成了,看看如何使用它吧。

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

2,集合操作

对集合可以进行如下操作。

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

2.1,并集

这里写图片描述

现在来实现Set类的union方法:

this.union = function(otherSet){    var unionSet = new Set();    var values = this.values();    for(var i = 0; i < values.length; i++){        unionSet.add(values[i]);    }    valuss = otherSet.values();    for(var i = 0; i < values.length; i++){        unionSet.add(values[i]);    }    return unionSet;}

首先要创建一个新的集合,代表两个集合的并集。接下来,获取第一个集合所有的值,遍历并全部添加到代表并集的集合中。然后对第二个集合做同样的事。最后返回结果。

测试一下上面的代码:

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 unionAB = setA.union(setB);console.log(unionAB.values());

输出为[‘1’,’2’,’3’,’4’,’5’,’6’]。注意元素3同时存在于集合A和集合B中,它在结果中只能出现一次。

2.2,交集

这里写图片描述

现在来实现Set类的intersection方法:

this.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;}

intersection方法需要找到当前Set实例中,所有也存在于给定Set实例中的元素。首先创建一个新的Set实例,这样就能能它返回共有的元素。接下来,遍历当前Set实例所有的值,验证它们是否也存在于otherSet实例。然后,如果这个值也存在于另一个Set实例中,就将其添加到创建intersectionSet遍历中,最后返回它。

做些测试:

var setA = new Set();setA.add(1);setA.add(2);setA.add(3);var setB = new Set();setB.add(2);setB.add(3);setB.add(4);setB.add(6);var intersectionAB = setA.intersection(setB);console.log(intersectionAB.values());

输出为[‘2’,’3’],因为2和3同时存在于两个集合中。

2.3,差集

这里写图片描述

实现Set类的difference方法:

this.difference = function(otherSet){    var differenceSet = new Set();    var values = this.values();    for(var i = 0;i < values.length; i++){        if(!otherSet.has(values[i])){            difference.add(values[i]);        }    }    return differenceSet;}

做些测试:

var setA = new Set();setA.add(1);setA.add(2);setA.add(3);var setB = new Set();setB.add(2);setB.add(3);setB.add(4);var differenceAB = setA.difference(setB);console.log(differenceAB.values());

输出为[‘1’],因为1是唯一一个仅存在于setA的元素。

2.4,子集

这里写图片描述

现在来实现Set类的subset方法:

this.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;    }}

首先需要验证的是当前Set实例的大小。如果当前实例中的元素比otherSet实例更多,它就不是一个子集。子集的元素个数需要小于或等于要比较的集合。

接下来要遍历集合中所有的元素,验证这样元素也存在于otherSet中。如果有任何元素不存在于otherSet中,就意味着他不是一个子集,返回false。如果所有元素都存在于otherSet中,就返回true。

检查一下上面的代码:

var setA = new Set();setA.add(1);setA.add(2);var setB = new Set();setB.add(1);setB.add(2);setB.add(3);var setC = new Set();setC.add(2);setC.add(3);setC.add(4);console.log(setA.subset(setB));console.log(setA.subset(setC));

我们有三个集合: setA是setB的子集(因此输出为true),然而setA不是setC的子集(setC只包含了setA中的2,而不包含1),因此输出为false。

0 0
原创粉丝点击