子集生成演算法屬於暴力法中一類非常重要的演算法.
給定乙個集合,請寫乙個演算法,得到其所有的子集.這裡假定該集合不存在重複的元素.
舉個栗子,給定集合[1, 2, 3]
, 你返回這樣一堆子集:
, [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3]
根據離散數學的知識,我們可以知道,乙個長度為n的集合的子集有2n個,集合的元素其實可以對應到乙個長度為n個二進位制數上.該二進位制數的第i位如果為1,表示取集合的第i個元素,否則表示不取.
舉個例子[1, 2, 3]
可以對應到乙個長度為3
的二進位制數上.111
表示[1, 2, 3]
,011
表示[2, 3]
.
二進位制數000...00
~111...11
,每個數都對應了乙個子集,首先這些數各不相同,其次這些數的總數為2n,所以這些數對應的子集正好對應了該集合的全部子集.
下面是這種方法的乙個簡易實現:
#include
#include
#include
using
namespace
std;
/** * 位向量法.
* 假定set的元素個數為n,那麼用乙個長度為n的bool陣列來模擬位向量,陣列的第i個元素為true,表示選擇set
* 的第i個元素, 否則表示不選擇.假定我們從二進位制的 000....00 (n個0) 開始數數, 一直數到 111...11
* (n個1)的話, 我們會發現我們數的每乙個數,都對應了set的乙個子集.利用這點,可以很快地寫出子集尋找演算法.
* 舉個例子,假定set為[1, 2, 3], 位向量001表示選擇第3個元素,即[3]是set的乙個子集.
* 位向量111表示[1, 2, 3]為set的乙個子集.
*//**
* @brief 尋找所有的子集
* @param[in] set 集合
* @param[in] mask 位標記
* @param[in] pos 當前已經處理到了第pos個位置了.
* @param[in, out] subsets 用於記錄結果的陣列.
*/void findsubsets(vector
& set, bool mask, int pos, vector
>& subsets)
subsets.push_back(subset);
return;
}mask[pos] = true;
findsubsets(set, mask, pos + 1, subsets);
mask[pos] = false;
findsubsets(set, mask, pos + 1, subsets);
}/**
* @brief 尋找set的所有子集.
* @param[in] set 包含了所有元素的集合
* @return 所有子集構成的陣列
*/vector
> subsets(vector
& set)
int main()
; auto subsets = subsets(nums);
for (auto &subset : subsets)
cout
<< endl;
}getchar();
}
這種方法比上面的位向量法稍微難以理解一點,不過做法也相當有意思.
很簡單,自然是遍歷集合的後n - k個元素,將每乙個元素新增到該子集後面即可.
很簡單,對於集合的第k + 1個元素,我們可以選擇新增進有前k個元素構成的子集,這樣可以生成2k個子集. 也可以選擇不新增,可以生成2k個子集,恰好是前k個元素構成的子集.
增量構造法結合了這兩點.
這種方法的簡單實現如下:
#include
#include
#include
using
namespace
std;
/** * 增量構造法
* 這種方法比位向量法稍微要複雜一些.事實上,我並不推薦這種方法,它並沒有比位向量法效率更高或者
* 更容易理解.
*//**
* @brief 尋找所有長度為subsetidx + 1的子集
* @param[in] set 要求子集的集合
* @param[in] setidx 用於表示集合的下標從0~setidx-1的元素即前setidx個元素在這輪迭代中不能再取.
* @param[in] subset 由集合的前setidx元素組合而成的長度為subsetidx的子集.
* @param[in] subsetidx 子集的長度.
* @param[in/out] subsets 用於記錄所有的子集的陣列
* @return 無
*/void findsubsets(vector
& set, int setidx, int* subset, int subsetidx, vector
>& subsets)
}/**
* @brief 尋找set的所有子集.
* @param[in] set 包含了所有元素的集合
* @return 所有子集構成的陣列
*/vector
> subsets(vector
& set)
int main()
; auto subsets = subsets(nums);
for (auto &subset : subsets)
cout
<< endl;
}getchar();
}
子集生成演算法
劉汝佳書上的內容 下文提到的集合 其元素預設為0 n 1 n 個 意思就是一次選乙個 放到 裡 include include include include include include include include include include include include includ...
子集生成演算法
1.增量構造法 基本思路 一次選出乙個元素放到集合中。使用前序遍歷的方法,因為a中元素個數不確定,每次遞迴呼叫都要輸出當前集合。演算法流程 另外,為了避免出現類似於按照和輸出兩次,可以使用定序的技巧,即規定集合a中所有元素的編號從小到大排列,對應於程式中的int s cur?a cur 1 1 0 ...
子集生成演算法
給定乙個集合 沒有重複元素 輸出所有子集。首先考慮1 n的所有子集 為了不出現和的情況,採用定序的方法。想象一棵解答樹,子節點的元素一定比父節點大。因為定序,解答樹葉子結點的深度不同。解答樹上的每乙個結點有個值,從根節點到葉子結點路徑上的結點值為乙個集合,每加乙個結點就輸出一次。如下 輸入n inc...