集合(set
)是一种包含不同元素的数据结构. 集合中的元素称为成员. 集合的两个最重要的特性是: 首先, 集合中的成员是无序的; 其次, 集合中不允许相同成员存在. 集合在计算机科学中扮演了非常重要的角色, 然而在很多编程语言中, 并不把集合当成一种数据类型. 当你想要创建一个数据结构, 用来保存一些独一无二的元素时, 比如一段文本中用到的单词, 集合就变得非常有用.
在ES6
中已加入集合Set
集合的定义、操作和属性
集合是由一组无序但彼此之间又有一定相关性的成员构成的, 每个成员在集合中只能出现一次.
集合的定义
下面是一些使用集合必须了解的定义.
- 不包含任何成员的集合称为 空集_, _全集 则是包含一切可能成员的集合.
- 如果两个集合的成员完全相同, 则称两个集合相等.
- 如果一个集合中所有的成员都属于另外一个集合, 则前一集合称为后一集合的子集.
对集合的操作
对集合的基本操作有下面几种:
- 并集 : 将两个集合中的成员进行合并, 得到一个新的集合.
- 交集 : 两个集合中共同存在的成员组成一个新的集合.
- 补集 : 属于一个集合而不属于另一个集合的成员组成的集合.
Set
类的实现
Set
类的实现基于数组, 数组用来存储数据.
window.log = console.log.bind(console)
class Set {
constructor() {
this._dataStore = [];
}
add(data) {
if(!this._dataStore.includes(data)) {
this._dataStore.push(data);
return true;
};
return false;
}
remove(data) {
const pos = this._dataStore.indexOf(data);
if(pos > -1) {
this._dataStore.splice(pos, 1);
return true;
};
return false;
}
show() {
return this._dataStore;
}
};
add()
方法: 因为集合中不能包含相同的元素, 所以, 使用add()
方法将数据存储到数组前, 先要确保数组中不存在该数据. 我们使用indexof()
检查新加入的元素在数组是否存在. 如果找到, 该方法返回该元素在数组中的位置; 如果没找到, 该方法返回-1
. 如果数组中还未包含该元素, add()
方法将新加入的元素保存在数组中并返回true
; 反之返回false
. 将add()
方法的返回值定义为布尔类型, 可以明确告诉我们是否将一个元素成功加入到了集合中. 这里使用ES6
中的includes()
也是可以的.
remove()
方法和add()
方法的工作原理类似. 首先检查待删除元素是否在数组中, 如何在, 则使用数组的splice()
方法删除该元素并返回true
; 反之返回false
, 表示集合中不存在这样的一个元素.
show()
显示集合中的成员.
测试程序:
const s = new Set();
s.add('a');
s.add('b');
s.add('c');
s.add('d');
s.add('e');
s.add('f');
log(s.show());
if(s.add('f')) {
log('f: 添加成功')
} else {
log('已存在f, 添加失败')
}
输出:
(6) ["a", "b", "c", "d", "e", "f"]
已存在f, 添加失败
更多集合操作
定义union()
、subset()
和difference()
方法会更有意思. union()
方法执行并集操作, 将两个集合合并成一个. 该方法首先将第一个集合里的成员悉数加入一个临时集合, 然后检查第二个集合中的成员, 看它们是否也同时属于第一个集合. 如果属于, 则跳过该成员, 否则就将该成员加入临时集合.
在定义union()
方法前, 先定一个辅助方法contains()
, 该方法检查一个成员是否属于该集合.
contains(data) {
return this._dataStore.includes(data);
}
// 并集
union(set) {
const tempSet = new Set();
this._dataStore.forEach(i => {
tempSet.add(i);
});
set._dataStore.forEach(i => {
if(!tempSet.contains(i)) {
tempSet._dataStore.push(i)
}
});
return tempSet;
}
执行程序:
const s = new Set();
s.add('a');
s.add('b');
s.add('c');
s.add('d');
s.add('e');
s.add('f');
log(s.show());
const s1 = new Set();
s1.add('a');
s1.add('f');
s1.add('g');
let res = new Set();
res = s.union(s1);
log(res.show());
//输出:
// (6) ["a", "b", "c", "d", "e", "f"]
// (7) ["a", "b", "c", "d", "e", "f", "g"]
使用intersect()
方法求两个集合的交集. 该方法定义起来相对简单. 每当发现第一个集合的成员也属于第二个集合时, 便将该成员加入一个心机和, 这个心机和即为方法的返回值.
// 交集
intersect(set) {
const tempSet = new Set();
this._dataStore.forEach(i => {
if(set.contains(i)) {
tempSet.add(i)
};
});
return tempSet;
}
下一个要定义的操作是subset()
判断子集. subset()
方法首先要确定该集合的长度是否小于带比较集合.
如果该集合比带比较集合还要大, 那么该集合肯定不会是待比较集合的一个子集.
当集合的长度小于待比较集合时, 再判断该集合内的成员是否都属于待比较集合. 如果有任意一个成员不属于待比较集合, 则返回false
, 程序终止. 如果一直比较完该集合的最后一个元素, 所有元素都属于待比较集合, 那么该集合就是待比较集合的一个子集, 该方法返回true
.
在判断每个元素是否属于待比较集合前, 该方法先使用size()
方法对比两个集合的大小.
size() {
return this._dataStore.length;
}
subSet(set) {
if(this.size() > set.size()) {
return false;
};
for(let i = 0; i < this._dataStore.length; i++) {
if(!set.contains(this._dataStore[i])) {
return false;
};
};
return true;
}
最后一个操作是difference()
补集, 该方法返回一个新集合, 该集合包含的是那些属于第一个集合但不属于第二个集合的成员.
difference(set) {
const tempSet = new Set();
this._dataStore.forEach(i => {
if(!set.contains(i)) {
tempSet.add(i);
};
});
return tempSet;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。