学习目标:
辨别: 什么是DFS深度优先? 什么是back tracking回溯? 什么是recursion递归?学习内容:
1、 DFS深度优先的高频题学习时间:
1、 周一至周日晚上 7 点—晚上9点学习产出:
1、 技术笔记 博客 1 篇=================
什么是DFS深度优先?
什么是back tracking回溯?
什么是recursion递归?
在学习leetcode中backtracking题目时,碰到了经典的DFS题目N-Queens。
为啥这个题目既是backtracking又是DFS呢?
DFS中为啥又有base case+recursion rule呢?
======================
一。区别几个定义
recursion是将当前的问题,分解成小一号的问题(divide and conquer)。在解决小问题的时候,然后recursively用同样的逻辑就把大问题“递归”好了。
recursion中,我们关心:
子问题是什么?当前做点什么?返回给上一层什么?
DFS是在进行“树”的搜索,或者问题可以被逻辑视为“树”、“层”、“叉”时,人为规定一个深度优先搜索的方式。
DFS有base case,规定何时return,触底反弹。
有recursion rule,“递归”规则为:
cur.add(i);
dfs(index+1,res,cur); //这里是recursion,进入下一层问题
cur.remove(cur.size()-1);
即加入一个元素i,index+1进入下一层,若回溯时需要移除该元素,方可回到这一层,然后试另外一个元素(叉)。
N-Queens可以视为:每一个row是一层,有N个可能性,放好第一个Q就进入下一层。
深度优先遍历顺序为 1->2->4->8->5->3->6->7 from jianshu.com
其实是pre-order traversal根左右的顺序。
public void depthOrderTraversal(){
if(root==null){
System.out.println("empty tree");
return;
}
ArrayDeque<TreeNode> stack=new ArrayDeque<TreeNode>();
stack.push(root);
while(stack.isEmpty()==false){
TreeNode node=stack.pop();
System.out.print(node.value+" ");
if(node.right!=null){
stack.push(node.right);
}
if(node.left!=null){
stack.push(node.left);
}
}
System.out.print("\n");
}
联系recursion:
进入下一层又可以视为分解为小一号的问题
小一号的问题,一直被递归到最小,然后触底反弹,“归来”解决了大问题。
无特殊情况,DFS就用递归实现!!!
back tracking回溯定义
Backtracking is a general algorithm for finding all (or some) solutions to some computational problems, notably constraint satisfaction problems, that incrementally builds candidates to the solutions, and abandons a candidate (“backtracks”) as soon as it determines that the candidate cannot possibly be completed to a valid solution.
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法。
即:放进去,进入下一层,剔除返回上一层的过程。
cur.add(i);
dfs(index+1,res,cur);
cur.remove(cur.size()-1);//这里是back tracking回溯
这岂不就是dfs?
其实,回溯是dfs的核心动作。
但不是所有DFS都有回溯这个动作。
例如,在下面的几个“岛屿”题中,并不需要回溯!
======================
二。back tracking算法
51. N-Queens
The n-queens puzzle is the problem of placing n queens on an n x n chessboard such that no two queens attack each other.
Given an integer n, return all distinct solutions to the n-queens puzzle.
如果返回值是List<Integer>
这里利用DFS深度优先搜索的方法:
public class Solution {
public List<List<Integer>> nqueens(int n) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> oneRes=new ArrayList<>();
dfs(n,res,oneRes);
return res;
}
private void dfs (int n,List<List<Integer>>res,List<Integer>oneRes){
//base case
if(oneRes.size()==n){
res.add(new ArrayList<Integer>(oneRes));//new !
return;
}
//recurs rule:这里没有使用idx因oneRes.size()即 idx
for(int i=0;i<n;i++){
if(checkValid(oneRes,i)){
oneRes.add(i);
dfs(n,res,oneRes);
oneRes.remove(oneRes.size()-1);//back tracking
}
}
}
private boolean checkValid (List<Integer> oneRes,int colIdx){
int currow=oneRes.size();
for(int i=0;i<currow;i++){
if(oneRes.get(i)==colIdx || Math.abs(oneRes.get(i)-colIdx)==currow-i){
return false;
}
}
return true;
}
}
78. Subsets
Given an integer array nums, return all possible subsets (the power set).
The solution set must not contain duplicate subsets.
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> cur=new ArrayList<>();
int n=nums.length;
dfs(nums,res,cur,0,n);
return res;
}
void dfs(int[] nums,List<List<Integer>> res,List<Integer> cur,int index,int n){
//base
if(index==n){
res.add(new ArrayList<>(cur));
return;
}
//recursion rule
//不+
dfs(nums,res,cur,index+1,n);
//+
cur.add(nums[index]);
dfs(nums,res,cur,index+1,n);
cur.remove(cur.size()-1);//back tracking
}
}
22. Generate Parentheses
Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses.
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res=new ArrayList<String> ();
StringBuilder sb=new StringBuilder();
dfs(res,sb,0,n,n);
return res;
}
public void dfs(List<String> res,StringBuilder sb,int index,int leftRemain,int rightRemain){
//base
if(leftRemain==0 && rightRemain==0){
res.add(sb.toString());
return;
}
//recursive rule
if(leftRemain>0){
sb.append('(');
dfs(res,sb,index+1,leftRemain-1,rightRemain);
sb.deleteCharAt(sb.length()-1);//back tracking
}
if(leftRemain<rightRemain){
sb.append(')');
dfs(res,sb,index+1,leftRemain,rightRemain-1);
sb.deleteCharAt(sb.length()-1);//back tracking
}
}
}
46. Permutations
Given an array nums of distinct integers, return all the possible permutations. You can return the answer in any order.
class Solution {
public List<List<Integer>> permute(int[] nums) {
//swap and swap
List<Integer> cur=new ArrayList<>();
List<List<Integer>> res=new ArrayList<>();
for(int num:nums){
cur.add(num);
}
int n=nums.length;
dfs(n,cur,res,0);
return res;
}
void dfs(int n, List<Integer> cur,List<List<Integer>> res,int index){
//base case
if(index==n){
res.add(new ArrayList<>(cur));
return;
}
//recursion rule
for(int i=index;i<n;i++){
Collections.swap(cur,index,i);
dfs(n,cur,res,index+1);
Collections.swap(cur,index,i);//back tracking
//区别laicode原题: 这里是swap ArrayList内的数, 所以直接调用Collections.swap
}
}
// void swap(int[] nums,int a,int b){
// int tmp=nums[a];
// nums[a]=nums[b];
// nums[b]=tmp;
// }
}
Given an array of distinct integers candidates and a target integer target, return a list of all unique combinations of candidates where the chosen numbers sum to target. You may return the combinations in any order.
The same number may be chosen from candidates an unlimited number of times. Two combinations are unique if the frequency of at least one of the chosen numbers is different.
It is guaranteed that the number of unique combinations that sum up to target is less than 150 combinations for the given input.
Example 1:
Input: candidates = [2,3,6,7], target = 7
Output: [[2,2,3],[7]]
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res=new ArrayList<>();
List<Integer> oneRes=new ArrayList<Integer>();
dfs(res,oneRes,candidates,target,0);
return res;
}