再來一篇深度優先遍歷/搜索總結?

再來一篇深度優先遍歷/搜索總結?

簡介:深度優先搜索算法(Depth-First-Search, DFS),最初是一種用於遍歷或搜索樹和圖的算法,在LeetCode中很常見,雖然感受不難,可是理解起來仍是有點難度的。html

簡要歸納,深度優先的主要思想就是「不撞南牆不回頭」,「一條路走到黑」,若是遇到「牆」或者「無路可走」時再去走下一條路。java

思路

假如對樹進行遍歷,沿着樹的深度遍歷樹的節點,儘量深的搜索樹的分支,當達到邊際時回溯上一個節點再進行搜索。以下圖的一個二叉樹。
node

首先給出這個二叉樹的深度優先遍歷的結果(假定先走左子樹):1->2->4->5->3->6->7git

那是怎樣獲得這樣的結果呢?

根據深度優先遍歷的概念:沿着這樹的某一分支向下遍歷到不能再深刻爲止,以後進行回溯再選定新的分支。算法

定義節點編程

class TreeNode{
    int val;
    TreeNode left;
    TreeNode right;
}

遞歸的方式數據結構

分別對左右子樹進行遞歸,一直到底才進行回溯。若是不瞭解遞歸能夠參考個人博客你真的懂遞歸嗎?code

class Solution{
    public void depthOrderTraversalWithRecusive(TreeNode root){
        if(root == null){
            return;
        }
        System.out.print(root.val +"->");
        depthOrderTraversalWithRecusive(root.left);
        depthOrderTraversalWithRecusive(root.right);
    }
}

迭代的方式htm

上面實現了遞歸方式的深度優先遍歷,也能夠利用棧把遞歸轉換爲迭代的方式。blog

可是爲了保證出棧的順序,須要先壓入右節點,再壓左節點。

class Solution{
    public void depthOrderTraversalWithoutRecusive(TreeNode root){
        if(root == null) return;
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            System.out.print(node.val + "->");
            if(node.right != null){
                stack.push(node.right);
            }
            if(node.left != null){
                stack.push(node.left);
            }
        }
    }
}

接着再列舉個利用深度優先遍歷的方式的題目

掃雷

給定一個表示遊戲板的二維字符矩陣,'M'表示一個未挖出的地雷,'E'表示一個未挖出的空方塊,'B' 表明沒有相鄰(上,下,左,右,和全部4個對角線)地雷的已挖出的空白方塊,數字('1''8')表示有多少地雷與這塊已挖出的方塊相鄰,'X' 則表示一個已挖出的地雷。

根據如下規則,返回相應位置被點擊後對應的面板:

  • 若是一個地雷('M')被挖出,遊戲就結束了- 把它改成 'X'
  • 若是一個沒有相鄰地雷的空方塊('E')被挖出,修改它爲('B'),而且全部和其相鄰的方塊都應該被遞歸地揭露。
  • 若是一個至少與一個地雷相鄰的空方塊('E')被挖出,修改它爲數字('1''8'),表示相鄰地雷的數量。
  • 若是在這次點擊中,若無更多方塊可被揭露,則返回面板。

示例

輸入: 

[['E', 'E', 'E', 'E', 'E'],
 ['E', 'E', 'M', 'E', 'E'],
 ['E', 'E', 'E', 'E', 'E'],
 ['E', 'E', 'E', 'E', 'E']]

Click : [3,0]

輸出: 

[['B', '1', 'E', '1', 'B'],
 ['B', '1', 'M', '1', 'B'],
 ['B', '1', '1', '1', 'B'],
 ['B', 'B', 'B', 'B', 'B']]

思路:根據給定的規則,當給定一個Click座標,當不爲雷的時候以此座標爲基點向四周8個方向進行深度遍歷,把空格E填充爲B,而且把與地雷M相連的空方塊標記相鄰地雷的數量。

注意

在這個題中能夠沿着8個方向遞歸遍歷,全部要注意程序中,採用了兩個for循環能夠實現向8個方向遞歸。

for(int i=-1;i<=1;i++){
    for(int j=-1;j<=1;j++){

   	}
}

本程序須要進行返回board,在最後須要進行返回。

編程步驟

  • Click給出的座標找出的是地雷,直接返回。
  • 不然,進行遞歸,並標出雷的數量。
class Solution{
    public char[][] updateBoard(char[][] board,int[] click){
        if(board[click[0]][click[1]] == 'M'){
            board[click[0]][click[1]] = 'X';
            return board;
        }
        return click(board,click[0],click[1]);
    }
    private char[][] click(char[][] board,int x,int y){
        int num = getNum(board, x,y);
        if(num == 0){
            board[x][y] = 'B';
        }else{
            board[x][y] = Character.forDigit(num,10);
            return board;
        }
        //遞歸
        for(int i=-1;i<=1;i++){
            for(int j=-1;j<=1;j++){
                if(x + i >= 0 && x + i < board.length&&y + j >=0&&y+j<board[0].length&&board[x+i][y+j]=='E'){
                    board = click(board,x+i,y+j);
                }
            }
        }
        return board;
    }
    private int getNum(char[][] board,int x,int y){
        int num = 0;
        for(int i=-1;i<=1;i++){
            for(int j=-1;j<=1;j++){
                if(x + i >= 0&&y + j >=0&&x+i<board.length&&y+j<board[0].length&&board[x+i][y+j]=='M'){
                    num ++;
                }
            }
        }
        return num;
    }
}

總結 :深度優先遍歷不只存在樹和圖的數據結構中,還有不少也能夠用到它。須要肯定的是每一步該怎麼走,有幾個方向能夠走。

參考

相關文章
相關標籤/搜索