class Solution {
public:
/*
* @param nums: A list of integers
* @return: A list of integers that's previous permuation
*/
vector<int> previousPermuation(vector<int> &nums) {
int n = nums.size();
if (n <= 1) return nums;
int hi = n - 2;
// 1. 从右往左找**第一个升序**的位置 hi
while (hi >= 0 && nums[hi] <= nums[hi + 1])
hi--;
if (hi >= 0) {
int lo = n - 1;
// 2. 从右往左找**第一个小于** nums[hi] 的位置 lo
while (lo >= 0 && nums[lo] >= nums[hi])
lo--;
// 3. 交换 nums[lo] 和 nums[hi]
swap(nums[lo], nums[hi]);
}
// 4. 反转 hi 之后的位置
reverse(nums.begin() + hi + 1, nums.end());
return nums; // 注意这里要你返回一个值
}
};
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
vector<bool> used;
int n = 0;
void dfs(vector<int>& nums, int step) {
if (tmp.size() == n) {
ret.push_back(tmp);
return;
}
for (int i = 0; i < n; i++) {
if (used[i] || (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]))
continue; // 这里的 !used[i - 1] 稍难理解,可以配合 IDE 或者手推一下整个过程
used[i] = 1;
tmp.push_back(nums[i]);
dfs(nums, step + 1);
tmp.pop_back();
used[i] = 0;
}
}
public:
vector<vector<int> > permuteUnique(vector<int>& nums) {
n = nums.size();
used.resize(n, 0);
sort(nums.begin(), nums.end());
dfs(nums, 0);
return ret;
}
};
基于交换的写法
class Solution {
vector<vector<int> > ret;
//void dfs(vector<int>& nums, int step) { // 传引用无法得出正确结果
void dfs(vector<int> nums, int step) { // 注意这里应该使用**值传递**
int n = nums.size();
if (step >= n - 1) {
ret.push_back(nums);
return;
}
for (int i = step; i < n; i++) {
if (i != step && nums[i] == nums[step])
continue;
swap(nums[i], nums[step]);
dfs(nums, step + 1);
//swap(nums[i], nums[step]); // 传引用配合回溯无法得出正确结果,
// 原因在于此时会破坏剩余数组的有序性
}
}
public:
vector<vector<int> > permuteUnique(vector<int>& nums) {
sort(nums.begin(), nums.end());
dfs(nums, 0);
return ret;
}
};
【注】全排序的时间复杂度
不重复情况下,n 个元素的不同全排列为 n! 个,所以算法的时间复杂度至少为 O(N!)
因此,全排列算法对大型的数据是无法处理的
组合
组合(n 选 k,无重复)
问题描述
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2
输出:
[
[2,4],
[3,4],
[2,3],
[1,2],
[1,3],
[1,4],
]
思路
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp; // 保存中间结果
int K;
void dfs(vector<int>& nums, int step) {
if (tmp.size() >= K) {
ret.push_back(tmp);
return;
}
for (int i = step; i < nums.size(); i++) {
tmp.push_back(nums[i]); // nums[i] == i,所以这里直接 push(i) 也可以
dfs(nums, i + 1);
tmp.pop_back();
}
}
public:
vector<vector<int> > combine(int n, int k) {
K = k;
vector<int> nums;
for (int i = 0; i < n; i++)
nums.push_back(i + 1);
dfs(nums, 0);
return ret;
}
};
组合(n 选 k,有重复)
(未验证)
如果要求每个组合中不重复,则可以先去重,再按照无重复的做法
如果不要求去重,则直接按照无重复的做法即可
组合总和(数字不重复但可重复使用)
思路
深度优先搜索
关键在于每个数字可以重复使用
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
int cur = 0;
int target = 0;
void dfs(vector<int>& nums, int step) {
if (cur >= target) {
if (cur == target)
ret.push_back(tmp);
return;
}
for (int i = step; i < nums.size(); i++) {
cur += nums[i];
tmp.push_back(nums[i]);
dfs(nums, i); // 因为每个数组可以重复使用,所以是 dfs(i) 而不是 dfs(i+1)
cur -= nums[i];
tmp.pop_back();
}
}
public:
vector<vector<int> > combinationSum(vector<int>& candidates, int target) {
this->target = target;
//sort(candidates.begin(), candidates.end()); // 不需要
dfs(candidates, 0);
return ret;
}
};
组合总和 2(存在重复数字但每个数字只能使用一次)
思路
DFS,关键是如何去除重复情况
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
int cur = 0;
int target;
void dfs(vector<int>& nums, int step) {
if (cur >= target) {
if (cur == target)
ret.push_back(tmp);
return;
}
for (int i = step; i < nums.size(); i++) {
if (i > step && nums[i] == nums[i - 1]) // 代码说明 1
continue;
cur += nums[i];
tmp.push_back(nums[i]);
dfs(nums, i + 1); // i+1 而不是 i,因为不能重复使用
tmp.pop_back();
cur -= nums[i];
}
}
public:
vector<vector<int> > combinationSum2(vector<int>& candidates, int target) {
this->target = target;
sort(candidates.begin(), candidates.end()); // 因为存在重复,需要先排序
dfs(candidates, 0);
return ret;
}
};
代码说明 1
if (i > step && nums[i] == nums[i - 1])
组合总和 3(数字不重复且指定数量)
问题描述
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
思路
C++
class Solution {
vector<vector<int> > ret;
vector<int> tmp;
int cur = 0;
int K;
void dfs(int n, int step) {
if (cur >= n) {
if (cur == n && tmp.size() == K) // 同时满足才加入结果集
ret.push_back(tmp);
return;
}
for (int i = step; i <= 9; i++) {
cur += i;
tmp.push_back(i);
dfs(n, i + 1); // 因为数字不能重复使用,所以是 dfs(i+1)
tmp.pop_back();
cur -= i;
}
}
public:
vector<vector<int> > combinationSum3(int k, int n) {
K = k;
dfs(n, 1);
return ret;
}
};