算法总结三:DFS可以套用模版吗?

学习目标:

辨别: 什么是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;
    // }
}

39. Combination Sum

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;
    
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值