1、回溯结构:
//给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (k <= 0 || n < k) {
return res;
}
// 从 1 开始是题目的设定
Deque<Integer> path = new ArrayDeque<>();
dfs(n, k, 1, path, res);
return res;
}
private void dfs(int n, int k, int begin, Deque<Integer> path, List<List<Integer>> res) {
// 递归终止条件是:path 的长度等于 k
if (path.size() == k) {
res.add(new ArrayList<>(path));
return;
}
// 遍历可能的搜索起点
for (int i = begin; i <= n; i++) {
// 向路径变量里添加一个数
path.addLast(i);
// 下一轮搜索,设置的搜索起点要加 1,因为组合数理不允许出现重复的元素
dfs(n, k, i + 1, path, res);
// 重点理解这里:深度优先遍历有回头的过程,因此递归之前做了什么,递归之后需要做相同操作的逆向操作
path.removeLast();
}
}
}
//有重复的数的排列。注意下一个数的筛选即可。
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList();
Stack<Integer> stack = new Stack();
boolean[] used = new boolean[nums.length];
Arrays.sort(nums);
dfs(nums,0,used,stack,res);
return res;
}
public void dfs(int[] nums,int cur,boolean[] used,Stack stack,List res){
if(stack.size() == nums.length){
res.add(new ArrayList(stack));
return;
}
for(int i = 0;i<nums.length;i++){
if(used[i] || (i > 0 && nums[i] == nums[i-1] && used[i-1] ) ){
continue;
}
stack.push(nums[i]);
used[i] = true;
dfs(nums,i+1,used,stack,res);
used[i] = false;
stack.pop();
}
}
2、动态规划
1、正常是O(2^n)。
2、用空间换时间的方法,dp[]数组保存状态。时间O(N),空间O(N)。
3、可以用3个变量进行移动的表示dp数组的意义。把dp数组也给省掉。最后优化为时间O(N),空间O(1)。