1. 程式人生 > 其它 >全子集的c++實現,二進位制,DFS,BFS三種方法

全子集的c++實現,二進位制,DFS,BFS三種方法

目錄

一、什麼是全子集

子集概念

子集是一個數學概念:如果集合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
有什麼想法或者疑問歡迎留言評論