【數據集合】並集、交集、差集、子集

什麼是集合?

  • 集合是由一組無序且惟一的項組成。你能夠把集合想象成一個既沒有重複元素,也沒有順序概念的數組。還有一個概念叫空集。空集就是不包括任何元素的集合。空集用{}表示。

建立集合類

  • ECMAScript2015介紹了Set類是Javascript API的一部分,咱們將基於ES2015的Set類來實現咱們本身的Set類。咱們也會實現一些原生ES2015沒有提供的集合運算,例如並集、交集和差集。
class Set {
    constructor() {
        this.items = {};
    }
}
複製代碼
  • 咱們使用對象而不是數組來表示集合(items)的緣由有兩個:
    • 第一個是由於在使用數組時,大部分方法的時間複雜度是O(n)。即咱們須要迭代整個數組直到找到要找的那個元素,在最壞的狀況下須要迭代數組的全部位置。若是數組有更多元素的話,所需的時間會更長。另外,數組是元素的一個有序集合。爲了保證元素排列有序,它會佔用更多的內存空間。
    • 另外一個就是由於Javascript的對象不容許一個鍵指向兩個不一樣屬性,也保證了集合裏的元素都是惟一的。

老規矩-來搞幾個Set類的方法

1. has() 檢驗某個元素是否存在於集合中javascript

has(element) {
    return Object.prototype.hasOwnProperty.call(this.items, element); // 返回一個代表對象是否具備特定屬性的布爾值
  }
複製代碼
  • Object原型有hasOwnProperty方法。該方法返回一個代表對象是否具備特定屬性的布爾值。
  • 也能夠使用this.items.hasOwnProperty(element),但這樣的話代碼檢查工具如(ESLint)會拋出一個錯誤。錯誤的緣由爲不是全部的對象都繼承了Object.prototype,甚至繼承的Object,prototype的對象上的hasOwnProperty方法也有可能會被覆蓋,致使代碼不能正常工做。
  • element in items也能夠。in運算符返回表示對象在原型鏈上是否有特定屬性的布爾值。

2. add() 向集合添加一個新元素java

add(element) {
    if (!this.has(element)) { // 檢查
      this.items[element] = element; // 若是不存在,就把element添加到集合中,返回true {element:element}
      return true;
    }
    return false; // 若是集合中有相同的元素,返回false便可
  }
複製代碼
  • 添加一個元素的時候,把它同時做爲鍵和值保存,由於這樣有利於查找該元素

3. delete()從集合中刪除一個元素算法

delete(element) {
    if (this.has(element)) { // 檢查,只能刪除存在與集合中的,(集合中都沒有,你刪誰去~)
      delete this.items[element]; // 從集合中移除element
      return true; // 返回true
    }
    return false; // 若是集合中沒有,返回false
  }
複製代碼
  • 既然咱們用對象來存儲集合的items對象,就能夠簡單的使用delete運算符從items對象中移除屬性。

4. size()返回集合中有多少元素數組

size() { // 返回集合中有多少元素
    return Object.keys(this.items).length; // 使用原生的內置方法,把對象的key轉化爲數組,再返回其length
  }
複製代碼

5. values()返回一個包含集合中全部全部值的數組瀏覽器

values() { // 返回一個包含集合中全部全部值的數組
    return Object.values(this.items); // 一樣使用原生方法(它是在ECMAscript2017中被添加進來的,目前只在如今瀏覽器中可用)
  }
複製代碼
  • 如今就實現了一個和ECMASscript2015中很是相似的Set類實現。

集合運算

  • 咱們能夠對集合進行以下運算:
    • 並集:對於給定的兩個集合,返回一個包含兩個集合中全部元素的新集合。
    • 交集:對於給定的兩個集合,返回一個包含兩個集合中共有元素的新集合。
    • 差集:對於給定的兩個集合,返回一個包含全部存在於第一個集合且不存在於第二個集合的元素的新集合。
    • 子集:驗證一個給定集合是不是另外一個集合的子集。

1.並集 數據結構

  • 該集合的定義是:x(元素)存在於A中,或x存在於B中。實現代碼以下:
union(otherSet) {
    const unionSet = new Set(); // 建立一個新的集合
    this.values().forEach(value => unionSet.add(value)); // 獲取第一個集合(當前的set類實例)全部的值並添加到新集合中
    otherSet.values().forEach(value => unionSet.add(value)); // 獲取第二個集合(傳入的set類實例)全部的值並添加到新集合中
    return unionSet; // 最後返回建立的新集合
  }
複製代碼

2.交集 工具

  • 由全部屬於集合A且屬於集合B的元素所組成的集合,叫作集合A與集合B的交集。實現代碼以下:
intersection(otherSet) {
    const intersectionSet = new Set(); // 建立一個新的集合
    const values = this.values(); // 獲取第一個集合(當前的set類實例)
    const otherValues = otherSet.values(); // 獲取第二個集合(傳入的set類實例)
    let biggerSet = values; // 假設當前集合的元素較多
    let smallerSet = otherValues; // 傳入集合的元素較少
    if (otherValues.length - values.length > 0) { // 比較兩個集合的元素個數
      biggerSet = otherValues; // 若是傳入集合的元素個數比當前集合的元素個數多的話,就交換較多的等於傳入的
      smallerSet = values; // 較少的等於當前集合
    }
    smallerSet.forEach(value => { // 最後迭代較少集合
      if (biggerSet.includes(value)) { // 若是較大的集合中也有這個元素
        intersectionSet.add(value); // 添加到新集合當中
      }
    });
    return intersectionSet; // 返回新集合
  }
複製代碼
  • 首先建立一個新的集合來存放intersection方法返回的結果。
  • 而後要獲取當前集合實例中的值和傳入集合中的值。
  • 先假設當前集合的元素較多, 傳入集合的元素較少。
  • 再比較兩個集合的元素個數,若是另外一個集合的元素個數比當前集合的元素個數多的話,就交換較多的等於傳入的,較少的等於當前集合。
  • 迭代較少集合,若是兩個集合中都有當前元素,把它插入到新集合當中
  • 最後返回新集合
  • 對兩個集合的元素個數作比較的目的是爲了儘可能最少循環迭代,更少的迭代次數意味這更少的過程消耗。

3. 差集學習

(沒找到更好的圖片,本身也懶的畫了,意思你們表達清除就好 哈~~)

  • 元素存在A中,且x不存在於B中。實現代碼以下:
difference(otherSet) {
    const differenceSet = new Set(); // 建立一個新的集合
    this.values().forEach(value => { // 當前集合的值轉換爲數組,並循環
      if (!otherSet.has(value)) { // 若是傳入的集合中沒有這個元素
        differenceSet.add(value); // 把它添加到新集合中
      }
    });
    return differenceSet; // 返回新的集合
  }
複製代碼

4. 子集優化

  • 集合A的每個元素,也須要存在於集合B中。實現代碼以下:
isSubsetOf(otherSet) {
    if (this.size() > otherSet.size()) { // 若是當前集合的元素比傳入集合多,那它確定不是傳入集合的子集,返回false
      return false;
    }
    let isSubset = true; // 先假設當前集合是傳入集合的子集
    // 迭代當前集合,當發現一個返回false,便再也不執行。
    this.values().every(value => {
      if (!otherSet.has(value)) { // 驗證迭代的元素是否也存在傳入集合中
        isSubset = false; // 只要有一個不是就改變變量
        return false; // 返回false 再也不往下執行
      }
      return true; // 若是都是,返回true
    });
    return isSubset; // 最後返回變量isSubset
  }
複製代碼
  • 首先要驗證當前集的元素比傳入集的小,若是當前集合的元素比傳入集合還多,那它確定不是傳入集合的子集,返回false
  • 而後先假設當前集合是傳入集合的子集。
  • 迭代當前集合的全部元素,驗證這些元素也存在於傳入集合中。
  • 若是有任何元素不存在於傳入集合中,就意味着它不是一個子集,返回false。
  • 若是全部元素都存在於傳入集合中,就返回true,isSubset變量不會改變。
  • 在子集方法中咱們用的是every方法。當咱們發現一個值不存在於傳入集合時,能夠中止迭代值,表示這不是一個子集。

ECMAScript2015——Set類

  • 先來看看原生的set類怎麼用:
const set = new Set();
set.add(1);
console.log(set.values()); // 輸出@Iterator
console.log(set.has(1)); // 輸出true
console.log(set.values()); // 輸出1
複製代碼
  • ES2015的set的values方法返回Iterator(一個包含鍵值對的迭代器對象),而不是值構成的數組。
  • 另外一個區別是,咱們實現的size方法返回set中存儲的值的個數,而ES2015的 Set則有一個size屬性。
  • 咱們實現的set類實現了並集、交集、差集、子集這些數學運算,然而ES6原生的Set並無這些功能。

ES6Set運算模擬ui

  • 首先建立兩個集合,等會兒會用到
const setA = new Set();
setA.add(1);
setA.add(2);
setA.add(3);

const setB = new Set();
setB.add(2);
setB.add(3);
setB.add(4);
複製代碼
  1. 模擬並集運算
const union = (set1, set2) => { // 普通模擬
  const unionAb = new Set();
  set1.forEach(value => unionAb.add(value));
  set2.forEach(value => unionAb.add(value));
  return unionAb;
};
console.log(union(setA, setB)); // [1,2,3,4]

console.log(new Set([...setA, ...setB]));  // 使用 擴展運算符模擬 [1,2,3,4]
複製代碼
  1. 模擬交集運算
const intersection = (set1, set2) => { // 普通模擬, 未經優化的
  const intersectionSet = new Set();
  set1.forEach(value => {
    if (set2.has(value)) {
      intersectionSet.add(value);
    }
  });
  return intersectionSet;
};
console.log(intersection(setA, setB)); // [2,3]

console.log(new Set([...setA].filter(x => setB.has(x)))); // 使用 擴展運算符模擬 [2,3]
複製代碼
  1. 模擬差集運算
const difference = (set1, set2) => { // 普通模擬
  const differenceSet = new Set();
  set1.forEach(value => {
    if (!set2.has(value)) {
      differenceSet.add(value);
    }
  });
  return differenceSet;
};
console.log(difference(setA, setB));// [1]

console.log(new Set([...setA].filter(x => !setB.has(x)))); // 使用 擴展運算符模擬 [1]
複製代碼

最後

好了,今天的隨手筆記完事兒了。本文內容全來自本人閱讀過《學習Javascript數據結構與算法》第七章後稍加整理而成。

相關文章
相關標籤/搜索