DFS习题复习 DFS的实际应用:括号检测,graph Bipartite及随机生成迷宫

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DFS习题复习 DFS的实际应用:括号检测,graph Bipartite及随机生成迷宫相关的知识,希望对你有一定的参考价值。

分享三个hard难度的题,个人感觉比较有意思和有实际意义的三道,思路上和上次的有些区别,不过大致上还是一样的。

1:All Valid Permutations Of Parentheses II

Get all valid permutations of l pairs of (), m pairs of [] and n pairs of {}.

Assumptions

  • l, m, n >= 0

Examples

l = 1, m = 1, n = 0, all the valid permutations are ["()[]", "([])", "[()]", "[]()"]

解题思路:

1:多少层? 层数为括号总数,因此总共有2*(m+n+l)层

2:每层多少case? 每层需考虑6个case,既"(",  ")",  "[", "]",  "{",  "}", 左右括号加的条件详见上一篇All Valid Permutations Of Parentheses I 的解释,

同时还需要考虑的是,在每一次添加右括号时,我们需要check一下前一个元素与该右括号是否匹配,比如,如果要加“)”,那我们就必须保证之前还未匹配过的左括号中的最末位一定是“(”。 感觉这样说有点绕,举个例子就是,假设这是之前添加过的所有左括号“([{”,此时我们添加右括号时,只能加“}”,加完“}”后,所有未匹配的左括号只剩下了“([", 此时只能添加“]”,以此类推。因此,我们需要反复回看之前添加的左括号是什么,当我们需要反复回看之前的内容时,最有效的办法就是用stack !!!,这也是本题之所以是hard的原因,因为同时牵扯到了DFS和stack的运用。

java solution:(代码存在函数传入参数过多的问题,可以通过建个数组什么的优化一下,本人比较懒就不做优化了23333)

技术分享
 1 public List<String> validParentheses(int l, int m, int n) {
 2         List<String> result = new ArrayList<String>();
 3         helper(0, 0, l, 0, 0, m, 0, 0, n, result, new StringBuilder(), new ArrayDeque<Character>());
 4         return result;
 5     }
 6 
 7     private void helper(int lleft, int lright, int l, int mleft, int mright, int m, int nleft, int nright, int n,
 8             List<String> result, StringBuilder sb, Deque<Character> deque) {
 9         if (lleft == l && lright == l && mleft == m && mright == m && nleft == n && nright == n) {
10             result.add(sb.toString());
11             return;
12         }
13         if (lleft < l) {
14             sb.append("(");
15             deque.offerLast(‘(‘);
16             helper(lleft + 1, lright, l, mleft, mright, m, nleft, nright, n, result, sb, deque);
17             sb.deleteCharAt(sb.length() - 1);
18             deque.pollLast();
19         }
20         if (lright < lleft && !deque.isEmpty() && deque.peekLast() == ‘(‘) {
21             sb.append(")");
22             deque.pollLast();
23             helper(lleft, lright + 1, l, mleft, mright, m, nleft, nright, n, result, sb, deque);
24             sb.deleteCharAt(sb.length() - 1);
25             deque.offerLast(‘(‘);
26         }
27         if (mleft < m) {
28             sb.append("[");
29             deque.offerLast(‘[‘);
30             helper(lleft, lright, l, mleft + 1, mright, m, nleft, nright, n, result, sb, deque);
31             sb.deleteCharAt(sb.length() - 1);
32             deque.pollLast();
33         }
34         if (mright < mleft && !deque.isEmpty() && deque.peekLast() == ‘[‘) {
35             sb.append("]");
36             deque.pollLast();
37             helper(lleft, lright, l, mleft, mright + 1, m, nleft, nright, n, result, sb, deque);
38             sb.deleteCharAt(sb.length() - 1);
39             deque.offerLast(‘[‘);
40         }
41         if (nleft < n) {
42             sb.append("{");
43             deque.offerLast(‘{‘);
44             helper(lleft, lright, l, mleft, mright, m, nleft + 1, nright, n, result, sb, deque);
45             sb.deleteCharAt(sb.length() - 1);
46             deque.pollLast();
47         }
48         if (nright < nleft && !deque.isEmpty() && deque.peekLast() == ‘{‘) {
49             sb.append("}");
50             deque.pollLast();
51             helper(lleft, lright, l, mleft, mright, m, nleft, nright + 1, n, result, sb, deque);
52             sb.deleteCharAt(sb.length() - 1);
53             deque.offerLast(‘{‘);
54         }
55     }
Parentheses

 

2:
Bipartite

Determine if an undirected graph is bipartite. A bipartite graph is one in which the nodes can be divided into two groups such that no nodes have direct edges to other nodes in the same group.

Examples

1  --   2

    /   

3  --   4

is bipartite (1, 3 in group 1 and 2, 4 in group 2).

1  --   2

    /   |

3  --   4

is not bipartite.

Assumptions

  • The graph is represented by a list of nodes, and the list of nodes is not null.

解题思路: 遍历图的常见算法有两种,为DFS(深度优先搜索)和BFS(广度优先搜索),此题两种算法均可,具体哪种需要考虑具体图的形状是什么。总结来说:如果图中节点分叉较多,推荐使用DFS, 如果分叉较少,推荐使用BFS。在此先给出DFS的求法,之后在总结BFS时会再更一次。

1:多少层? unknown, 知道遍历完所有节点为止。

2:每层需判断当前节点和其所有children是否为bipartite.

Java Solution:

技术分享
public boolean isBipartite(List<GraphNode> graph) {
    HashMap<GraphNode,Integer>visited=new HashMap<GraphNode, Integer>();
    for(GraphNode node: graph){
      if(!visited.containsKey(node)){
      visited.put(node,0);
      boolean result=DFS(node,visited);
      if(result == false){
        return false;
      }
    }
    }
    return true;
  }
  private boolean DFS(GraphNode node, HashMap<GraphNode,Integer> visited){
    int prevGroup=visited.get(node);
    for(GraphNode nei:node.neighbors){
      if(visited.containsKey(nei)){
        if(visited.get(nei) == prevGroup){
          return false;
        }
      }else{
        int curGroup=prevGroup == 0? 1:0;
        visited.put(nei,curGroup);
        boolean result=DFS(nei,visited);
        if(result == false){
          return false;
        }
      }
    }
    return true;
  }
Bipartite

 

3:Generate Random Maze

Randomly generate a maze of size N * N (where N = 2K + 1) whose corridor and wall’s width are both 1 cell. For each pair of cells on the corridor, there must exist one and only one path between them. (Randomly means that the solution is generated randomly, and whenever the program is executed, the solution can be different.). The wall is denoted by 1 in the matrix and corridor is denoted by 0.

Assumptions

  • N = 2K + 1 and K >= 0
  • the top left corner must be corridor
  • there should be as many corridor cells as possible
  • for each pair of cells on the corridor, there must exist one and only one path between them

Examples

N = 5, one possible maze generated is

        0  0  0  1  0

        1  1  0  1  0

        0  1  0  0  0

        0  1  1  1  0

        0  0  0  0  0

解题思路: 1:多少层? 同上题,unknown,直到走完所有节点为止。

                2:每层case: 假设2维矩阵全部是墙(全为1),我们要做的就是从(0,0)起始位置开始,随机的走一条通路出来。所以走到每一个位置的时候,我们需要考虑4种情况,既向上,向下,向左,向右,并check 这4种情况是不是能走通。因此,在做dfs时,每层需要判断4件事,既往上能不能走,往下能不能走,往左能不能走,往右能不能走。

trick: 由于题目要求墙和通道的厚度都不能超过1, 但如果我们每次只走一步的话,往回看的时候就可能造成两次的通路重复从而厚度为2的情况,所以为了解决这个情况,一个trick就是每一次走两步就好(这个可以画个图走一下就清楚了,这确实太难想了,真不知道哪个大神想出来的办法。。。。)

trick2:由于每次都判断4个方向能不能走会使代码变的非常复杂,我们可以使用java中的enum(枚举类)来简化代码的难度,关于enum具体可google java enum。

Java Solution:

技术分享
 1 enum Direction {
 2         NORTH(0, 1), SORTH(0, -1), EAST(1, 0), WEST(-1, 0);
 3         int x;
 4         int y;
 5 
 6         Direction(int x, int y) {
 7             this.x = x;
 8             this.y = y;
 9         }
10 
11         public int moveX(int x, int time) {
12             return x + this.x * time;
13         }
14 
15         public int moveY(int y, int time) {
16             return y + this.y * time;
17         }
18     }
19 
20     public int[][] maze(int n) {
21         int[][] result = new int[n][n];
22         for (int i = 0; i < n; i++) {
23             for (int j = 0; j < n; j++) {
24                 if (i == 0 && j == 0) {
25                     result[i][j] = 0;
26                 } else {
27                     result[i][j] = 1;
28                 }
29             }
30         }
31         generateMap(result, 0, 0);
32         return result;
33     }
34 
35     private void generateMap(int[][] result, int x, int y) {
36         Direction[] dir = Direction.values();
37         shuffle(dir);
38         for (int i = 0; i < dir.length; i++) {
39             int nextX = dir[i].moveX(x, 2);
40             int nextY = dir[i].moveY(y, 2);
41             if (isValid(result, nextX, nextY)) {
42                 result[dir[i].moveX(x, 1)][dir[i].moveY(y, 1)] = 0;
43                 result[nextX][nextY] = 0;
44                 generateMap(result, nextX, nextY);
45             }
46         }
47     }
48 
49     private void shuffle(Direction[] dir) {
50         for (int i = 0; i < dir.length; i++) {
51             int index = (int) (Math.random() * (dir.length - i));
52             Direction temp = dir[i];
53             dir[i] = dir[index];
54             dir[index] = temp;
55         }
56     }
57 
58     private boolean isValid(int[][] result, int x, int y) {
59         return x >= 0 && x < result.length && y >= 0 && y < result.length && result[x][y] != 0;
60     }
Generate Random Maze

 

以上是关于DFS习题复习 DFS的实际应用:括号检测,graph Bipartite及随机生成迷宫的主要内容,如果未能解决你的问题,请参考以下文章

DFS

为啥我在图中的 DFS 循环检测总是返回 true?

蓝桥杯DFS正确入门方式 | DFS + 递归与递推习题课(上) | 一节课教你爆搜!——学习笔记

DFS习题-LeetCode

复习+dfs

蓝桥杯DFS深度优先练习题——基础入门模板