在力扣上刷到的這類題,做一個總結。思路相似,都可以用回溯(決策樹)解決。
#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";
}
程序運行示例結果如下: