算法训练营(day29)
回溯算法模板
1 2 3 4 5 6 7 8 9 10 11 12
| void backtracking(参数) { if (终止条件) { 存放结果; return; }
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 处理节点; backtracking(路径,选择列表); 回溯,撤销处理结果 } }
|
491. 递增子序列
题目链接:https://leetcode.cn/problems/non-decreasing-subsequences/description/
给你一个整数数组 nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
解题思路
解题过程:回溯
- 定义终止条件:当子序列
path
内元素数量大于1时, 放入 result
中
- 定义
map
记录 元素是否已使用
- 定义循环条件
- 横向:从
startIndex
遍历到 nums.length
- 纵向:如果
nums[i] < path.getLast()
或者 map.getOrDefault(nums[i], 0) >= 1
, 跳过 nums[i]
;
- 回溯:回滚
path
的末位
详细代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> findSubsequences(int[] nums) { backTrack(nums, 0); return result; }
public void backTrack(int[] nums, int startIndex){ if(path.size() > 1){ result.add(new ArrayList<>(path)); } 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]); backTrack(nums, i + 1); path.removeLast(); } } }
|
46. 全排列
题目链接:https://leetcode.cn/problems/permutations/description/
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
解题思路
解题过程:排列有序
- 定义终止条件:当
path
长度等于 nums.length
时,放入 result
中
- 定义循环条件
- 横向:从
startIndex
遍历到 nums.length
- 纵向:排列组合
nums
元素
- 回溯:回滚
path
的末位元素
注意:解法二的 used
表示的是 在 当前下标 处该元素的使用情况,避免重复选用
详细代码
解法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> permute(int[] nums) { backTrack(nums, path); return result; }
public void backTrack(int[] nums, LinkedList<Integer> path){ if(path.size() == nums.length){ result.add(new ArrayList<>(path)); } for(int i = 0; i < nums.length; i++){ if(path.contains(nums[i])){ continue; } path.add(nums[i]); backTrack(nums, path); path.removeLast(); } } }
|
解法二:使用 boolean[] used
去重
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> path = new LinkedList<>(); boolean[] used; public List<List<Integer>> permute(int[] nums) { used = new boolean[nums.length]; backTrack(nums); return result; }
public void backTrack(int[] nums){ if(path.size() == nums.length){ result.add(new ArrayList<>(path)); return; } for(int i = 0; i < nums.length; i++){ if(used[i]){ continue; } used[i] = true; path.add(nums[i]); backTrack(nums); path.removeLast(); used[i] = false; } } }
|
47. 全排列 II
题目链接:https://leetcode.cn/problems/permutations-ii/description/
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
解题思路
解题过程:排列有序
- 对
nums
序列进行排序,定义 used
判断使用情况
- 定义终止条件:当
path
长度等于 nums.length
时,放入 result
中
- 定义循环条件
- 横向:从
startIndex
遍历到 nums.length
- 纵向:排列组合
nums
元素
- 回溯:回滚
path
的末位元素
注意: used
表示的是 在 当前下标 处该元素的使用情况,避免重复选用
详细代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| class Solution { List<List<Integer>> result = new ArrayList<>(); LinkedList<Integer> path = new LinkedList<>(); boolean[] used; public List<List<Integer>> permuteUnique(int[] nums) { Arrays.sort(nums); used = new boolean[nums.length]; Arrays.fill(used, false); backTrack(nums); return result; }
public void backTrack(int[] nums){ if(path.size() == nums.length){ result.add(new ArrayList<>(path)); return; } for(int i = 0; i < nums.length; i++){ if(used[i]){ continue; } if(i > 0 && nums[i - 1] == nums[i] && used[i - 1] == false){ continue; } if(used[i] == false){ used[i] = true; path.add(nums[i]); backTrack(nums); path.removeLast(); used[i] = false; } } } }
|