數組的子集

Given a set of distinct integers,  S , return all possible subsets.

Note: 

  • Elements in a subset must be in non-descending order.
  • The solution set must not contain duplicate subsets.

For example, 
If  S  =  [1,2,3] , a solution is:

[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]
本文總結了三種思路來求數組的子集集合。前兩種思路雖然都基於遞歸,但是出發點略有不同;後一種思路基於位運算,但是對於原數組的數量有限制。

1.基於DFS的遞歸

原數組中每一個元素在子集中有兩種狀態:要麼存在、要麼不存在。這樣構造子集的過程中每個元素就有兩種選擇方法:選擇、不選擇,因此可以構造一顆二叉樹來表示所有的選擇狀態:二叉樹中的第i+1層第0層無節點表示子集中加入或不加入第i個元素,左子樹表示加入,右子樹表示不加入。所有葉節點即爲所求子集。因此可以採用DFS的遞歸思想求得所有葉節點。 

代碼如下:

//S爲原數組,temp爲當前子集,level爲原數組中的元素下標亦爲二叉樹的層數,result爲所求子集集合
void subsets(vector<int> &S,vector<int> temp,int level,vector<vector<int> > &result)
  {
    //如果是葉子節點則加入到result中
    if(level == S.size())
    {
      result.push_back(temp);
      return;
    }
    //對於非葉子節點,不將當前元素加入到temp中
    subsets(S,temp,level + 1,result);
    //將元素加入到temp中
    temp.push_back(S[level]);
    subsets(S,temp,level + 1,result);
  }
2.基於同質的遞歸

只要我們能找到比原問題規模小卻同質的問題,都可以用遞歸解決。比如要求{1, 2, 3}的所有子集,可以先求{2, 3}的所有子集,{2, 3}的子集同時也是{1, 2, 3} 的子集,然後我們把{2, 3}的所有子集都加上元素1後(注意排序),又得到同樣數量的子集, 它們也是{1, 2, 3}的子集。這樣一來,我們就可以通過求{2, 3}的所有子集來求 {1, 2, 3}的所有子集了。即爲求1,2,3的子集,要先求2,3的子集,然後再把1加入到2,3的子集中去,典型的遞歸思路。代碼如下: 

vector<vector<int> > subsets(vector<int> &S,int idx,int n)
  {
    vector<vector<int> > result;
    if(idx == n)
    {
      vector<int> temp;
      result.push_back(temp);
    }
    else
    {
      vector<vector<int> > vec = subsets(S,idx + 1,n);
      int a = S[idx];
      for(int i = 0; i < vec.size();i++)
      {
        vector<int> v = vec[i];
        result.push_back(v);
        v.push_back(a);
        sort(v.begin(),v.end());
        result.push_back(v);
      }
    }
    return result;
  }
3.位運算

求子集問題就是求組合問題。數組中的n個數可以用n個二進制位表示,當某一位爲1表示選擇對應的數,爲0表示不選擇對應的數。

vector<vector<int> > subsets(vector<int> &S,int n)
  {
     //n個數有0~max-1即2^n中組合,1<<n表示2^n
    int max = 1<<n;
    vector<vector<int> >result;
    for(int i = 0;i < max;i++)
    {
      vector<int> temp;
      int idx = 0;
      int j = i;
      while(j > 0)
      {
        //判斷最後一位是否爲1,若爲1則將對應數加入到當前組合中
        if(j&1)
        {
          temp.push_back(S[idx]);
        }
        idx++;
        //判斷了這一位是否爲1後要右移
        j = j>>1;
      }
      //判斷完了一種組合,加入到結果集中
      result.push_back(temp);
    }
    return result;
  }
發佈了212 篇原創文章 · 獲贊 32 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章