所用代码 java
递增子序列 LeetCode 491
题目链接:递增子序列 LeetCode 491 - 中等
思路
不能排序的情况需去重的话需使用哈希进行去重。
示意图如下:
使用Set去重: 自带去重功能
class Solution {
// 使用双向链表LinkedList,可方便移除末尾元素
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return res;
}
public void backtracking(int[] nums, int startIndex){
// 收获结果 -- 树枝收集
if (path.size() >= 2) {
res.add(new ArrayList<>(path));
}
// 使用set去重
Set<Integer> set = new HashSet<>();
for (int i = startIndex; i < nums.length; i++) {
// 数层去重
if ((path.size()>0 && nums[i] < path.getLast())
|| set.contains(nums[i])){
continue;
}
path.add(nums[i]);
set.add(nums[i]);
backtracking(nums, i + 1);
path.removeLast();
}
}
}
使用map去重: 原理和set差不多
class Solution {
// 使用双向链表LinkedList,可方便移除末尾元素
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return res;
}
public void backtracking(int[] nums, int startIndex){
// 收获结果
if (path.size() >= 2) {
res.add(new ArrayList<>(path));
}
// 使用map去重 定义在此处就是每一层使用
Map<Integer, Integer> map = new HashMap<>();
for (int i = startIndex; i < nums.length; i++) {
// 保证递增
if (!path.isEmpty() && nums[i] < path.getLast()){
continue;
}
// 数层去重 -- 判断该元素是否使用过
if (map.getOrDefault(nums[i], 0) >= 1){
continue;
}
map.put(nums[i], map.getOrDefault(nums[i], 0)+1);
path.add(nums[i]);
backtracking(nums, i + 1);
path.removeLast();
}
}
}
以数组当作哈希表: 本题由于nums的数量是小于等于 200 的,所以我们可以使用一个长度大于200的used数组来代表某个元素已被访问。
class Solution {
// 使用双向链表LinkedList,可方便移除末尾元素
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
public List<List<Integer>> findSubsequences(int[] nums) {
backtracking(nums, 0);
return res;
}
public void backtracking(int[] nums, int startIndex){
// 收获结果
if (path.size() >= 2) {
res.add(new ArrayList<>(path));
}
// 数据量小可使用数组去重 定义在此处就是每一层使用
// 长度为201,刚好大于nums的最大数量
int[] used = new int[201];
for (int i = startIndex; i < nums.length; i++) {
// 保证递增
if (!path.isEmpty() && nums[i] < path.getLast()){
continue;
}
// 数层去重
// +100 的原因是 -100 <= nums[i] <= 100 ,所以需把下标调整到0-201
if (used[nums[i] + 100] == 1){
continue;
}
path.add(nums[i]);
used[nums[i] + 100] = 1;
backtracking(nums, i + 1);
path.removeLast();
}
}
}
总结
本题和前题去重逻辑不同在于本题不能排序,所以不能按前面的方法和前一个数比较然后进行数层去重。但是我们可用哈希的方法记录本层是否出现过这个数,若出现就记录下来,再次出现就可在记录中发现该数。
全排列 LeetCode 46
题目链接:全排列 LeetCode 46 - 中等
思路
排列问题和组合问题是不同的。
排列问题所有元素都要使用,是在叶子结点收集结果,但是不能重复使用,所以用used数组标记
组合问题不一定会用所有的元素,所有用startIndex标记
示意图如下:
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
// used用来标记哪些元素使用过
int[] used;
public List<List<Integer>> permute(int[] nums) {
used = new int[nums.length];
backtracking(nums);
return res;
}
public void backtracking(int[] nums){
if (path.size() == nums.length){
res.add(new LinkedList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
// 使用过的元素就不能在使用
if (used[i] == 1) continue;
path.add(nums[i]);
used[i] = 1;
backtracking(nums);
path.removeLast();
used[i] = 0;
}
}
}
总结
全排列问题在于每次递归时,每个元素都要去考虑,但是已使用过的元素需要排除
,所有就定义了一个used数组来辨别哪些元素时使用过的。
全排列 II LeetCode 47
题目链接:全排列 II LeetCode 47 - 中等
思路
本题是在上题的基础上,多了一步数层去重操作,就和之前的组合2集合2方法一样
示意图如下:
class Solution {
LinkedList<Integer> path = new LinkedList<>();
List<List<Integer>> res = new ArrayList<>();
int[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
// 初始化used数组,全0
used = new int[nums.length];
// 去重记得先排序
Arrays.sort(nums);
backtracking(nums);
return res;
}
public void backtracking(int[] nums){
if (path.size() == nums.length){
res.add(new LinkedList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
// 数层去重
if (i > 0 && nums[i] == nums[i-1] && used[i-1] == 0){
continue;
}
// 判断该元素是否使用过
if (used[i] == 1){
continue;
}
path.add(nums[i]);
used[i] = 1;
backtracking(nums);
path.removeLast();
used[i] = 0;
}
}
}
总结
方法万变不离其宗主要就是以下情况:
-
需要去重
- 数据能重排 – 重排之后利用uesd数组
used[i-1] == 0
进行数层的去重 - 数据不能重拍 – 使用
set
或是map
去重,数据少也可以使用used数组
- 数据能重排 – 重排之后利用uesd数组
-
不需要去重