491.递增子序列
和子集类似,但不能排序去重,也就不能通过挨着的元素树层去重。
- 求子集,子序列都是在节点上收获结果(回溯可以不要终止条件,找到结果也不需要return),而划分,分割都是在叶子节点上获得结果,注意区分,组合问题和排列问题是在树形结构的叶子节点上收集结果,而子集问题就是取树上所有节点的结果。
- 求子集不需要终止条件,搜到底就结束了。
- 注意Uset仅是一层的局部变量,不需要回溯,和used数组去重需要回溯不一样,因为used是一个全局变量(通过引用传参)
//C++
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> findSubsequences(vector<int>& nums) {
BackTracking(nums, 0);
return res;
}
void BackTracking(vector<int>& nums, int startIndex) {
if (path.size() > 1) {
res.push_back(path);
//别return,取树枝上的点
}
unordered_set<int> uset;
for (int i = startIndex; i < nums.size(); i++) {
if ((!path.empty() && nums[i] < path.back()) || (uset.find(nums[i]) != uset.end())) {
continue;
}
uset.insert(nums[i]);
path.push_back(nums[i]);
BackTracking(nums, i + 1);
path.pop_back();
}
}
};
// C#
public class Solution {
public IList<IList<int>> res = new List<IList<int>>();
public List<int> path = new List<int>();
public IList<IList<int>> FindSubsequences(int[] nums) {
BackTracking(nums, 0);
return res;
}
public void BackTracking(int[] nums, int startIndex) {
if (path.Count >= 2) res.Add(new List<int>(path));
HashSet<int> uset = new HashSet<int>();
for (int i = startIndex; i < nums.Length; i++) {
if ((path.Count != 0 && nums[i] < path[path.Count - 1]) || (uset.Contains(nums[i])))
continue;
uset.Add(nums[i]);
path.Add(nums[i]);
BackTracking(nums, i + 1);
path.RemoveAt(path.Count - 1);
}
}
}
使用数组进行哈希,适当对-100 100的nums进行平移缩放到0-200。
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> findSubsequences(vector<int>& nums) {
BackTracking(nums, 0);
return res;
}
void BackTracking(vector<int>& nums, int startIndex) {
if (path.size() > 1) {
res.push_back(path);
//别return,取树枝上的点
}
int used[201] = {0};
for (int i = startIndex; i < nums.size(); i++) {
if ((!path.empty() && nums[i] < path.back()) || used[nums[i] + 100] == 1) {
continue;
}
used[nums[i] + 100] = 1;
path.push_back(nums[i]);
BackTracking(nums, i + 1);
path.pop_back();
}
}
};
46.全排列
全排列集合靠后的数可以重复向前选择,所以不用startIndex,这时候要注意需要进行树枝去重(错了,不是树枝,树枝是指不同位置相同值,实际上这里用的是同一个位置的元素,等同于树层去重里记录每一位元素是否被用过)避免重复。
- vector可以初始化定义大小和值:构造函数(nums.size(), false),C# List没有,但可以先定义空间,在通过Array.Fill(对象,填充内容)函数填充元素;
- 每一层实际都遍历了整个数字集合取分支,used数组标记了上面的树使用过的元素,跳过即可。
排列问题的不同:
每层都是从0开始搜索而不是startIndex;需要used数组记录path里都放了哪些元素了
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> permute(vector<int>& nums) {
vector<bool> used(nums.size(), false);
BackTracking(nums, used);
return res;
}
void BackTracking(vector<int>& nums, vector<bool>& used) {
if (path.size() == nums.size()) res.push_back(path);
for (int i = 0; i < nums.size(); i++) {
if (used[i] == true) continue;
used[i] = true;
path.push_back(nums[i]);
BackTracking(nums, used);
used[i] = false;
path.pop_back();
}
}
};
//C#
public class Solution {
IList<IList<int>> res = new List<IList<int>>();
List<int> path = new List<int>();
public IList<IList<int>> Permute(int[] nums) {
bool[] used = new bool[nums.Length];
BackTracking(nums, used);
return res;
}
public void BackTracking(int[] nums, bool[] used) {
if (path.Count == nums.Length) {
res.Add(new List<int>(path));
return;
}
for (int i = 0; i < nums.Length; i++) {
if (used[i] == true) continue;
used[i] = true;
path.Add(nums[i]);
BackTracking(nums, used);
used[i] = false;
path.RemoveAt(path.Count - 1);
}
}
}
47.全排列 II
考验去重,使用uset在当前层去重,或者排序后使用used去重(注意不能用startIndex了,每次都从0开始),使用used数组效率更高一些,uset本身哈希费时间费空间。
//C++ uset去重
class Solution {
public:
vector<vector<int>> res;
vector<int> path;
vector<vector<int>> permuteUnique(vector<int>& nums) {
vector<bool> used(nums.size(), false);
BackTracking(nums, used);
return res;
}
void BackTracking(vector<int>& nums, vector<bool>& used) {
if (path.size() == nums.size()) {
res.push_back(path);
return;
}
unordered_set<int> uset;
for (int i = 0; i < nums.size(); i++) {
if (used[i] == true) continue;
if (uset.find(nums[i]) != uset.end()) continue;
used[i] = true;
uset.insert(nums[i]);
path.push_back(nums[i]);
BackTracking(nums, used);
path.pop_back();
used[i] = false;
}
}
};
//C# 排序后去重
public class Solution {
IList<IList<int>> res = new List<IList<int>>();
List<int> path = new List<int>();
bool[] use;
public IList<IList<int>> PermuteUnique(int[] nums) {
use = new bool[nums.Length];
Array.Sort(nums);
BackTracking(nums);
return res;
}
public void BackTracking(int[] nums) {
if (nums.Length == path.Count) {
res.Add(new List<int>(path));
return;
}
for (int i = 0; i < nums.Length; i++) {
if (use[i] == true) continue;
if (i > 0 && nums[i] == nums[i - 1] && use[i - 1] == false) continue;
use[i] = true;
path.Add(nums[i]);
BackTracking(nums);
path.RemoveAt(path.Count - 1);
use[i] = false;
}
}
}
注意use[i - 1] == false 也可以写成true,那就是树枝去重了,效率低。