全子集的c++實現,二進位制,DFS,BFS三種方法
阿新 • • 發佈:2021-12-24
目錄
一、什麼是全子集
子集概念
子集是一個數學概念:如果集合A的任意一個元素都是集合B的元素,那麼集合A稱為集合B的子集
全子集概念
全,顧名思義就是一個集合的所有子集
應用
全子集是DFS的經典應用
但也可以用BFS等其它演算法解決
二、全子集有哪些
比如有一個數組 [1,2,3,4]
全子集如下
[] [1] [2] [3] [4] [1,2] [1,3] [1,4] [2,3] [2,4] [3,4] [1,2,3] [1,2,4] [1,3,4] [2,3,4] [1,2,3,4]
三、全子集分類
3.1 無重複數字
無重複數字,比如數組裡 都是唯一的數字,如[1,3,5,6]之類的
3.2 有重複數字
有重複數字,比如[1,2,2,3]之類的
四、二進位制實現全子集
原理
比如[1,2,3,4]
0 0 0 0 = [] 0 0 0 1 = [4] 0 0 1 0 = [3] 0 0 1 1 = [3,4] 0 1 0 0 = [2] 0 1 0 1 = [2,4] 0 1 1 0 = [2,3] 0 1 1 1 = [2,3,4] 1 0 0 0 = [1] 1 0 0 1 = [1,4] 1 0 1 0 = [1,3] 1 0 1 1 = [1,3,4] 1 1 0 0 = [1,2] 1 1 0 1 = [1,2,4] 1 1 1 0 = [1,2,3] 1 1 1 1 = [1,2,3,4]
從N個0到N個1(n表示陣列的元素個數)
恰好是全子集的所有情況,即2^n次方
但只能解決無重複元素的情況
程式碼
//二進位制的方法 vector<vector<int>>binarySubset(vector<int>& vec) { vector<vector<int>>result; result.push_back(vector<int>());//首先有一個空子集 if (vec.size() == 0)return result;//空集的子集也是空集 //1 0-1 2^1 //2 00-11 2^2 //3 000-111 2^3 //4 0000-1111 2^4 int length = 1; //2的n次方是出現情況的次數 for (int i = 1; i <= vec.size(); i++)length = length * 2; for (int i = 1; i < length; i++) { int tempNum = i; vector<int>tempVec; for (int j = 0; j < vec.size(); j++) { if ((tempNum & 1) == 1) tempVec.push_back(vec[j]); tempNum = tempNum >> 1; } result.push_back(tempVec); } return result; }
五、DFS實現全子集
無重複元素
此時 dfs順序應該為:
1
12
123
1234
124(回到123,把3扔出去,再加入4,與123同級)
13
134
14
2
23
234
24
3
34
4
vector<vector<int>> DFSSubset(vector<int>& nums) {
vector<vector<int>> result;
result.push_back(vector<int>());//空集,任何時間都存在
DFSFunc(result, nums, 0);
return result;
}
void DFSFunc(vector<vector<int>>& result, vector<int>& nums, int startPos, vector<int>prefix = vector<int>()) {
if (startPos == nums.size())return;
for (size_t i = startPos; i < nums.size(); i++)
{
vector<int>tempVec;
//先把字首加進去
for (size_t j = 0; j < prefix.size(); j++)tempVec.push_back(prefix[j]);
//再把當前數字加進去
tempVec.push_back(nums[i]);
result.push_back(tempVec);
//通過當前數字進行遞迴
//先把當前數字作為字首,再把當前數字遞迴後移除
prefix.push_back(nums[i]);
DFSFunc(result, nums, i + 1, prefix);
prefix.pop_back();
}
}
有重複元素情況
有重複元素,如:[1,2,2,3]
需要對情況進行篩選
如果執行上面的程式碼,會得到下面的結果
[]
[1]
[1,2]
[1,2,2]
[1,2,2,3]
[1,2,3]
[1,2]
[1,2,3]
[1,3]
[2]
[2,2]
[2,2,3]
[2,3]
[2]
[2,3]
[3]
可以發現,有重複的情況發生
如果想去掉重複,明顯有兩種方法
1. 全部生成後刪除重複資料
這種情況太暴力,就不寫例子了
2. 根據條件跳過重複的資料
首先對陣列進行排序,保證陣列是有序的
此時 重複的資料是相鄰的
只需要判斷上一個資料和當前資料是否相同
相同時 就是重複的,可以跳過
但當前資料作為第一個資料時,仍然需要加入進去
//存在重複子集的情況
vector<vector<int>> DFSSubset2(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> result;
result.push_back(vector<int>());
DFSFunc2(result, nums, 0);
return result;
}
void DFSFunc2(vector<vector<int>>& result, vector<int>& nums, int startPos, vector<int>prefix = vector<int>()) {
if (startPos == nums.size())return;
for (size_t i = startPos; i < nums.size(); i++)
{
//若i為startPos時,仍然需要把當前值新增進去
if (i != 0 && nums[i] == nums[i - 1] && i != startPos) {
continue;
}
vector<int>tempVec;
//先把字首加進去
for (size_t j = 0; j < prefix.size(); j++)tempVec.push_back(prefix[j]);
//再把當前數字加進去
tempVec.push_back(nums[i]);
result.push_back(tempVec);
//通過當前數字進行遞迴
//先把當前數字作為字首,再把當前數字遞迴後移除
prefix.push_back(nums[i]);
DFSFunc2(result, nums, i + 1, prefix);
prefix.pop_back();
}
}
六、BFS
無重複元素
比如[1,2,3,4] ,無重複元素的情況
此時順序應該為
[1,2,3,4]
入:進入佇列
出:進入結果
入:[1,2,3,4]
出:[1],入[2,3,4](字首為1)
出:[2],入[3,4](字首為2)
出:[3],入[4](字首為3)
出:[4],入空
此時:
結果:[[1],[2],[3],[4]]
佇列:[[2,3,4],[3,4],[4]](字首分別為1,2,3)
出:2,入[3,4](字首為12)
出:3,入[4](字首為13)
出:4,入空
出:3,入[4](字首為23)
出:4,入空
出:4,入空
此時:
結果:[[1],[2],[3],[4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4]]
佇列:[[3,4],[4],[4]](字首分別為12,13,23)
出:3,入[4](字首為123)
出:4,入空
出:4,入空
出:4,入空
此時:
結果:[[1],[2],[3],[4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4],[1,2,3],[1,2,4],[1,3,4],[2,3,4]]
佇列:[[4]](字首分別為123)
出:4,入空
此時:
結果:[[1],[2],[3],[4],[1,2],[1,3],[1,4],[2,3],[2,4],[3,4],[1,2,3],[1,2,4],[1,3,4],[2,3,4],[1,2,3,4]]
佇列:[]
程式碼如下:
//BFS無重複子集
vector<vector<int>> BFSSubset(vector<int>& nums) {
vector<vector<int>>result;
result.push_back(vector<int>());
if (nums.size() == 0)return result;
sort(nums.begin(), nums.end());
queue<pair<pair<int,int>, vector<int>>>que;
//迴圈存入值和字首陣列
for (size_t i = 0; i < nums.size(); i++) {
pair<int, int>num(i,nums[i]);//數字不是單純的數字,還要帶著自己的下標
que.push(pair<pair<int, int>, vector<int>>(num, vector<int>()));
}
//迴圈出隊
while (que.size())
{
auto currentData = que.front();
que.pop();
//因為沒有重複,也可以使用find在nums裡查找出現的下標
size_t beginPos = currentData.first.first;
for (size_t i = beginPos + 1; i < nums.size(); i++)
{
int pushValue = nums[i];
vector<int>prefix(currentData.second);
prefix.push_back(currentData.first.second);//增加一個字首
que.push(pair<pair<int, int>, vector<int>>(pair<int, int>(i,pushValue), vector<int>(prefix)));
}
vector<int>tempVec;//存放出棧的結果
//先壓入字首再壓入當前數字
for (size_t i = 0; i < currentData.second.size(); i++)tempVec.push_back(currentData.second[i]);
tempVec.push_back(currentData.first.second);
result.push_back(tempVec);
}
return result;
}
有重複元素
有重複元素的情況用BFS不太好解決
我用的是暴力解法
如有有更好的解法歡迎留言
//BFS有重複子集
vector<vector<int>> BFSSubset2(vector<int>& nums) {
vector<vector<int>>result;
result.push_back(vector<int>());
if (nums.size() == 0)return result;
sort(nums.begin(), nums.end());
queue<pair<pair<int, int>, vector<int>>>que;
//迴圈存入值和字首陣列
for (size_t i = 0; i < nums.size(); i++) {
pair<int, int>num(i, nums[i]);//數字不是單純的數字,還要帶著自己的下標
que.push(pair<pair<int, int>, vector<int>>(num, vector<int>()));
}
//迴圈出隊
while (que.size())
{
auto currentData = que.front();
que.pop();
size_t beginPos = currentData.first.first;
vector<int>tempVec;//存放出棧的結果
for (size_t i = beginPos + 1; i < nums.size(); i++)
{
int pushValue = nums[i];
vector<int>prefix(currentData.second);
prefix.push_back(currentData.first.second);//增加一個字首
que.push(pair<pair<int, int>, vector<int>>(pair<int, int>(i, pushValue), vector<int>(prefix)));
}
//先壓入字首再壓入當前數字
for (size_t i = 0; i < currentData.second.size(); i++)tempVec.push_back(currentData.second[i]);
tempVec.push_back(currentData.first.second);
result.push_back(tempVec);
}
//暴力去重
vector<vector<int>>res;
for (size_t i = 0; i < result.size(); i++)
{
//查詢是否存在
if (find(res.begin(),res.end(),result[i])!=res.end()) {
continue;
}
res.push_back(result[i]);
}
return res;
}
七、總結
雖然全子集用其它辦法也能解決,但是最簡單最容易理解的還是DFS
有什麼想法或者疑問歡迎留言評論