数据结构和算法(六)之集合结构
几乎每种编程语言中,都有集合结构
集合比较常见的实现方式是哈希表,我们这里来实现一个封装的集合类
一. 集合介绍
集合的特点
- 集合通常是由一组无序的,不能重复的元素构成
- 和数字的集合名词比较相似,但是数学中的集合范围更大一些,也允许集合中的元素重复。
- 在计算机中,集合通常表示的结构中元素时不允许重复的。
- 看成一种特殊的数组
- 其实集合可以将它看成一种特殊的数组
- 特殊之处在于里面的元素没有顺序,也不能重复。
- 没有顺序意味着不能通过下标值进行访问,不能重复意味着相同的对象在集合中只会存在一份。
集合的实现
- 我们要像之前学习其他数据结构一样,来学习一下集合
- 最主要的学习方法就是封装一个属于自己的集合类,并且可以通过该类进行集合相关的操作。
- 2011年6月份发布的 ES5 中已经包含
Array
类 - 2015年6月份发布的 ES6 中已经包含了
Set
类,所以其实我们可以不封装,直接使用它。
- 2011年6月份发布的 ES5 中已经包含
二. 封装集合
创建集合类
-
我们先来封装一个
Set
类// 封装集合的构造函数 function Set() { // 使用一个对象来保存集合的元素 this.items = {} }
-
代码解析:
- 代码就是封装了一个集合的构造函数。
- 在集合中,添加了一个
items
属性,用于保存之后添加到集合中的元素。因为Object
的keys
本身就是一个集合。 - 后续我们给集合添加对应的操作方法。
操作的方法
-
集合有哪些常见的操作方法呢?
-
add(value)
:向集合添加一个新的项。 -
remove(value)
:从集合移除一个值。 -
has(value)
:如果值在集合中,返回true
,否则返回false
。 -
clear()
:移除集合中所有项。 -
size()
:返回集合所包含元素的数量。与数组的length
属性类似。 -
values()
:返回一个包含集合中所有值得数组。 -
union(otherSet)
:求两个集合的并集 -
intersection(otherSet)
:求两个集合的交集 -
difference(otherSet)
:求两个集合的差集 -
subset(otherSet)
:求两个集合的子集
-
-
has(value)
方法// 判断集合中是否有某个元素 Set.prototype.has = function (value) { return this.items.hasOwnProperty(value) }
-
add(value)
方法// 向集合中添加元素 Set.prototype.add = function (value) { // 1.判断集合中是否已经包含了该元素 if(this.has(value)) return false // 2.将元素添加到集合中 this.items[value] = value return true }
-
remove(value)
方法// 从集合中删除某个元素 Set.prototype.remove = function (value) { // 1.判断集合中是否包含该元素 if(!this.has(value)) return false // 2.包含该元素,那么将元素删除 delete this.items[value] return true }
-
clear()
方法// 清空集合中所有的元素 Set.prototype.clear = function () { this.items = {} }
-
size()
方法// 获取集合的大小 Set.prototype.size = function () { return Object.keys(this.items).length /* 考虑兼容性问题,使用下面的代码 var count = 0 for (var value in this.items) { if(this.items.hasOwnProperty(value)){ count++ } } return count */ }
-
values()
方法// 获取集合中所有的值 Set.prototype.values = function () { return Object.keys(this.items) /* 考虑兼容性问题,使用一下代码 var keys = [] for(var value in this.items){ keys.push(value) } return keys */ }
-
union(otherSer)
方法// 求两个集合的并集 Set.prototype.union = function(otherSet) { // 1.创建一个新歌集合 let unionSet = new Set() // 2.将当前集合(this)的所有 value ,添加到新集合(unionSet) for (let value of this.values()) { unionSet.add(value) } // 3.将 otherSet 集合的所有 value,添加到新集合 (unionSet) 中 for(let value of otherSet.values()) { unionSet.add(value) // add() 已经有重复判断 } return unionSet }
-
intersection(otherSet)
方法// 求两个集合的交集 Set.prototype.intersection = function (otherSet) { // 1.创建一个新集合 let intersectionSet = new Set() // 2.从当前集合中取出每一个 value,判断是否在 otherSet 集合中存在 for(let value of this.values()) { if (otherSet.has(value)) { intersectionSet.add(value) } } return intersectionSet }
-
difference(otherSet)
方法// 求两个集合的差集 Set.prototype.difference = function (otherSet) { // 1.创建一个新的集合 let differenceSet = new Set() // 2.从当前集合中取出一个 value ,判断是否在 otherSet 集合中存在 for(let value of this.values()) { if(!otherSet.has(value)) { differenceSet.add(value) } } // 3.从otherSet集合中取出一个 value,判断是否在 当前集合 中存在,不存在的即为差集 for (let value of otherSet.values()) { if(!this.has(value)) { differenceSet.add(value); } } return differenceSet }
-
subset(otherSet)
方法// 求是否为子集 Set.prototype.subset = function (otherSet) { // 从当前集合中取出每一个 value,判断是否在 otherSet 集合中存在,有不存在的返回 false // 遍历完所有的,返回 true for(let value of this.values()) { if(!otherSet.has(value)) { return false } } return true }
集合的使用
-
我们来简单使用和测试一下封装的集合类
// 测试和使用集合类 let set = new Set() // 添加元素 set.add(1) alert(set.values()) // 1 set.add(1) alert(set.values()) // 1 set.add(100) set.add(200) alert(set.values()) // 1,100,200 // 判断是否包含元素 alert(set.has(100)) // true // 删除元素 set.remove(100) alert(set.values()) // 1,200 // 获取集合的大小 alert(set.values()) // 2 set.clear() alert(set.values()) // 0 // ------------集合间操作-------------// const setA = new Set() setA.add('111') setA.add('222') setA.add('333') alert(setA.values()) // 111,222,333 const setB = new Set() setB.add('111') setB.add('222') setB.add('aaa') setB.add('bbb') alert(setB.values()) // 111,222,aaa,bbb // 求两个集合的并集 alert(setA.union(setB).values()) // 111,222,333,aaa,bbb // 求两个集合的交集 alert(setA.intersection(setB).values()) // 111,222 // 求两个集合的差集 alert(setA.difference(setB).values()) // 333,aaa,bbb // 求集合A是否是集合B的子集 alert(setA.subset(setB)) // false
三. 完整代码
// 封装集合的构造函数
function Set() {
// 使用一个对象来保存集合的元素
this.items = {}
// 集合的操作方法
// 判断集合中是否有某个元素
Set.prototype.has = function (value) {
return this.items.hasOwnProperty(value)
}
// 向集合中添加元素
Set.prototype.add = function (value) {
// 1.判断集合中是否已经包含了该元素
if (this.has(value)) return false
// 2.将元素添加到集合中
this.items[value] = value
return true
}
// 从集合中删除某个元素
Set.prototype.remove = function (value) {
// 1.判断集合中是否包含该元素
if (!this.has(value)) return false
// 2.包含该元素, 那么将元素删除
delete this.items[value]
return true
}
// 清空集合中所有的元素
Set.prototype.clear = function () {
this.items = {}
}
// 获取集合的大小
Set.prototype.size = function () {
return Object.keys(this.items).length
/*
考虑兼容性问题, 使用下面的代码
var count = 0
for (var value in this.items) {
if (this.items.hasOwnProperty(value)) {
count++
}
}
return count
*/
}
// 获取集合中所有的值
Set.prototype.values = function () {
return Object.keys(this.items)
/*
考虑兼容性问题, 使用下面的代码
var keys = []
for (var value in this.items) {
keys.push(value)
}
return keys
*/
}
// ---------- 集合间操作 ------------- //
// 求两个集合的并集
Set.prototype.union = function(otherSet) {
// 1.创建一个新歌集合
let unionSet = new Set()
// 2.将当前集合(this)的所有 value ,添加到新集合(unionSet)
for (let value of this.values()) {
unionSet.add(value)
}
// 3.将 otherSet 集合的所有 value,添加到新集合 (unionSet) 中
for(let value of otherSet.values()) {
unionSet.add(value) // add() 已经有重复判断
}
return unionSet
}
// 求两个集合的交集
Set.prototype.intersection = function (otherSet) {
// 1.创建一个新集合
let intersectionSet = new Set()
// 2.从当前集合中取出每一个 value,判断是否在 otherSet 集合中存在
for(let value of this.values()) {
if (otherSet.has(value)) {
intersectionSet.add(value)
}
}
return intersectionSet
}
// 求两个集合的差集
Set.prototype.difference = function (otherSet) {
// 1.创建一个新的集合
let differenceSet = new Set()
// 2.从当前集合中取出一个 value ,判断是否在 otherSet 集合中存在
for(let value of this.values()) {
if(!otherSet.has(value)) {
differenceSet.add(value)
}
}
// 3.从otherSet集合中取出一个 value,判断是否在 当前集合 中存在,不存在的即为差集
for (let value of otherSet.values()) {
if(!this.has(value)) {
differenceSet.add(value);
}
}
return differenceSet
}
// 求是否为子集
Set.prototype.subset = function (otherSet) {
// 从当前集合中取出每一个 value,判断是否在 otherSet 集合中存在,有不存在的返回 false
// 遍历完所有的,返回 true
for(let value of this.values()) {
if(!otherSet.has(value)) {
return false
}
}
return true
}
}