iT邦幫忙

2021 iThome 鐵人賽

DAY 11
2

今天要來解一題以前數學課本第0章都會遇到也很常被我們跳躍式略過的東西。

在看這題之前我們先來了解一個名詞 Power sets
假設有一個集合 X ,我們將 X 中所有的子集合所形成的集合 ,稱它為冪集合(Power Set)

舉例 Power set的例子:
考慮集合 A={1,2,3}
則其 power set 為 P(A)={∅,{1},{2},{3},{1,2},{2,3},{1,3},{1,2,3}}
(集合內不需考慮順序,空集合是所有集合的子集。)

也就是LeetCode中給的例子

Input: nums = [1,2,3]
Output: [[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

在這裡先自己承認,看到這題題目時候就是看得懂,但實作上有點不知道要從哪個地方切入才對。今天就直接分享兩種方式,一種是迭代(iterative)的方式,另一種則是遞迴的方式。


迭代(iterative)的方式

就以nums = [1, 2, 3]來講解
我們要的結果是包在一個陣列中 subsets=[ ]

一開始,因為我們知道空集合是所有集合的子集,因此我們可以在一開始就先塞入一個空陣列,也就是在subsets加入一個[] → subsets=[ [ ] ]

然後我們跑到nums[0] = 1的位置上

我們抓出subsets的第一個元素,也就是subsets[0] = [ ],然後把nums[0] = 1,加入到subsets[0]=[ ],產生了[1]新的子集,並把他加到我們的subsets = [ [ ], [ 1 ]]

接著來到了nums[ 1 ] = 2

我們再繼續 一樣取出 subsets的元素,來分別加入2後,加入到subsets中,subsets = [ [ ], [ 1 ], [ 2 ], [ 1, 2]]

重複以上的方式來得到最後的結果 →[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

過程中,我們每次都要去迭代subsets,且subsets會隨著我們拜訪nums的長度而增長,可以發現是 2^0 → 2^1 → 2^2 ...,每次都是前一次的兩倍

時間複雜度方面,我們知道的subsets個數會是2^n(n為nums的長度),我們每次的操作會是在既有的subset加入新的元素,且所有的子集中最長的長度會是n,最短的長度會是0,取平均來看時間會在O(2^n * n)。同樣的空間也是O(2^n * n)。

接著當然就是直接來寫程式碼了

var subsets = function (nums) {
  const subsets = [[]];
  for (const element of nums) {
    const length = subsets.length;
    for (let i = 0; i < length; i++) {
      const currentSubset = subsets[i];
      subsets.push(currentSubset.concat(element));
    }
  }
  return subsets;
};

遞迴的方式

這裡主要用的一個觀念

例如 P([1, 2]) ⊂ P([1, 2, 3])
也就是P([1, 2, ...., x -1]) ⊂ P([1, 2, 3, x-1, x])

做法上很像是我們做完P([1, 2, ...., x -1])再加上一個([x])
不過自己會覺得這個方法如果沒有真的看過,比迭代的方法更不直覺一些,提供大家參考。

var subsets = function powerset(nums, index = null) {
  //一開始呼叫這個函式時我們不傳入任何的index,default為null
  // 假設nums = [1, 2, 3]
  if (index === null) {
    //如果index為null,把index擺在陣列的尾巴
    index = nums.length - 1;
  }
  if (index < 0) {
    // 當然如果陣列長度為0,扣掉1後一定小於1,這是基本的狀況
    return [[]];
  }
  //element = 3
  const element = nums[index];
  // 遞迴呼叫powerset
  // powerset(nums, 1)-> P([1, 2])
  const subsets = powerset(nums, index - 1);
  const length = subsets.length;
  for (let i = 0; i < length; i++) {
    // 仿照迭代的方式
    const currentSubset = subsets[i];
    subsets.push(currentSubset.concat(element));
  }
  return subsets;
}

明日題目預告: Merge Intervals


上一篇
Day 10 :Longest Palindromic Substring
下一篇
Day 12: Merge Intervals
系列文
30天用JavaScript刷題刷起來!30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
TD
iT邦新手 4 級 ‧ 2021-10-01 09:14:31

遞迴真的不太好理解,不過有看到另外一種解法,分享一下:https://leetcode.com/problems/subsets/discuss/184674/javascript

我要留言

立即登入留言