求全排列、組合、子集,C++程序總結

在力扣上刷到的這類題,做一個總結。思路相似,都可以用回溯(決策樹)解決。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;


// 全排列-------------------------------------------------------------------------------------------------------------
// 問題:給定數組nums,求集合nums的全排列
void back_track1(vector<int>& nums, vector<int>& track, vector<vector<int>>& res);	// 先聲明回溯函數,函數定義在後面

vector<vector<int>> permute(vector<int>& nums) {
	vector<vector<int>> res;			 // 保存答案        
	vector<int> track;					 // 路徑:在決策樹中已經做出的選擇
	back_track1(nums, track, res);       // 回溯算法
	return res;
}

void back_track1(vector<int>& nums, vector<int>& track, vector<vector<int>>& res) {
	// 遞歸結束條件:路徑大小==nums大小,到達決策樹底層
	int nums_siz = nums.size();
	if (track.size() == nums_siz) {
		res.push_back(track);
		return;
	}

	for (int i = 0; i < nums_siz; ++i) {
		// 做選擇
		auto iter = find(track.begin(), track.end(), nums[i]);
		if (iter != track.end()) {
			continue;
		}			
		track.push_back(nums[i]);
		back_track1(nums, track, res);
		// 撤銷選擇
		track.pop_back();
	}
}



// 組合--------------------------------------------------------------------------------------------------------
// 問題:1~n共n個數,求k個數的所有組合
void back_track2(int n, int k, int start, vector<int>& track, vector<vector<int>>& res);
vector<vector<int>> combine(int n, int k) {
	vector<vector<int>> res;	
	vector<int> track;					 // 路徑:在決策樹中已經做出的選擇
	back_track2(n, k, 1, track, res);
	return res;
}

void back_track2(int n, int k, int start, vector<int> &track, vector<vector<int>> &res) {
	// 遞歸結束條件:路徑大小==k,到達決策樹底層
	if (track.size() == k) {
		res.push_back(track);
		return;
	}
	
	// i 從 start 開始遞增
	for (int i = start; i <=n; i++) {
		// 做選擇
		track.push_back(i);
		// 回溯
		back_track2(n, k, i+1, track, res);
		// 撤銷選擇
		track.pop_back();
	}
}



// 子集--------------------------------------------------------------------------------------------------------
// 問題:給定數組nums,求集合nums的所有子集
// 方法一:
// 空集的子集:{}
// 有一個元素的集合{a}的子集:{}、{a}
// 有兩個元素的集合{a,b}的子集:{}、{a}    、{b}、{a,b}
// 集合每增加一個元素,其子集爲:原集合子集、原集合的每個子集中加上新增加的這個元素
// 遞歸程序:
vector<vector<int>> subsets(vector<int>& nums) {
	// 遞歸結束條件:nums爲空,返回一個空集。
	if (nums.empty()) return { {} };
	
	int n = nums.back();	// 把nums最後一個元素拿出來
	nums.pop_back();
	vector<vector<int>> res = subsets(nums);	// 前面元素的所有子集
	
	// 然後在 前面元素的所有子集 後增加這個元素
	int siz = res.size();
	for (int i = 0; i < siz; i++) {
		res.push_back(res[i]);
		res.back().push_back(n);
	}
	return res;
}

// 子集
// 方法二:回溯
void back_track3(vector<int>& nums, int start, vector<int>& track, vector<vector<int>>& res);

vector<vector<int>> subset_method2(vector<int>& nums) {
	vector<vector<int>> res;         // 保存答案
	vector<int> track;               // 路徑:在決策樹中已經做出的選擇	
	back_track3(nums, 0, track,res);
	return res;
}

void back_track3(vector<int>& nums, int start, vector<int>& track, vector<vector<int>> &res) {
	res.push_back(track);
	// i 從 start 開始遞增
	int siz = nums.size();
	for (int i = start; i <siz; i++) {
		// 做選擇
		track.push_back(nums[i]);
		// 回溯
		back_track3(nums, i + 1, track,res);
		// 撤銷選擇
		track.pop_back();
	}
}

// 控制檯輸出二維數組
void print(vector<vector<int>> &res) {
	for (auto it : res) {
		for (auto i : it) {
			cout << i << "  ";
		}
		cout << "  ;  ";
	}
}

int main()
{
   
    vector<int> nums{ 1,2,3 };
	// 全排列	
	vector<vector<int>> res=permute(nums);
	cout << endl << "全排列:";
	print(res);

	// 子集
	vector<vector<int>> res1=subset_method2(nums);
	cout << endl << "子集,方法二:";
	print(res1);
	vector<vector<int>> res2 = subsets(nums);
	cout << endl << "子集,方法一:";
	print(res2);

	// 組合
	vector<vector<int>> res3 = combine(5, 2);
	cout << endl << "組合:";
	print(res3);

   cout << "\n";
}

程序運行示例結果如下:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章