回溯
子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例: 输入: nums = [1,2,3] 输出: [ [3], [1], [2], [1,2,3], [1,3], [2,3], [1,2], [] ]
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
dfs(0, nums);
return ans;
}
void dfs(int idx, const vector<int>& nums) {
if (idx == nums.size()) {
ans.push_back(numset);
return ;
}
// 当前数字加入到集合
numset.push_back(nums[idx]);
dfs(idx + 1, nums);
numset.pop_back();
// 当前数字不加入到集合
dfs(idx + 1, nums);
}
private:
vector<vector<int>> ans;
vector<int> numset;
};
// 解法二:更优(回溯树的节点都是子集)
class Solution {
private:
vector<vector<int>> result;
vector<int> path;
void backtracking(vector<int>& nums, int startIndex) {
result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
if (startIndex >= nums.size()) { // 终止条件可以不加
return;
}
for (int i = startIndex; i < nums.size(); i++) {
path.push_back(nums[i]);
backtracking(nums, i + 1);
path.pop_back();
}
}
public:
vector<vector<int>> subsets(vector<int>& nums) {
backtracking(nums, 0);
return result;
}
};
子集Ⅱ
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
- 输入: [1,2,2]
- 输出: [ [2], [1], [1,2,2], [2,2], [1,2], [] ]
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(), nums.end()); // 先排序
backtrace(0, nums);
return ans;
}
void backtrace(int index, vector<int>& nums) {
ans.push_back(numset); // 每个回溯树的节点都push
if (index >= nums.size()) {
return ;
}
set<int> currset; // 记录当前位置已经放过的数字
for (int i = index; i < nums.size(); ++i) {
if (currset.find(nums[i]) == currset.end()) {
currset.insert(nums[i]);
numset.push_back(nums[i]);
backtrace(i + 1, nums);
numset.pop_back();
}
}
}
private:
vector<vector<int>> ans;
vector<int> numset;
};
递增子序列
给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
-
输入: [4, 6, 7, 7]
-
输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
-
给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。
和子集Ⅱ类似,同样是由一个set来记录当前位置已经放过的数字。因为如果是相同的数字,肯定是遍历前面的一个数字即可。
class Solution {
public:
vector<vector<int>> findSubsequences(vector<int>& nums) {
backtrace(0, nums);
return ans;
}
void backtrace(int index, vector<int>& nums) {
if (numset.size() >= 2) {
ans.push_back(numset);
}
if (index >= nums.size()) {
return ;
}
set<int> currset;
for (int i = index; i < nums.size(); ++i) {
if (currset.find(nums[i]) == currset.end() && (numset.empty() || numset.back() <= nums[i])) {
numset.push_back(nums[i]);
currset.insert(nums[i]);
backtrace(i + 1, nums);
numset.pop_back();
}
}
}
private:
vector<vector<int>> ans;
vector<int> numset;
};
全排列
给定一个 没有重复数字的序列,返回其所有可能的全排列。
示例:
- 输入: [1,2,3]
- 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
backtrace(0, nums);
return ans;
}
void backtrace(int index, vector<int>& nums) {
if (index >= nums.size()) {
ans.push_back(nums);
return ;
}
for (int i = index; i < nums.size(); ++i) {
swap(nums[i], nums[index]);
backtrace(index + 1, nums);
swap(nums[i], nums[index]);
}
}
private:
vector<vector<int>> ans;
};
全排列 II
给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。
示例 1:
- 输入:nums = [1,1,2]
- 输出: [[1,1,2], [1,2,1], [2,1,1]]
和全排列一样,只不过有重复元素,所以需要用一个set去重,并且要注意插入set的是nums[i]而不是nums[index]
class Solution {
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
backtrace(0, nums);
return ans;
}
void backtrace(int index, vector<int>& nums) {
if (index >= nums.size()) {
ans.push_back(nums);
return ;
}
set<int> currset;
for (int i = index; i < nums.size(); ++i) {
if (currset.find(nums[i]) == currset.end()) {
currset.insert(nums[i]);
swap(nums[i], nums[index]);
backtrace(index + 1, nums);
swap(nums[i], nums[index]);
}
}
}
private:
vector<vector<int>> ans;
};
重新安排行程
给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。
提示:
- 如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前
- 所有的机场都用三个大写字母表示(机场代码)。
- 假定所有机票至少存在一种合理的行程。
- 所有的机票必须都用一次 且 只能用一次。
示例 2:
- 输入:[[“JFK”,“SFO”],[“JFK”,“ATL”],[“SFO”,“ATL”],[“ATL”,“JFK”],[“ATL”,“SFO”]]
- 输出:[“JFK”,“ATL”,“JFK”,“SFO”,“ATL”,“SFO”]
- 解释:另一种有效的行程是 [“JFK”,“SFO”,“ATL”,“JFK”,“ATL”,“SFO”]。但是它自然排序更大更靠后。
N皇后
n 皇后问题 研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
board = vector<string>(n, string(n, '.'));
dfs(n, 0);
return ans;
}
void dfs(int n, int index) {
if (index == n) {
ans.push_back(board);
return ;
}
for (int i = 0; i < n; ++i) {
if (canGo(index, i, n)) {
board[index][i] = 'Q';
dfs(n, index + 1);
board[index][i] = '.';
}
}
}
bool canGo(int x, int y, int n) {
for (int i = 0; i <= x; i++) {
for (int j = 0; j < n; ++j) {
if (board[i][j] == 'Q' && (abs(x - i) == abs(y - j) || j == y)) {
return false;
}
}
}
return true;
}
private:
vector<vector<string>> ans;
vector<string> board;
};