上一篇《TypeScript 数据结构与算法:链表》实现了 Typescript
中链表的数据结构与算法,本篇继续实现集合。
集合
是由一组无序且唯一(即不能重复)的项组成的。该数据结构使用了与有限集合相同的数学概念,但应用在计算机科学的数据结构中。
集合(Set
)之间的运算包括并集、交集、差集等,如下图所示:
数据结构
集合
一个集合中,应该有以下方法:
add(element)
:向集合添加一个新元素。delete(element)
:从集合移除一个元素。has(element)
:如果元素在集合中,返回true
,否则返回false
。clear()
:移除集合中的所有元素。size()
:返回集合所包含元素的数量。它与数组的length
属性类似。values()
:返回一个包含集合中所有值(元素)的数组。union(otherSet)
:返回与其他集合的并集。intersection(otherSet)
:返回与其他集合的交集。difference(otherSet)
:返回与其他集合的差集。isSubsetOf(otherSet)
:返回是否为其他集合的子集。
在 ES2015
后,已经存在原生的 Set
数据结构了,但是没有上面所述的所有方法,所以以原生 Set
为基础来构造一个 CustomSet
:
export default class CustomSet<T> {
private items: Set<T>;
constructor(array?: T[]) {
this.items = new Set(array);
}
/**
* @description: 向集合添加一个新元素。
* @param {T} element
* @return {boolean} 是否添加成功
*/
add(element: T): boolean {
if (!this.has(element)) {
this.items.add(element);
return true;
}
return false;
}
/**
* @description: 从集合移除一个元素。
* @param {T} element
* @return {boolean} 是否删除成功
*/
delete(element: T): boolean {
if (this.has(element)) {
return this.items.delete(element);
}
return false;
}
/**
* @description: 如果元素在集合中,返回 true,否则返回 false。
* @param {T} element
* @return {boolean}
*/
has(element: T): boolean {
return this.items.has(element);
}
/**
* @description: 返回一个包含集合中所有值(元素)的数组。
* @return {Array<T>}
*/
values(): T[] {
return [...this.items];
}
/**
* @description: 并集
* @param {CustomSet} otherSet
* @return {CustomSet}
*/
union(otherSet: CustomSet<T>): CustomSet<T> {
const unionSet = new CustomSet<T>();
// 迭代两个集合,把元素都add进来
this.values().forEach((value) => unionSet.add(value));
otherSet.values().forEach((value) => unionSet.add(value));
return unionSet;
}
/**
* @description: 交集
* @param {CustomSet} otherSet
* @return {CustomSet}
*/
intersection(otherSet: CustomSet<T>): CustomSet<T> {
const intersectionSet = new CustomSet<T>();
// 在当前集合中过滤掉otherSet中不存在的元素
this.values()
.filter((v) => otherSet.has(v))
.forEach((v) => {
intersectionSet.add(v);
});
return intersectionSet;
}
/**
* @description: 差集
* @param {CustomSet} otherSet
* @return {CustomSet}
*/
difference(otherSet: CustomSet<T>): CustomSet<T> {
const differenceSet = new CustomSet<T>();
// 在当前集合中过滤掉otherSet中也存在的元素
this.values()
.filter((v) => !otherSet.has(v))
.forEach((v) => {
differenceSet.add(v);
});
return differenceSet;
}
/**
* @description: 是否为子集
* @param {CustomSet} otherSet
* @return {boolean}
*/
isSubsetOf(otherSet: CustomSet<T>): boolean {
if (this.size() > otherSet.size()) {
return false;
}
let isSubset = true;
// 判据:当前集合的所有元素在otherSet中都存在
this.values().forEach((value) => {
if (!otherSet.has(value)) {
isSubset = false;
}
});
return isSubset;
}
/**
* @description: 是否为空
* @return {boolean}
*/
isEmpty(): boolean {
return this.size() === 0;
}
/**
* @description: 集合的元素数
* @return {number}
*/
size(): number {
return this.items.size;
}
/**
* @description: 清空集合
*/
clear() {
this.items = new Set();
}
/**
* @description: 替换原生toString
* @return {string}
*/
toString(): string {
return `${this.values()}`;
}
}
多重集
集合数据结构不允许存在重复的元素。但是,在数学中有一个叫作 多重集
的概念,允许我们向集合中插入重复元素。多重集(MultiSet
)在计算集合中元素的出现次数时很有用。
多重集有一些方法和普通集合有区别:
count(element)
:返回集合中指定元素的数目。set(element, count)
:将集合中指定元素设为count
个。add(element, count)
:向集合添加count
个指定元素。remove(element, count)
:在集合删除count
个指定元素。delete(element)
:从集合移除所有指定元素。dimension()
:返回集合所包含元素的种类数。它与数组的length
属性类似。
具体实现如下:
export default class MultiSet<T> {
private items: Map<T, number>;
constructor() {
this.items = new Map();
}
/**
* @description: 如果元素在集合中,返回 true,否则返回 false。
* @param {T} element
* @return {boolean}
*/
has(element: T): boolean {
return this.items.has(element);
}
/**
* @description: 返回元素的维数,也就是元素的种类数
* @return {number}
*/
dimension(): number {
return this.items.size;
}
/**
* @description: 返回该元素的个数
* @param {T} element
* @return {number}
*/
count(element: T): number {
return this.items.get(element) ?? 0;
}
/**
* @description: 删除所有的该元素
* @param {T} element
* @return {boolean}
*/
delete(element: T): boolean {
return this.items.delete(element);
}
/**
* @description: 设置该元素的个数
* @param {T} element
* @param {number} count
* @return {boolean}
*/
set(element: T, count: number): boolean {
if (count <= 0) {
return this.delete(element);
}
this.items.set(element, count);
return true;
}
/**
* @description: 给该元素添加count个
* @param {T} element
* @param {number} count
* @return {boolean}
*/
add(element: T, count: number = 1): boolean {
let newCount = this.count(element) + count;
return this.set(element, newCount);
}
/**
* @description: 给该元素移除count个
* @param {T} element
* @param {number} count
* @return {boolean}
*/
remove(element: T, count: number = 1): boolean {
return this.add(element, -count);
}
/**
* @description: 是否为空
* @return {boolean}
*/
isEmpty(): boolean {
return this.dimension() === 0;
}
// 👇 以下方法与CustomSet的同名方法实现一致
/**
* @description: 返回一个包含集合中所有值(元素)的数组。
* @return {Array<T>}
*/
values(): T[] {
return Array.from(this.items.entries()).reduce((acc, cur) => {
const [key, value] = cur;
for (let i = 0; i < value; i++) {
acc.push(key);
}
return acc;
}, []);
}
/**
* @description: 集合的元素数
* @return {number}
*/
size(): number {
return this.values().length;
}
/**
* @description: 清空集合
*/
clear() {
this.items = new Map();
}
/**
* @description: 并集
* @param {MultiSet} otherSet
* @return {MultiSet}
*/
union(otherSet: MultiSet<T>): MultiSet<T> {
const unionSet = new MultiSet<T>();
// 迭代两个集合,把元素都add进来
this.values().forEach((value) => unionSet.add(value));
otherSet.values().forEach((value) => unionSet.add(value));
return unionSet;
}
/**
* @description: 交集
* @param {MultiSet} otherSet
* @return {MultiSet}
*/
intersection(otherSet: MultiSet<T>): MultiSet<T> {
const intersectionSet = new MultiSet<T>();
// 在当前集合中过滤掉otherSet中不存在的元素
this.values()
.filter((v) => otherSet.has(v))
.forEach((v) => {
intersectionSet.add(v);
});
return intersectionSet;
}
/**
* @description: 差集
* @param {MultiSet} otherSet
* @return {MultiSet}
*/
difference(otherSet: MultiSet<T>): MultiSet<T> {
const differenceSet = new MultiSet<T>();
// 在当前集合中过滤掉otherSet中也存在的元素
this.values()
.filter((v) => !otherSet.has(v))
.forEach((v) => {
differenceSet.add(v);
});
return differenceSet;
}
/**
* @description: 是否为子集
* @param {MultiSet} otherSet
* @return {boolean}
*/
isSubsetOf(otherSet: MultiSet<T>): boolean {
if (this.size() > otherSet.size()) {
return false;
}
let isSubset = true;
// 判据:当前集合的所有元素在otherSet中都存在
this.values().forEach((value) => {
if (!otherSet.has(value)) {
isSubset = false;
}
});
return isSubset;
}
/**
* @description: 替换原生toString
* @return {string}
*/
toString(): string {
return `${this.values()}`;
}
}
下一篇来分析字典和散列表。
前端记事本,不定期更新,欢迎关注!