深度优先算法

Posted 碎屑笔记

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深度优先算法相关的知识,希望对你有一定的参考价值。

深度优先算法(Depth-First-Search)

深度优先算法简称为DFS,最直接的例子就是“走迷宫”,首先沿着其中的一个路口一直走,当走不通时,就返回上一个岔路口,沿着另一条路径一直走,知道走到出口为止。

深度优先算法的说明以无向图为例,进行说明

以连接表的方式来存储图

private int v; //顶点的个数
   private LinkedList<Integer> adj[];  //邻接表

   public Graph(int v){
       this.v = v;
       adj = new LinkedList[v];
       for(int i=0; i<v; i++){
           adj[i] = new LinkedList<Integer>();
      }
  }
   public void addEdge(int s, int t){ //无向图一条边存两次
       adj[s].add(t);
       adj[t].add(s);

  }
   public void showGraph(){
       for(int i=0; i<v; i++){
           Iterator iterator = adj[i].iterator();
           while(iterator.hasNext()){
               System.out.println(iterator.next());
          }
      }
  }

存储的样式如图所示,最左边的标识顶点,右边的表示与该顶点相连的节点

通过深度优先对图进行查找

 /*
   * visited用来记录已经被访问的顶点,用来避免顶点被重复访问
   * prev 用来记录搜索路径,
   *
   * */

   boolean found = false; //found为false表示还没有找到
   //在一张图中从s点开始,去查找t
   public void dfs(int s, int t){
       found = false;
       boolean[] visited = new boolean[v];
       int[] prev = new int[v];
       for (int i=0; i< v; i++){
           prev[i] = -1;
      }

  }
   private void recurDfs(int w, int t, boolean[] visited, int[] prev){
       if(found == true){ return ;} //判断是否已经找到
       visited[w] = true; //找过的节点为true
       if(w==t){ //找到了使found为true
           found = true;
           return ;
      }
       for(int i=0; i< adj[w].size(); i++){
           int q = adj[w].get(i);
           if(!visited[q]){ //没有访问过的节点
               prev[q] = w;
               recurDfs(q,t,visited,prev);
          }
      }
  }

LeetCode练习

数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。

链接:https://leetcode-cn.com/problems/generate-parentheses

示例:

输入:n = 3
输出:[
      "((()))",
      "(()())",
      "(())()",
      "()(())",
      "()()()"
    ]

  1. 暴力法

可以生成长度为2n的所有可能的字符序列,然后我们检查每一个是否满足条件。

为了检测字符串是否有效,我们需要对字符串进行遍历,只有当其中的 '(' 数等于 ‘)’数时才表示该字符序列有效。

 public List<String> generateParenthesis(int n) {
       List<String> combinations = new ArrayList(); //combinations用来存储满足条件的结果
       generateAll(new char[2 * n], 0, combinations);
       return combinations;
  }

   //通过遍历将数组的所有可能都填写进去,当pos的长度等于current.length时,对此时的数组进行判断,如果该数组满足条件,则将其添加到result中
   public void generateAll(char[] current, int pos, List<String> result) {
       if (pos == current.length) { //
           if (valid(current)) {   //递归的终止条件,当该数组满足条件时,将其加入到result中
               result.add(new String(current));
          }
      } else {
           current[pos] = '('; //递归添加各种可能的数据,并在其长度等于2*n时对数组进行判断
           generateAll(current, pos+1, result);
           current[pos] = ')';
           generateAll(current, pos+1, result);
      }
  }

   public boolean valid(char[] current) {
       int balance = 0;
       for (char c: current) { //对current数组进行遍历
           if (c == '(') {balance++;}
           else {balance--;}
           if (balance < 0) {return false;}
      }
       return (balance == 0); //左括号和右括号的数量一致
  }
  1. 使用深度优先算法(回溯法)来实现

如果左括号数量不大于 n,我们可以放一个左括号。如果右括号数量小于左括号的数量,我们可以放一个右括号。

public List<String> generateParenthesis(int n) {
       List<String> ans = new ArrayList();
       backtrack(ans, new StringBuilder(), 0, 0, n);
       return ans;
  }

public void backtrack(List<String> ans, StringBuilder cur, int open, int close, int max){
       //ans用于存储满足条件的结果
       if (cur.length() == max * 2) {
           ans.add(cur.toString());
           return;
      }

       if (open < max) {//open表示左括号位置的下标 open左括号数小于n,可以添加左括号
           cur.append('(');
           backtrack(ans, cur, open+1, close, max); //open = 0, close = 0 , max = 1
           cur.deleteCharAt(cur.length() - 1);
      }
       if (close < open) {//close表示右括号   右括号数小于左括号数
           cur.append(')');
           backtrack(ans, cur, open, close+1, max);
           cur.deleteCharAt(cur.length() - 1);
      }
  }


以上是关于深度优先算法的主要内容,如果未能解决你的问题,请参考以下文章

数据结构与算法图遍历算法 ( 深度优先搜索代码示例 )

深度优先和广度优先算法

数据结构—深度优先遍历广度优先遍历图遍历算法的应用

深度优先搜索算法解释下?

七十九深度和广度优先搜索算法

深度优先算法和Ncurses - 迷宫