用 JavaScript 學習資料結構和演算法:集合(Set)篇
什麼是集合(Set)?
集合是一個源自於數學理論中擁有不同元素的集合:
N = {0, 1, 2, 3, 4, 5, 6, …}
空集合:{}
其特性在於它是由一組無序且不重複的項目組成。你也可以想成是一個沒有重複元素和無順序的陣列。在這篇文章我們會介紹如何實作集合資料結構並使用 交集、聯集、差集等集合操作方式。
集合初體驗
事實上,在 ES6 中就有原生的 Set,在這邊我們試著使用 JavaScript 物件模仿 ES6 的 Set 設計集合資料結構(使用物件好處是物件 key 是唯一)。
The Set object lets you store unique values of any type, whether primitive values or object references.
建立集合類別和方法
1
2
3
4
5
6
7
8
9function Set() {
var items = {};
this.add(value) {};
this.remove(value) {};
this.has(value) {};
this.clear() {};
this.size() {};
this.values() {};
}- has(value):判斷是否元素在集合中,若有回傳 true,反之傳回 false
1
2
3
4this.has = function(value) {
return items.hasOwnProperty(value);
// 由於我們是使用 JavaScript 物件實作所以也可以使用 value in items
}- add(value):新增元素到集合
1
2
3
4
5
6
7this.add = function(value) {
if(!this.has(value)) {
items[value] = value;
return true;
}
return false;
}- remove(value):刪除元素
1
2
3
4
5
6
7this.remove = function(value) {
if(this.has(value)) {
delete items[value];
return true;
}
return false;
};- clear():移除集合所有元素
1
2
3this.clear = function() {
items = {};
}size():回傳集合元素數量
- 使用類似於 Linked List 的 length 變數計算法,當新增、刪除元素時順便增減長度
1
2
3this.size = function() {
return length;
}- 使用 JavaScript 內建 Object 類別的內建函數 keys
1
2
3this.size = function() {
return Object.keys(items).length;
}- 手動迭代判斷是否存在集合中並累加個數
1
2
3
4
5
6
7
8
9
10
11this.size = function() {
let count = 0;
// 特別注意在 JavaScript 中 for in 會一起把繼承於 Object 類別和物件自身的所有相關非相關資料結構的屬性一起迭代出來
for(let prop in items) {
// 判斷是否屬於 items 的屬性
if(items.hasOwnProperty(prop)) {
++count;
}
return count;
}
}values():回傳集合所有值,使用 JavaScript 內建 Object 類別的內建函數 keys 以陣列形式回傳
1
2
3this.values = function() {
return Object.keys(items);
}另外一種瀏覽器相容性較高的寫法
1
2
3
4
5
6
7this.value = function() {
let keys = [];
for(let key in items) {
keys.push(key);
}
return keys;
}使用集合類別
1
2
3
4
5
6
7
8
9
10
11
12const set = new Set();
set.add(12);
console.log(set.values());
console.log(set.has(12));
console.log(set.size());
set.add(12);
set.add(7);
set.remove(12);
set.add(1);
console.log(set.has(12));
console.log(set.values());
console.log(set.size());
集合操作
參考數學上的集合概念,我們可以針對集合進行以下操作:
聯集
對於給定兩集合,回傳一個包含兩個集合中所有元素的新集合1
2
3
4
5
6
7
8
9
10
11
12
13this.union = function(otherSet) {
// 首先建立代表聯集的新集合
let unionSet = new Set();
let values = this.values();
for(var i = 0; i < values.length; i++) {
unionSet.add(values[i]);
}
values = otherSet.values();
for(let j = 0; j < values.length; j++) {
unionSet.add(values[i]);
}
return unionSet;
}在 console 測試:
1
2
3
4
5
6
7
8
9
10
11
12
13let setA = new Set();
setA.add(1);
setA.add(4);
setA.add(2);
let setB = new Set();
setB.add(1);
setB.add(4);
setB.add(2);
setB.add(7);
let unionSet = setA.unique(setB);
console.log(unionSet);交集
對於給定兩集合,回傳一個包含兩個集合中共有元素的新集合1
2
3
4
5
6
7
8
9
10this.intersection = function(otherSet) {
let intersectionSet = new Set();
let values = this.values();
for(let i = 0; i < values.length; i++) {
if(otherSet.has(values[i])) {
intersectionSet.add(values[i]);
}
}
return intersectionSet;
}在 console 測試:
1
2
3
4
5
6
7
8
9
10
11
12
13let setA = new Set();
setA.add(1);
setA.add(4);
setA.add(2);
let setB = new Set();
setB.add(1);
setB.add(4);
setB.add(2);
setB.add(7);
let intersectionSet = setA.intersection(setB);
console.log(intersectionSet);差集
對於給定兩集合,回傳一個包含所有存在第一個集合但不存在於第二集合的元素集合1
2
3
4
5
6
7
8
9
10this.difference = function(otherSet) {
let differenceSet = new Set();
let values = this.values();
for(let i = 0; i < values; i++) {
if(!otherSet.has(values[i])) {
differenceSet.add(values[i]);
}
}
return differenceSet;
}在 console 測試:
1
2
3
4
5
6
7
8
9
10
11
12
13let setA = new Set();
setA.add(1);
setA.add(4);
setA.add(2);
let setB = new Set();
setB.add(1);
setB.add(4);
setB.add(2);
setB.add(7);
let differenceSet = setA.difference(setB);
console.log(differenceSet);子集
驗證給定集合是否為另一個集合的子集1
2
3
4
5
6
7
8
9
10
11
12
13this.subSet = function(otherSet) {
if(this.size() > otherSet.size()) {
return false;
} else {
let values = this.values();
for(let i = 0; i < values.length; i++) {
if(!otherSet.has(values[i])) {
return false;
}
return true;
}
}
}在 console 測試:
1
2
3
4
5
6
7
8
9
10
11
12
13let setA = new Set();
setA.add(1);
setA.add(4);
setA.add(2);
let setB = new Set();
setB.add(1);
setB.add(4);
setB.add(2);
setB.add(7);
let subSet = setA.subSet(setB);
console.log(subSet);
總結
在這篇文章中我們學會了:
- 什麼是集合(Set)?
- 建立集合類別和方法
- 集合操作(聯集、交集、差集、子集)
延伸閱讀
關於作者:
@kdchang 文藝型開發者,夢想是做出人們想用的產品和辦一所心目中理想的學校。A Starter & Maker. JavaScript, Python & Arduino/Android lover.:)
留言討論