九、leetcode刷題之【DFS】

[TOC]

深度優(yōu)先遍歷

定義

「一條路走到底路克,不撞南墻不回頭」樟结。深度優(yōu)先遍歷 只要前面有可以走的路,就會(huì)一直向前走精算,直到無路可走才會(huì)回頭瓢宦;

無路可走有兩種情況:
① 遇到了墻;
② 遇到了已經(jīng)走過的路灰羽;

在無路可走的時(shí)候驮履,沿著原路返回,直到回到了還有未走過的路的路口廉嚼,嘗試?yán)^續(xù)走沒有走過的路徑玫镐;有一些路徑?jīng)]有走到,這是因?yàn)檎业搅顺隹诘≡耄绦蚓屯V沽耍?/p>

樹的深度優(yōu)先遍歷

二叉樹的深度優(yōu)先遍歷從「根結(jié)點(diǎn)」開始恐似,依次 「遞歸地」 遍歷「左子樹」的所有結(jié)點(diǎn)和「右子樹」的所有結(jié)點(diǎn)。

二叉樹的深度優(yōu)先遍歷還可以分為:前序遍歷傍念、中序遍歷和后序遍歷矫夷。

  1. 前序遍歷
    對(duì)于任意一棵子樹,先輸出根結(jié)點(diǎn)憋槐,再遞歸輸出左子樹的 所有 結(jié)點(diǎn)双藕、最后遞歸輸出右子樹的 所有 結(jié)點(diǎn)。上圖前序遍歷的結(jié)果就是深度優(yōu)先遍歷的結(jié)果:[0阳仔、1忧陪、3、4近范、7嘶摊、2、5顺又、8更卒、9、6稚照、10]蹂空。

  2. 中序遍歷
    對(duì)于任意一棵子樹俯萌,先遞歸輸出左子樹的 所有 結(jié)點(diǎn),然后輸出根結(jié)點(diǎn)上枕,最后遞歸輸出右子樹的 所有 結(jié)點(diǎn)咐熙。上圖中序遍歷的結(jié)果是:[3、1辨萍、7棋恼、4、0锈玉、8爪飘、5、9拉背、2师崎、10、6]椅棺。

  3. 后序遍歷(重要)
    對(duì)于任意一棵子樹犁罩,總是先遞歸輸出左子樹的 所有 結(jié)點(diǎn),然后遞歸輸出右子樹的 所有 結(jié)點(diǎn)两疚,最后輸出根結(jié)點(diǎn)床估。后序遍歷體現(xiàn)的思想是:先必需得到左右子樹的結(jié)果,才能得到當(dāng)前子樹的結(jié)果诱渤,這一點(diǎn)在解決一些問題的過程中非常有用丐巫。上圖后序遍歷的結(jié)果是:[3、7源哩、4鞋吉、1鸦做、8励烦、9、5泼诱、10坛掠、6、2治筒、0]屉栓。

性質(zhì) 1:二叉樹的 前序遍歷 序列,根結(jié)點(diǎn)一定是 最先 訪問到的結(jié)點(diǎn)耸袜;
性質(zhì) 2:二叉樹的 后序遍歷 序列友多,根結(jié)點(diǎn)一定是 最后 訪問到的結(jié)點(diǎn);
性質(zhì) 3:根結(jié)點(diǎn)把二叉樹的 中序遍歷 序列劃分成兩個(gè)部分堤框,第一部分的所有結(jié)點(diǎn)構(gòu)成了根結(jié)點(diǎn)的左子樹域滥,第二部分的所有結(jié)點(diǎn)構(gòu)成了根結(jié)點(diǎn)的右子樹纵柿。

圖的深度優(yōu)先遍歷

深度優(yōu)先遍歷有「回頭」的過程,在樹中由于不存在「環(huán)」(回路)启绰,對(duì)于每一個(gè)結(jié)點(diǎn)來說昂儒,每一個(gè)結(jié)點(diǎn)只會(huì)被遞歸處理一次。而「圖」中由于存在「環(huán)」(回路)委可,就需要 記錄已經(jīng)被遞歸處理的結(jié)點(diǎn)(通常使用布爾數(shù)組或者哈希表)渊跋,以免結(jié)點(diǎn)被重復(fù)遍歷到。

深度優(yōu)先遍歷的兩種實(shí)現(xiàn)方式

在深度優(yōu)先遍歷的過程中着倾,需要將 當(dāng)前遍歷到的結(jié)點(diǎn) 的相鄰結(jié)點(diǎn) 暫時(shí)保存 起來拾酝,以便在回退的時(shí)候可以繼續(xù)訪問它們。遍歷到的結(jié)點(diǎn)的順序呈現(xiàn)「后進(jìn)先出」的特點(diǎn)卡者,因此 深度優(yōu)先遍歷可以通過「椢⒈Γ」實(shí)現(xiàn)。

深度優(yōu)先遍歷有以下兩種方式:

編寫遞歸方法虎眨;
編寫棧蟋软,通過迭代的方式實(shí)現(xiàn)。

深度優(yōu)先的應(yīng)用

應(yīng)用 1:獲得圖(樹)的一些屬性

104. 二叉樹的最大深度(簡(jiǎn)單)


func maxDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    queue :=[]*TreeNode{root}       // 輔助隊(duì)列,根節(jié)點(diǎn)入隊(duì)
    depth := 0                  // 初始化深度為0

    for len(queue) > 0 { // 當(dāng)隊(duì)列不為空時(shí)嗽桩,循環(huán)以下操作
        size := len(queue)//當(dāng)前隊(duì)列中元素個(gè)數(shù)size作為限定:當(dāng)前層級(jí)中節(jié)點(diǎn)數(shù)量
        for i := 0; i < size; i++ { // 遍歷當(dāng)前層級(jí)中的節(jié)點(diǎn)
            s := queue[0]      // 獲取隊(duì)首元素
            if s.Left != nil { // 如果左子樹不為空岳守,左子樹入隊(duì)
                queue = append(queue, s.Left)
            }
            if s.Right != nil { // 如果右子樹不為空,右子樹入隊(duì)
                queue = append(queue, s.Right)
            }
            queue = queue[1:]  // 隊(duì)首元素出隊(duì)
        }
        depth++ // for循環(huán)結(jié)束后這一層級(jí)節(jié)點(diǎn)已經(jīng)訪問結(jié)束碌冶,深度加+1
    }
    return depth
}

111. 二叉樹的最小深度【簡(jiǎn)單/BFS/DFS】

// ============== 解法一: BFS 迭代實(shí)現(xiàn)湿痢,廣度優(yōu)先遍歷 ================
// https://www.cnblogs.com/Lusai/p/15709094.html
/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
 
 
func minDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    queue :=[]*TreeNode{root}     // 查找隊(duì)列,將起點(diǎn)加入隊(duì)列
    depth := 1                  // root 本身就是一層,depth初始化為1
    for len(queue)!=0{
        size := len(queue)
        // 將當(dāng)前隊(duì)列中的所有結(jié)點(diǎn)向四周擴(kuò)散
        for i := 0; i < size; i++ {
            cur := queue[0]
            // 判斷是否到終點(diǎn)
            if cur.Left == nil && cur.Right == nil {
                return depth
            }
            // 將 cur的相鄰節(jié)點(diǎn)加入隊(duì)列
            if cur.Left != nil {
                queue = append(queue, cur.Left)
            }
            if cur.Right != nil {
                queue = append(queue, cur.Right)
            }
            // 去掉當(dāng)前節(jié)點(diǎn)
            queue = queue[1:]
        }
        // 這里增加深度
        depth++
    }
    return depth
}



// ============== 解法二: DFS   ================

func minDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    if root.Left != nil && root.Right == nil {
        return 1 + minDepth(root.Left)
    }
    if root.Right != nil && root.Left == nil {
        return 1 + minDepth(root.Right)
    }
    return 1 + Min(minDepth(root.Left), minDepth(root.Right))
}

112. 路徑總和(簡(jiǎn)單)

113. 路徑總和 II(中等)

翻轉(zhuǎn)二叉樹

相同的樹

對(duì)稱二叉樹

求根到葉子節(jié)點(diǎn)數(shù)字之和

236. 二叉樹的最近公共祖先(中等)

 

// ======  解法二 利用hash表和存儲(chǔ)走過的路 ==============
/*
思路:我們可以用哈希表存儲(chǔ)所有節(jié)點(diǎn)的父節(jié)點(diǎn)扑庞,然后我們就可以利用節(jié)點(diǎn)的父節(jié)點(diǎn)信息從 p 結(jié)點(diǎn)開始不斷往上跳譬重,并記錄已經(jīng)訪問過的節(jié)點(diǎn)
,再?gòu)?q 節(jié)點(diǎn)開始不斷往上跳罐氨,如果碰到已經(jīng)訪問過的節(jié)點(diǎn)臀规,那么這個(gè)節(jié)點(diǎn)就是我們要找的最近公共祖先。
算法:
從根節(jié)點(diǎn)開始遍歷整棵二叉樹栅隐,用哈希表記錄每個(gè)節(jié)點(diǎn)的父節(jié)點(diǎn)指針塔嬉。
從 p 節(jié)點(diǎn)開始不斷往它的祖先移動(dòng),并用數(shù)據(jù)結(jié)構(gòu)記錄已經(jīng)訪問過的祖先節(jié)點(diǎn)租悄。
同樣谨究,我們?cè)購(gòu)?q 節(jié)點(diǎn)開始不斷往它的祖先移動(dòng),如果有祖先已經(jīng)被訪問過泣棋,即意味著這是 p 和 q 的深度最深的公共祖先送矩,即 LCA 節(jié)點(diǎn)抑淫。
*/

func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
    parent := map[int]*TreeNode{}
    visited := map[int]bool{}

    var dfs func(*TreeNode)

    dfs = func(r *TreeNode) {
        if r == nil {
            return
        }
        if r.Left != nil {
            parent[r.Left.Val] = r
            dfs(r.Left)
        }
        if r.Right != nil {
            parent[r.Right.Val] = r
            dfs(r.Right)
        }
    }

    dfs(root)

    for p != nil {
        visited[p.Val] = true
        p = parent[p.Val]
    }
    for q != nil {
        if visited[q.Val] {
            return q
        }
        q = parent[q.Val]
    }

    return nil

}


// func lowestCommonAncestor(root, p, q *TreeNode) *TreeNode {
//  if root == nil {
//      return nil
//  }

//  // 如果p,q為根節(jié)點(diǎn),則公共祖先為根節(jié)點(diǎn)
//  if root.Val == p.Val || root.Val == q.Val {
//      return root
//  }

//  // 如果p,q在左子樹症副,則公共祖先在左子樹查找
//  if find(root.Left, p) && find(root.Left, q) {
//      return lowestCommonAncestor(root.Left, p, q)
//  }

//  // 如果p,q在右子樹,則公共祖先在右子樹查找
//  if find(root.Right, p) && find(root.Right, q) {
//      return lowestCommonAncestor(root.Right, p, q)

//  }

//  // 如果p,q分屬兩側(cè),則公共祖先為根節(jié)點(diǎn)
//  return root

// }

// func find(root, c *TreeNode) bool {
//  if root == nil {
//      return false
//  }
//  if root.Val == c.Val {
//      return true
//  }
//  return find(root.Left, c) || find(root.Right, c)
// }

從前序與中序遍歷序列構(gòu)造二叉樹

從中序與后序遍歷序列構(gòu)造二叉樹

前序遍歷構(gòu)造二叉搜索樹

從先序遍歷還原二叉樹

二叉樹的前序遍歷

應(yīng)用 2:計(jì)算無向圖的連通分量

323

應(yīng)用 3:檢測(cè)圖中是否存在環(huán)

第 684 題:冗余連接(中等)

第 802 題:找到最終的安全狀態(tài)(中等)

應(yīng)用 4:二分圖檢測(cè)

第 785 題:判斷二分圖(中等)

應(yīng)用 5:拓?fù)渑判?/h3>

第 210 題:課程表 II(中等)

應(yīng)用 6:回溯算法獲得一個(gè)問題所有的解

回溯算法是深度優(yōu)先遍歷思想的應(yīng)用.回溯算法是一種通過不斷 嘗試 ,搜索一個(gè)問題的一個(gè)解或者 所有解 的方法。在求解的過程中棚辽,如果繼續(xù)求解不能得到題目要求的結(jié)果,就需要 回退 到上一步嘗試新的求解路徑冰肴∏辏回溯算法的核心思想是:在一棵 隱式的樹(看不見的樹) 上進(jìn)行一次深度優(yōu)先遍歷。
完成「力扣」第 22 題:括號(hào)生成(中等)熙尉;
完成「力扣」第 17 題:電話號(hào)碼的字母組合(中等)联逻;
完成「力扣」第 784 題:字母大小寫全排列(中等)。

Leetcode題

利用DFS通殺島嶼系列

二叉樹遍歷框架

type TreeNode struct {
    Val   int
    Left  *TreeNode
    Right *TreeNode
}

func traverse(root *TreeNode) {
   traverse(root.Left)
   traverse(root.Right)
}

二維矩陣的 DFS 代碼框架

// 二維矩陣遍歷框架一
func dfs_array(grid [][]int, visited [][]bool, i int, j int) {
   row, col := len(grid), len(grid[0])
   if i < 0 || i >= row || j < 0 || j >= col || visited[i][j] {
      return
   }
   visited[i][j] = true
   dfs_array(grid, visited, i-1, j) // 上
   dfs_array(grid, visited, i+1, j) // 下
   dfs_array(grid, visited, i, j-1) // 左
   dfs_array(grid, visited, i, j+1) // 右
}


// 二維矩陣遍歷框架二
func dfs_array2(grid [][]int, visited [][]bool, i int, j int) {
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    row, col := len(grid), len(grid[0])
    if i < 0 || i >= row || j < 0 || j >= col || visited[i][j] {
        return
    }
    visited[i][j] = true
    for _, v := range dir {
        next_row := i + v[0]
        next_col := j + v[1]
        dfs_array2(grid, visited, next_row, next_col)
    }
}

200. 島嶼數(shù)量(中等)

給你一個(gè)由 '1'(陸地)和 '0'(水)組成的的二維網(wǎng)格检痰,請(qǐng)你計(jì)算網(wǎng)格中島嶼的數(shù)量包归。島嶼總是被水包圍,并且每座島嶼只能由水平方向和/或豎直方向上相鄰的陸地連接形成铅歼。此外公壤,你可以假設(shè)該網(wǎng)格的四條邊均被水包圍。

輸入:grid = [["1","1","1","1","0"],["1","1","0","1","0"],["1","1","0","0","0"],["0","0","0","0","0"]]
輸出:1

輸入:grid = [["1","1","0","0","0"],["1","1","0","0","0"],["0","0","1","0","0"],["0","0","0","1","1"]]
輸出:3
func main() {
    grid := [][]byte{
        {'1', '1', '0', '0', '0'},
        {'1', '1', '0', '0', '0'},
        {'1', '1', '0', '0', '0'},
        {'1', '1', '0', '0', '0'},
        {'0', '0', '1', '0', '0'},
        {'0', '0', '0', '1', '1'}}
    fmt.Println(numIslands(grid))
}

/*
============   解法一: DFS ========================
思路:
先找到島嶼椎椰,然后利用DFS將島嶼周邊相連的都變?yōu)楹#@里不用visited來記錄了慨飘,可以直接利用=海來剪枝
*/
func numIslands(grid [][]byte) int {
    row, col := len(grid), len(grid[0])

    res := 0
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid[i][j] == '1' {
                res++
                dfs_array2(grid, i, j)
            }
        }
    }
    return res

}

// 二維矩陣遍歷框架二
func dfs_array2(grid [][]byte, i int, j int) {
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    row, col := len(grid), len(grid[0])
    if i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == '0' {
        return
    }
    grid[i][j] = '0'
    for _, v := range dir {
        next_row := i + v[0]
        next_col := j + v[1]
        dfs_array2(grid, next_row, next_col)
    }
}

1254. 統(tǒng)計(jì)封閉島嶼的數(shù)目(中等)

二維矩陣 grid 由 0 (土地)和 1 (水)組成确憨。島是由最大的4個(gè)方向連通的 0 組成的群,封閉島是一個(gè) 完全 由1包圍(左瓤的、上休弃、右、下)的島圈膏。請(qǐng)返回 封閉島嶼 的數(shù)目塔猾。

輸入:grid = [[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]]
輸出:2
解釋:
灰色區(qū)域的島嶼是封閉島嶼,因?yàn)檫@座島嶼完全被水域包圍(即被 1 區(qū)域包圍)本辐。那么如何判斷「封閉島嶼」呢桥帆?其實(shí)很簡(jiǎn)單,把那些靠邊的島嶼排除掉慎皱,剩下的不就是 封閉島嶼
func main() {
    grid := [][]int{
        {1, 1, 1, 1, 1, 1, 1, 0},
        {1, 0, 0, 0, 0, 1, 1, 0},
        {1, 0, 1, 0, 1, 1, 1, 0},
        {1, 0, 0, 0, 0, 1, 0, 1},
        {1, 1, 1, 1, 1, 1, 1, 0}}
    fmt.Println(closedIsland(grid))
}

/*
===========  解法一: DFS  ===========
先把周邊的島嶼直接填海,再來做題叶骨,相當(dāng)于數(shù)據(jù)進(jìn)行一個(gè)預(yù)處理茫多。
*/
func closedIsland(grid [][]int) int {
    row, col := len(grid), len(grid[0])
    for j := 0; j < col; j++ {
        dfs_array(grid, 0, j)     // 把靠上邊的島嶼淹掉
        dfs_array(grid, row-1, j) // 把靠下邊的島嶼淹掉
    }

    for i := 0; i < row; i++ {
        dfs_array(grid, i, 0)     // 把靠左邊的島嶼淹掉
        dfs_array(grid, i, col-1) // 把靠右邊的島嶼淹掉
    }

    res := 0
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid[i][j] == 0 { // 此題中0是島
                res++
                dfs_array(grid, i, j) // 因?yàn)檫B在一起算是一個(gè)島,所以這里是把連在一起的全都找出來忽刽,填海
            }
        }
    }
    return res
}

// 二維矩陣遍歷框架二
func dfs_array(grid [][]int, i int, j int) {
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    row, col := len(grid), len(grid[0])
    if i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == 1 { // 此題中1是海
        return
    }
    grid[i][j] = 1
    for _, v := range dir {
        next_row := i + v[0]
        next_col := j + v[1]
        dfs_array(grid, next_row, next_col)
    }
}

1020. 飛地的數(shù)量(中等)

給你一個(gè)大小為 m x n 的二進(jìn)制矩陣 grid 天揖,其中 0 表示一個(gè)海洋單元格夺欲、1 表示一個(gè)陸地單元格。
一次 移動(dòng) 是指從一個(gè)陸地單元格走到另一個(gè)相鄰(上今膊、下些阅、左、右)的陸地單元格或跨過 grid 的邊界斑唬。
返回網(wǎng)格中 無法 在任意次數(shù)的移動(dòng)中離開網(wǎng)格邊界的陸地單元格的數(shù)量市埋。

輸入:grid = [[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
輸出:3
解釋:有三個(gè) 1 被 0 包圍。一個(gè) 1 沒有被包圍恕刘,因?yàn)樗谶吔缟稀?
輸入:grid = [[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]]
輸出:0
解釋:所有 1 都在邊界上或可以到達(dá)邊界缤谎。
func main() {
    grid := [][]int{{0, 0, 0, 0}, {1, 0, 1, 0}, {0, 1, 1, 0}, {0, 0, 0, 0}}
    fmt.Println(numEnclaves(grid))
}

func numEnclaves(grid [][]int) int {
    // 先數(shù)據(jù)預(yù)處理一下,把靠邊的島嶼都填成海
    row, col := len(grid), len(grid[0])
    for j := 0; j < col; j++ {
        dfs_array(grid, 0, j)     // 把靠上邊的島嶼淹掉
        dfs_array(grid, row-1, j) // 把靠下邊的島嶼淹掉
    }

    for i := 0; i < row; i++ {
        dfs_array(grid, i, 0)     // 把靠左邊的島嶼淹掉
        dfs_array(grid, i, col-1) // 把靠右邊的島嶼淹掉
    }

    res := 0
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid[i][j] == 1 { // 此題中1是島
                res++
            }
        }
    }
    return res
}

func dfs_array(grid [][]int, i int, j int) {
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    row, col := len(grid), len(grid[0])
    if i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == 0 { // 此題中0是海
        return
    }
    grid[i][j] = 0
    for _, v := range dir {
        next_row := i + v[0]
        next_col := j + v[1]
        dfs_array(grid, next_row, next_col)
    }
}

695. 島嶼的最大面積(中等)

給你一個(gè)大小為 m x n 的二進(jìn)制矩陣 grid 褐着。島嶼 是由一些相鄰的 1 (代表土地) 構(gòu)成的組合坷澡,這里的「相鄰」要求兩個(gè) 1 必須在 水平或者豎直的四個(gè)方向上 相鄰。你可以假設(shè) grid 的四個(gè)邊緣都被 0(代表水)包圍著含蓉。島嶼的面積是島上值為 1 的單元格的數(shù)目频敛。計(jì)算并返回 grid 中最大的島嶼面積。如果沒有島嶼馅扣,則返回面積為 0 姻政。

輸入:grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
輸出:6
解釋:答案不應(yīng)該是 11 ,因?yàn)閸u嶼只能包含水平或垂直這四個(gè)方向上的 1 岂嗓。

[圖片上傳失敗...(image-f9303f-1671480093471)]

// ================  解法一: DFS ===================
func maxAreaOfIsland(grid [][]int) int {
    row, col := len(grid), len(grid[0])

    res := 0
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid[i][j] == 1 { // 此題中1是島
                res = max(res, dfs_array(grid, i, j))
            }
        }
    }
    return res

}

func dfs_array(grid [][]int, i int, j int) int {
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    row, col := len(grid), len(grid[0])
    if i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == 0 { // 此題中0是海
        return 0
    }
    grid[i][j] = 0
    count := 1 // 其實(shí)這里設(shè)置為1汁展,真的很不好理解
    for _, v := range dir {
        next_row := i + v[0]
        next_col := j + v[1]
        count += dfs_array(grid, next_row, next_col)
    }
    return count
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

// ============== 解法二: BFS =========
func maxAreaOfIsland(grid [][]int) int {
    row, col := len(grid), len(grid[0])
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    ans := 0

    // 這里還真不能一次性把所有的島頭都找出來,因?yàn)檎翌^是找=1厌殉,如果不伴隨著填海一起找食绿,會(huì)有問題。
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid[i][j] == 0 {
                continue
            }

            // 這里一定要注意公罕,找到島嶼的頭器紧,然后加入queue, count要從1開始,然后再把島嶼的頭填為海
            queue := [][]int{{i, j}} // 找到頭
            count := 1
            grid[i][j] = 0

            for len(queue) != 0 {
                top := queue[0]
                for _, v := range dir {
                    newRow, newCol := top[0]+v[0], top[1]+v[1]
                    if newRow < 0 || newRow >= row || newCol < 0 || newCol >= col || grid[newRow][newCol] == 0 {
                        continue
                    }
                    grid[newRow][newCol] = 0
                    count++
                    queue = append(queue, []int{newRow, newCol})
                }
                queue = queue[1:]
            }
            ans = max(ans, count)
        }
    }
    return ans

}

1905. 統(tǒng)計(jì)子島嶼(中等)

給你兩個(gè) m x n 的二進(jìn)制矩陣 grid1 和 grid2 楼眷,它們只包含 0 (表示水域)和 1 (表示陸地)铲汪。一個(gè) 島嶼 是由 四個(gè)方向 (水平或者豎直)上相鄰的 1 組成的區(qū)域。任何矩陣以外的區(qū)域都視為水域罐柳。

如果 grid2 的一個(gè)島嶼掌腰,被 grid1 的一個(gè)島嶼 完全 包含,也就是說 grid2 中該島嶼的每一個(gè)格子都被 grid1 中同一個(gè)島嶼完全包含张吉,那么我們稱 grid2 中的這個(gè)島嶼為 子島嶼 齿梁。

請(qǐng)你返回 grid2 中 子島嶼 的 數(shù)目 。

輸入:grid1 = {{1,1,1,0,0},{0,1,1,1,1},{0,0,0,0,0},{1,0,0,0,0},{1,1,0,1,1}}, grid2 = {{1,1,1,0,0},{0,0,1,1,1},{0,1,0,0,0},{1,0,1,1,0},{0,1,0,1,0}}
輸出:3
解釋:如上圖所示,左邊為 grid1 勺择,右邊為 grid2 创南。
grid2 中標(biāo)紅的 1 區(qū)域是子島嶼,總共有 3 個(gè)子島嶼省核。

[圖片上傳失敗...(image-58379c-1671480093471)]


func main() {
    grid1 := [][]int{
        {1, 1, 1, 0, 0},
        {0, 1, 1, 1, 1},
        {0, 0, 0, 0, 0},
        {1, 0, 0, 0, 0},
        {1, 1, 0, 1, 1}}
    grid2 := [][]int{
        {1, 1, 1, 0, 0},
        {0, 0, 1, 1, 1},
        {0, 1, 0, 0, 0},
        {1, 0, 1, 1, 0},
        {0, 1, 0, 1, 0}}

    fmt.Println(countSubIslands(grid1, grid2))
}

/*
什么情況下 grid2 中的一個(gè)島嶼 B 是 grid1 中的一個(gè)島嶼 A 的子島稿辙?
當(dāng)島嶼 B 中所有陸地在島嶼 A 中也是陸地的時(shí)候,島嶼 B 是島嶼 A 的子島气忠。
反過來說邻储,如果島嶼 B 中存在一片陸地,在島嶼 A 的對(duì)應(yīng)位置是海水笔刹,那么島嶼 B 就不是島嶼 A 的子島芥备。
只要遍歷 grid2 中的所有島嶼,把那些不可能是子島的島嶼排除掉舌菜,剩下的就是子島萌壳。
*/

func countSubIslands(grid1 [][]int, grid2 [][]int) int {
    // 先數(shù)據(jù)預(yù)處理一下
    row, col := len(grid1), len(grid1[0])
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid2[i][j] == 1 && grid1[i][j] == 0 {
                dfs_array(grid2, i, j)
            }
        }
    }

    // 現(xiàn)在 grid2 中剩下的島嶼都是子島,計(jì)算島嶼數(shù)量
    res := 0
    for i := 0; i < row; i++ {
        for j := 0; j < col; j++ {
            if grid2[i][j] == 1 {
                res++
                dfs_array(grid2, i, j)
            }
        }
    }
    return res

}

func dfs_array(grid [][]int, i int, j int) {
    dir := [][]int{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}
    row, col := len(grid), len(grid[0])
    if i < 0 || i >= row || j < 0 || j >= col || grid[i][j] == 0 { // 此題中0是海
        return
    }
    grid[i][j] = 0
    for _, v := range dir {
        next_row := i + v[0]
        next_col := j + v[1]
        dfs_array(grid, next_row, next_col)
    }
}

回溯算法 (處理 排列-組合-子集 問題)

回溯算法和我們常說的 DFS 算法非常類似日月,本質(zhì)上就是一種暴力窮舉算法袱瓮。回溯算法和 DFS 算法的細(xì)微差別是:**回溯算法是在遍歷「樹枝」爱咬,DFS 算法是在遍歷「節(jié)點(diǎn)」尺借。

https://labuladong.gitee.io/algo/4/31/106/

216. 組合總和 III(中等)

找出所有相加之和為 n 的 k 個(gè)數(shù)的組合,且滿足下列條件:
只使用數(shù)字1到9
每個(gè)數(shù)字 最多使用一次 
返回 所有可能的有效組合的列表 精拟。該列表不能包含相同的組合兩次燎斩,組合可以以任何順序返回。

輸入: k = 3, n = 7
輸出: [[1,2,4]]
解釋:
1 + 2 + 4 = 7
沒有其他符合的組合了蜂绎。

輸入: k = 3, n = 9
輸出: [[1,2,6], [1,3,5], [2,3,4]]
解釋:
1 + 2 + 6 = 9
1 + 3 + 5 = 9
2 + 3 + 4 = 9
沒有其他符合的組合了栅表。
 
輸入: k = 4, n = 1
輸出: []
解釋: 不存在有效的組合。
在[1,9]范圍內(nèi)使用4個(gè)不同的數(shù)字师枣,我們可以得到的最小和是1+2+3+4 = 10怪瓶,因?yàn)?0 > 1,沒有有效的組合践美。

39. 組合總和(中等)

給你一個(gè) 無重復(fù)元素 的整數(shù)數(shù)組 candidates 和一個(gè)目標(biāo)整數(shù) target 洗贰,找出 candidates 中可以使數(shù)字和為目標(biāo)數(shù) target 的 所有 不同組合 ,并以列表形式返回陨倡。你可以按 任意順序 返回這些組合敛滋。candidates 中的 同一個(gè) 數(shù)字可以 無限制重復(fù)被選取 。如果至少一個(gè)數(shù)字的被選數(shù)量不同玫膀,則兩種組合是不同的矛缨。 對(duì)于給定的輸入,保證和為 target 的不同組合數(shù)少于 150 個(gè)帖旨。

輸入:candidates = [2,3,6,7], target = 7
輸出:[[2,2,3],[7]]
解釋:
2 和 3 可以形成一組候選箕昭,2 + 2 + 3 = 7 。注意 2 可以使用多次解阅。
7 也是一個(gè)候選落竹, 7 = 7 。
僅有這兩種組合货抄。

輸入: candidates = [2,3,5], target = 8
輸出: [[2,2,2,2],[2,3,3],[3,5]]

40. 組合總和 II(中等)

給定一個(gè)候選人編號(hào)的集合 candidates 和一個(gè)目標(biāo)數(shù) target 述召,找出 candidates 中所有可以使數(shù)字和為 target 的組合。candidates 中的每個(gè)數(shù)字在每個(gè)組合中只能使用 一次 蟹地。
注意:解集不能包含重復(fù)的組合积暖。 

輸入: candidates = [10,1,2,7,6,1,5], target = 8,
輸出:[[1,1,6],[1,2,5],[1,7],[2,6]]

輸入: candidates = [2,5,2,1,2], target = 5,
輸出:[[1,2,2],[5]]

46. 全排列(中等)

給定一個(gè)不含重復(fù)數(shù)字的數(shù)組 nums ,返回其 所有可能的全排列 怪与。你可以 按任意順序 返回答案夺刑。
輸入:nums = [1,2,3]
輸出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

輸入:nums = [0,1]
輸出:[[0,1],[1,0]]

輸入:nums = [1]
輸出:[[1]]

47. 全排列 II(中等)

給定一個(gè)可包含重復(fù)數(shù)字的序列 nums ,按任意順序 返回所有不重復(fù)的全排列分别。

輸入:nums = [1,1,2]
輸出:[[1,1,2],[1,2,1],[2,1,1]]
 
輸入:nums = [1,2,3]
輸出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

77. 組合(中等)

給定兩個(gè)整數(shù) n 和 k遍愿,返回范圍 [1, n] 中所有可能的 k 個(gè)數(shù)的組合。你可以按 任何順序 返回答案耘斩。

輸入:n = 4, k = 2
輸出:[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4],]

輸入:n = 1, k = 1
輸出:[[1]]

78. 子集(中等)

給你一個(gè)整數(shù)數(shù)組 nums 沼填,數(shù)組中的元素 互不相同 。返回該數(shù)組所有可能的子集(冪集)括授。解集 不能 包含重復(fù)的子集坞笙。你可以按 任意順序 返回解集。

輸入:nums = [1,2,3]
輸出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

輸入:nums = [0]
輸出:[[],[0]]

90. 子集 II(中等)

給你一個(gè)整數(shù)數(shù)組 nums 荚虚,其中可能包含重復(fù)元素薛夜,請(qǐng)你返回該數(shù)組所有可能的子集(冪集)。解集 不能 包含重復(fù)的子集曲管。返回的解集中却邓,子集可以按 任意順序 排列。

輸入:nums = [1,2,2]
輸出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

輸入:nums = [0]
輸出:[[],[0]]

劍指 Offer II 079. 所有子集(中等)

給定一個(gè)整數(shù)數(shù)組 nums 院水,數(shù)組中的元素 互不相同 腊徙。返回該數(shù)組所有可能的子集(冪集)。解集 不能 包含重復(fù)的子集檬某。你可以按 任意順序 返回解集撬腾。

輸入:nums = [1,2,3]
輸出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

輸入:nums = [0]
輸出:[[],[0]]

劍指 Offer II 080. 含有 k 個(gè)元素的組合(中等)

給定兩個(gè)整數(shù) n 和 k,返回 1 ... n 中所有可能的 k 個(gè)數(shù)的組合恢恼。

輸入: n = 4, k = 2
輸出:
[ [2,4],[3,4],[2,3],[1,2],[1,3],[1,4]]

輸入: n = 1, k = 1
輸出: [[1]]

291. 單詞規(guī)律 II(會(huì)員/中等)

給你一種規(guī)律 pattern 和一個(gè)字符串 s民傻,請(qǐng)你判斷 s 是否和 pattern 的規(guī)律相匹配。如果存在單個(gè)字符到字符串的 雙射映射 ,那么字符串 s 匹配 pattern 漓踢,即:如果pattern 中的每個(gè)字符都被它映射到的字符串替換牵署,那么最終的字符串則為 s 。雙射 意味著映射雙方一一對(duì)應(yīng)喧半,不會(huì)存在兩個(gè)字符映射到同一個(gè)字符串奴迅,也不會(huì)存在一個(gè)字符分別映射到兩個(gè)不同的字符串。

輸入:pattern = "abab", s = "redblueredblue"
輸出:true
解釋:一種可能的映射如下:
'a' -> "red"
'b' -> "blue"

輸入:pattern = "aaaa", s = "asdasdasdasd"
輸出:true
解釋:一種可能的映射如下:
'a' -> "asd"

320. 列舉單詞的全部縮寫(會(huì)員/中等)

單詞的 廣義縮寫詞 可以通過下述步驟構(gòu)造:先取任意數(shù)量的 不重疊挺据、不相鄰 的子字符串取具,再用它們各自的長(zhǎng)度進(jìn)行替換。

例如扁耐,"abcde" 可以縮寫為:
"a3e"("bcd" 變?yōu)?"3" )
"1bcd1"("a" 和 "e" 都變?yōu)?"1")
"5" ("abcde" 變?yōu)?"5")
"abcde" (沒有子字符串被代替)
然而暇检,這些縮寫是 無效的 :
"23"("ab" 變?yōu)?"2" ,"cde" 變?yōu)?"3" )是無效的婉称,因?yàn)楸贿x擇的字符串是相鄰的
"22de" ("ab" 變?yōu)?"2" 块仆, "bc" 變?yōu)?"2")  是無效的,因?yàn)楸贿x擇的字符串是重疊的
給你一個(gè)字符串 word 酿矢,返回 一個(gè)由 word 的所有可能 廣義縮寫詞 組成的列表 榨乎。按 任意順序 返回答案。

 
輸入:word = "word"
輸出:["4","3d","2r1","2rd","1o2","1o1d","1or1","1ord","w3","w2d","w1r1","w1rd","wo2","wo1d","wor1","word"]

輸入:word = "a"
輸出:["1","a"]
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瘫筐,一起剝皮案震驚了整個(gè)濱河市蜜暑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌策肝,老刑警劉巖肛捍,帶你破解...
    沈念sama閱讀 217,907評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異之众,居然都是意外死亡拙毫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門棺禾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缀蹄,“玉大人,你說我怎么就攤上這事膘婶∪鼻埃” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵悬襟,是天一觀的道長(zhǎng)衅码。 經(jīng)常有香客問我,道長(zhǎng)脊岳,這世上最難降的妖魔是什么逝段? 我笑而不...
    開封第一講書人閱讀 58,586評(píng)論 1 293
  • 正文 為了忘掉前任垛玻,我火速辦了婚禮,結(jié)果婚禮上奶躯,老公的妹妹穿的比我還像新娘帚桩。我一直安慰自己,他們只是感情好巫糙,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評(píng)論 6 392
  • 文/花漫 我一把揭開白布朗儒。 她就那樣靜靜地躺著颊乘,像睡著了一般参淹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上乏悄,一...
    開封第一講書人閱讀 51,488評(píng)論 1 302
  • 那天浙值,我揣著相機(jī)與錄音,去河邊找鬼檩小。 笑死开呐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的规求。 我是一名探鬼主播筐付,決...
    沈念sama閱讀 40,275評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼阻肿!你這毒婦竟也來了瓦戚?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤丛塌,失蹤者是張志新(化名)和其女友劉穎较解,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赴邻,經(jīng)...
    沈念sama閱讀 45,619評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡印衔,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姥敛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奸焙。...
    茶點(diǎn)故事閱讀 39,932評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖彤敛,靈堂內(nèi)的尸體忽然破棺而出与帆,到底是詐尸還是另有隱情,我是刑警寧澤臊泌,帶...
    沈念sama閱讀 35,655評(píng)論 5 346
  • 正文 年R本政府宣布鲤桥,位于F島的核電站,受9級(jí)特大地震影響渠概,放射性物質(zhì)發(fā)生泄漏茶凳。R本人自食惡果不足惜嫂拴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評(píng)論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望贮喧。 院中可真熱鬧筒狠,春花似錦、人聲如沸箱沦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谓形。三九已至灶伊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寒跳,已是汗流浹背聘萨。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留童太,地道東北人米辐。 一個(gè)月前我還...
    沈念sama閱讀 48,095評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像书释,于是被迫代替她去往敵國(guó)和親翘贮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容