美文网首页
深度优先搜索

深度优先搜索

作者: 欧阳_z | 来源:发表于2020-09-06 17:57 被阅读0次

    1、简介
    DFS(Depth-First-Search):深度优先搜索。从一个初始结点开始,沿着一条路一直走到底,如果不是目标解,就返回到上一个结点,从另一条路开始走到底,使用了回溯的思想。

    2、应用
    (1)二叉树的层序遍历
    下面是 Go 语言版的完整代码,可运行:

    package main
    
    import (
    "fmt"
    "container/list"
    )
    
    type TreeNode struct {
        Val int
        Left *TreeNode
        Right *TreeNode
    }
    
    func dfs(node *TreeNode, result *[][]int, level int) {
        if len(*result) < level + 1 {
            *result = append(*result, []int{} )
        }
        (*result)[level] = append( (*result)[level], node.Val )
    
        if nil != node.Left{
            dfs(node.Left, result, level+1)
        }
        if nil != node.Right{
            dfs(node.Right, result, level+1)
        }
    }
    
    func levelOrderDFS(root *TreeNode) [][]int {
        if nil == root{
            return [][]int{}
        }
        result := [][]int{}
        dfs(root, &result, 0)
        return result
    }
    
    func createBST( v []int ) *TreeNode {
        n := len(v)
        if n == 0{
            return nil
        }else if n == 1{
            return & TreeNode{ v[0], nil, nil}
        }
    
        m := n/2
        root := & TreeNode{ v[m], nil, nil}
        root.Left = createBST( v[0:m] )
        root.Right = createBST( v[m+1:] )
        return root
    }
    
    func main() {
        tree := createBST( []int {1,2,3,4,5,6,7} )
        fmt.Println(levelOrderDFS(tree))
    }
    

    (2)二叉树的最大深度
    某个结点的最大深度,可以先求出左子树的最大深度和右子树的最大深度中较大的那一个,再加上 1 即可。递归的结束条件是遇到空结点,返回 0

    func maxDepthDFS(root *TreeNode) int {
        if root == nil{
            return 0
        }
        max := maxDepthDFS(root.Left)
        right := maxDepthDFS(root.Right)
        if right > max{
            max = right
        }
        return 1 + max
    }
    

    (3)二叉树的最小深度:

    func minDepthDFS(root *TreeNode) int {
        if root == nil{
            return 0
        }
        min := minDepthDFS(root.Left)
        right := minDepthDFS(root.Right)
        if right < min{
            min = right
        }
        return 1 + min
    }
    

    (4)解数独
    (A) 判断数独是否有效,暴力破解
    一个简单的解决方案是 遍历该 9 x 9 数独,以确保:
    行中没有重复的数字,
    列中没有重复的数字,
    3 x 3 子数独内没有重复的数字。
    完整代码如下,外围 2 层循环,加上 isLegal 函数内的 1 层循环,时间复杂度是 O(n3)

    package main
    import "fmt"
    
    func isValid(board *[][]byte, row int, col int) bool {
        for i:=0 ; i < 9; i++ {
            if (*board)[i][col] == (*board)[row][col] && i != row {
                return false
            }
            if (*board)[row][i] == (*board)[row][col] && i != col {
                return false
            }
            r := row / 3 * 3 + i / 3
            c := col / 3 * 3 + i % 3
            if (*board)[r][c] == (*board)[row][col] && r != row && c != row {
                return false
            }
        }
        return true
    }
    
    func isValidSudoku(board [][]byte) bool {
        for i:=0 ; i < 9; i++ {
            for j:=0 ; j < 9; j++ {
                if board[i][j] == '.' || isValid(&board, i , j) {
                    continue
                }
                return false
            }
        }
        return true
    }
    
    func main() {
        input := [][]byte{
            {'5','3','.','.','7','.','.','.','.'},
            {'6','.','.','1','9','5','.','.','.'},
            {'.','9','8','.','.','.','.','6','.'},
            {'8','.','.','.','6','.','.','.','3'},
            {'4','.','.','8','.','3','.','.','1'},
            {'7','.','.','.','2','.','.','.','6'},
            {'.','6','.','.','.','.','2','8','.'},
            {'.','.','.','4','1','9','.','.','5'},
            {'.','.','.','.','8','.','.','7','9'} }
        fmt.Println(isValidSudoku(input))
    }
    

    (B) 判断数独是否有效--空间换时间
    先用数组或哈希表把状态保存起来,这样在判重的时候时间复杂度是 O(1),总的时间复杂度是 O(n2) :

    func isValidSudoku(board [][]byte) bool {
        x := [9][9]bool{}
        y := [9][9]bool{}
        box := [3][3][9]bool{}
        
        for i:=0 ; i < 9; i++ {
            for j:=0 ; j < 9; j++ {
                if board[i][j] == '.' {
                    continue
                }
                bx := i/3
                by := j/3
                n := board[i][j] - '1'
    
                if true == x[i][n] || true == y[j][n] || true == box[bx][by][n] {
                    return false
                }
                x[i][n] = true
                y[j][n] = true
                box[bx][by][n] = true
            }
        }
        return true
    }
    

    (C) 解数独
    先用数组或哈希表把状态保存起来,遍历每个空格,for 循环遍历 9 个数字,调用 couldPlace() 方法来判断,如果某个格子可以放该数字,就调用 placeNumber() 方法来更新状态 ,调用 placeNextNumbers() 方法继续下一个格子,如果这条路走不通,就再调用 removeNumber() 方法清除刚才的状态。直到找出解。

    package main
    import "fmt"
    
    const (
        n = 3
        N = n*n
    )
    
    type Sudoku struct {
        rows [N][N]bool
        columns [N][N]bool
        boxes [N][N]bool
        success bool
        pBoard *[][]byte
    }
    
    func (this *Sudoku) couldPlace(d int, row int, col int) bool {
        idx := (row / n ) * n + col / n;
        return !( this.rows[row][d] || this.columns[col][d] || this.boxes[idx][d] )
    }
    
    func (this *Sudoku) placeNumber(d int, row int, col int) {
        idx := (row / n ) * n + col / n
        this.rows[row][d] = true
        this.columns[col][d] = true
        this.boxes[idx][d] = true
        (*this.pBoard)[row][col] = byte(d + '1')
    }
    
    func (this *Sudoku) removeNumber(d int, row int, col int) {
        idx := (row / n ) * n + col / n
        this.rows[row][d] = false
        this.columns[col][d] = false
        this.boxes[idx][d] = false
        (*this.pBoard)[row][col] = '.'
    }
    
    func (this *Sudoku) placeNextNumbers(row int, col int) {
        if col == N-1 && row == N-1 {
            this.success = true
        } else {
            if col == N-1 {
                this.dfs(row + 1, 0)
            } else {
                this.dfs(row , col + 1)
            }
        }
    }
    
    func (this *Sudoku) dfs(row int, col int) {
        if (*this.pBoard)[row][col] == '.' {
            for i := 0; i < N; i++ {
                if true == this.couldPlace(i, row, col) {
                    this.placeNumber(i, row, col)
                    this.placeNextNumbers(row, col)
    
                    if false == this.success {
                        this.removeNumber(i, row, col)
                    }     
                }
            }
        } else {
            this.placeNextNumbers(row, col)
        }
    }
    
    func (this *Sudoku) init_array() {
        for i := 0; i < N; i++ {
            for j := 0; j < N; j++ {
                num := (*this.pBoard)[i][j]
                if num != '.' {
                    d := num - '1'
                    this.placeNumber( int(d), i, j)
                }
            }
        }
    }
    
    func solveSudoku(board [][]byte) {
        p := Sudoku{pBoard: &board}
        p.init_array()
        p.dfs(0, 0)
    }
    
    func printSudoku(board [][]byte) {
        fmt.Println()
        for i := 0; i < N; i++ {
            for j := 0; j < N; j++ {
                fmt.Printf( "%c  ", board[i][j])
            }
            fmt.Println()
        }
        fmt.Println()
    }
    
    func main() {
        input := [][]byte{
            {'5','3','.','.','7','.','.','.','.'},
            {'6','.','.','1','9','5','.','.','.'},
            {'.','9','8','.','.','.','.','6','.'},
            {'8','.','.','.','6','.','.','.','3'},
            {'4','.','.','8','.','3','.','.','1'},
            {'7','.','.','.','2','.','.','.','6'},
            {'.','6','.','.','.','.','2','8','.'},
            {'.','.','.','4','1','9','.','.','5'},
            {'.','.','.','.','8','.','.','7','9'} }
        printSudoku(input)
        solveSudoku(input)
        printSudoku(input)
    }
    

    相关文章

      网友评论

          本文标题:深度优先搜索

          本文链接:https://www.haomeiwen.com/subject/dqgcektx.html