图论 给你一个由 '1'
(陆地)和 '0'
(水)组成的的二维网格,请你计算网格中岛屿的数量。
岛屿总是被水包围,并且每座岛屿只能由水平方向和/或竖直方向上相邻的陆地连接形成。
此外,你可以假设该网格的四条边均被水包围。
示例 1:
1 2 3 4 5 6 7 输入:grid = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 输出:1
示例 2:
1 2 3 4 5 6 7 输入:grid = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 输出:3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 func numIslands (grid [][]byte ) (ans int ) { m, n := len (grid), len (grid[0 ]) var dfs func (int , int ) dfs = func (i, j int ) { if i < 0 || i >= m || j < 0 || j >= n || grid[i][j] != '1' { return } grid[i][j] = '2' dfs(i, j-1 ) dfs(i, j+1 ) dfs(i-1 , j) dfs(i+1 , j) } for i, row := range grid { for j, c := range row { if c == '1' { dfs(i, j) ans++ } } } return }
这是个无向图,然后上下左右走一走
用插旗的方法,遍历过的地方标记为2,这样子第一次遍历的地方就是1
相当于二维数组的直接两层for循环遍历
在给定的 m x n
网格 grid
中,每个单元格可以有以下三个值之一:
值 0
代表空单元格;
值 1
代表新鲜橘子;
值 2
代表腐烂的橘子。
每分钟,腐烂的橘子 周围 4 个方向上相邻 的新鲜橘子都会腐烂。
返回 直到单元格中没有新鲜橘子为止所必须经过的最小分钟数。如果不可能,返回 -1
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 type pair struct { x , y int } var directions = []pair{{1 , 0 }, {-1 , 0 }, {0 , 1 }, {0 , -1 }}func orangesRotting (grid [][]int ) int { m, n := len (grid) , len (grid[0 ]) fresh := 0 queue := []pair{} for i, row := range grid { for j, x := range row { if x == 1 { fresh ++ }else if x == 2 { queue = append (queue, pair{i, j}) } } } ans := 0 for fresh > 0 && len (queue) > 0 { ans ++ tmp := queue queue = []pair{} for _ , p := range tmp{ for _ , d := range directions{ i , j := p.x + d.x , p.y + d.y if i >= 0 && j >= 0 && i < m && j < n && grid[i][j] == 1 { grid[i][j] = 2 fresh -- queue = append (queue, pair{i, j}) } } } } if fresh > 0 { return -1 } return ans }
这道题很明显是bfs的模板,像外部扩散的模型
老规矩 上下左右扩散一次 –> 也就是遍历一次
两层for循环直接遍历
使用一个队列来标记是否腐烂
小结: 前面两题都是无向图的典例:有这么一些共同的特征:
上下左右遍历
两层for循环直接遍历
需要用 0 - 1 - 2 来标记状态
你这个学期必须选修 numCourses
门课程,记为 0
到 numCourses - 1
。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites
给出,其中 prerequisites[i] = [ai, bi]
,表示如果要学习课程 ai
则 必须 先学习课程 bi
。
例如,先修课程对 [0, 1]
表示:想要学习课程 0
,你需要先完成课程 1
。
请你判断是否可能完成所有课程的学习?如果可以,返回 true
;否则,返回 false
。
示例 1:
1 2 3 输入:numCourses = 2, prerequisites = [[1,0]] 输出:true 解释:总共有 2 门课程。学习课程 1 之前,你需要完成课程 0 。这是可能的。
示例 2:
1 2 3 输入:numCourses = 2, prerequisites = [[1,0],[0,1]] 输出:false 解释:总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0 ;并且学习课程 0 之前,你还应先完成课程 1 。这是不可能的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 func canFinish (numCourses int , prerequisites [][]int ) bool { adjacency := make (map [int ][]int ) courseStatus := make (map [int ]int ) for _, prerequisite := range prerequisites { adjacency[prerequisite[1 ]] = append (adjacency[prerequisite[1 ]], prerequisite[0 ]) } var dfs func (int ) bool dfs = func (course int ) bool { if courseStatus[course] == 1 { return false } if courseStatus[course] == 2 { return true } courseStatus[course] = 1 for _, afterCourse := range adjacency[course] { if !dfs(afterCourse) { return false } } courseStatus[course] = 2 return true } for i := 0 ; i < numCourses; i++ { if !dfs(i) { return false } } return true }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 func canFinish (numCourses int , prerequisites [][]int ) bool { adjacencyMap := make (map [int ][]int ) inDegreeMap := make (map [int ]int ) queue := make ([]int , 0 ) result := make ([]int , 0 ) for _, prerequisite := range prerequisites { adjacencyMap[prerequisite[1 ]] = append (adjacencyMap[prerequisite[1 ]], prerequisite[0 ]) inDegreeMap[prerequisite[0 ]]++ } for course := 0 ; course < numCourses; course++ { if inDegreeMap[course] == 0 { queue = append (queue, course) } } for len (queue) > 0 { course := queue[0 ] queue = queue[1 :] result = append (result, course) for _, nextCourse := range adjacencyMap[course] { inDegreeMap[nextCourse]-- if inDegreeMap[nextCourse] == 0 { queue = append (queue, nextCourse) } } } return len (result) == numCourses }
拓扑排序: 有向无环图 – 具体感受就是任务分配先后
整个问题就是 入度和出度的问题 ; 上游的节点入度为零,下游的节点出度为零
建议先理解bfs版本的代码:
用一个邻接矩阵来记录 上下游关系
用一个入度表来记录 那个是第一节课(入度==0)
当第一节课上完后,邻接矩阵中对应的所有下游课程的入度-1
将入度=0的课程放入队列
len(result) == numCourses 这个是核心 (如果有环,那么意味着他的节点永远是入度不等于零,所以result永远不可能为0)
再理解dfs版本的代码;
邻接矩阵和入度表都一样的意思
他用 0 1 2 来表示在一次遍历中,某个节点是否被遍历过两遍(如果是,就是环,因为环是无线遍历的[前提是在某个遍历过程中])
直接for循环启动
**Trie **(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补全和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。
void insert(String word)
向前缀树中插入字符串 word
。
boolean search(String word)
如果字符串 word
在前缀树中,返回 true
(即,在检索之前已经插入);否则,返回 false
。
boolean startsWith(String prefix)
如果之前已经插入的字符串 word
的前缀之一为 prefix
,返回 true
;否则,返回 false
。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 输入 ["Trie", "insert", "search", "search", "startsWith", "insert", "search"] [[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]] 输出 [null, null, true, false, true, null, true] 解释 Trie trie = new Trie(); trie.insert("apple"); trie.search("apple"); // 返回 True trie.search("app"); // 返回 False trie.startsWith("app"); // 返回 True trie.insert("app"); trie.search("app"); // 返回 True
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 type Trie struct { children [26 ]*Trie isEnd bool } func Constructor () Trie { return Trie{} } func (t *Trie) Insert(word string ) { node := t for _, ch := range word { ch -= 'a' if node.children[ch] == nil { node.children[ch] = &Trie{} } node = node.children[ch] } node.isEnd = true } func (t *Trie) SearchPrefix(prefix string ) *Trie { node := t for _, ch := range prefix { ch -= 'a' if node.children[ch] == nil { return nil } node = node.children[ch] } return node } func (t *Trie) Search(word string ) bool { node := t.SearchPrefix(word) return node != nil && node.isEnd } func (t *Trie) StartsWith(prefix string ) bool { return t.SearchPrefix(prefix) != nil }
整体的结构就是树,只是这个树的每个节点是二十六位数组,每个数组存一个单词,很抽象我觉得,这样子浪费的内存也太多了吧
没什么好讲的