图
- 图是网络结构的抽象模型,是一组由边连接的节点
- 图可以表示任何二元关系,比如道路、航班
- js中没有图,但是可以用Object、Array构建
- 图的表示法:邻接矩阵、邻接表、关联矩阵
邻接矩阵
邻接表
图的常用操作
- 深度优先遍历
- 尽可能深的搜索图的分支
- 算法口诀
- 访问根节点
- 对根节点的没访问过的相邻节点挨个进行深度优先遍历
- code: 对此图进行深度优先遍历
-
const graph = { 0: [1, 2], 1: [2], 2: [0, 3], 3: [3] } const visited = new Set() const dfs = (n) => { console.log(n) // 2 0 1 3 visited.add(n) graph[n].forEach = (c => { if (!visited.has(c)){ dfs(c) } }) } dfs(2)
- 广度优先遍历
- 先访问离根节点最近的节点
- 算法口诀
- 新建一个队列,把根节点入队
- 把队头出队并访问
- 把队头的没有访问过的相邻节点入队
- 重复第二、三步,直到队列为空
- code
-
const graph = { 0: [1, 2], 1: [2], 2: [0, 3], 3: [3] } const visited = new Set() visited.add(2) const q = [2] while(q.length) { const n = q.shift() console.log(n) // 2 0 3 1 graph[n].forEach = (c => { if (!visited.has(c)){ q.push(c) visited.add(c) } }) }
-
leetcode65:有效数字 【难度:困难】
解题思路
所有的字符判断可通过这张图来解释,共分为8个状态,其中淡红色的6、3、5状态为合法的数字,即题目返回true的状态;
- 0: 即空字符串
- 1: 由0状态转变,输入了“+”或者“-”的字符
- 2: 由0、1状态转变,输入了小数点“.”字符
- 6: 由1、0 转变,输入了“0~9”之间的数字
- 3: 由2、6、3转变,6输入了小数点“.”字符, 2输入了“0~9”之间的数字,3仍输入了“0~9”之间的数字
- 以此类推,只要最后的状态停留在6、3、5状态即为true
解题步骤
- 构建一个表示状态的图
- 遍历字符串中,并沿着图走,如果到了某个节点无路可走就返回false
- 遍历结束,如走到3/5/6,就返回true,否则为false
- code
-
function isNumber (s) { // 用blank代表空格,sign代表+/-,digit代表0~9数字 // 时间复杂度O(n), 空间复杂赋O(1) const graph = { 0: {'blank': 0, 'sign': 1, '.':2, 'digit': 6}, 1: {'digit': 6, '.': 2}, 2: {'digit': 3}, 3: {'digit': 3, 'e': 4}, 4: {'digit': 5, 'sign': 7}, 5: {'digit': 5}, 6: {'digit': 6, '.': 3, 'e': 4}, 7: {'digit': 5}, } let state = 0 for (c of s.trim()) { if (c >= '0' && c <= '9') { c = 'dight' } else if (c === ' ') { c = 'black' } else if (c === '+' || c === '-' ) { c = 'sign' } else if (c === 'E' ) { c = 'e' } state = graph[state][c] if (state === undefined) { return false } } if (state === 3 || state === 5 || state === 6) { return true } }
-
leetcode417:太平洋大西洋水流问题 【难度:中等】
- 解题思路
- 矩阵想象成图
- 从海岸线逆流而上遍历图,所到之处就是可以流到某个大洋的坐标
- 解题步骤
- 新建两个矩阵,分别记录能流到两个大洋的坐标
- 从海岸线,多管齐下,同时深度优先遍历图,过程中填充坐标
- 遍历两个矩阵,找出能流到两个大洋的坐标
// 时间复杂度O(m*n) 空间复杂度O(m*n)
var atlantic = function (matrix){
if (!matrix || !matrix[0]) return []
const m = matrix.length // m * n 矩阵
const n = matrix[0].length
// 构建自己的二维数组
const flow1 = Array.from({length: m}, () => new Array(n).fill(false)) // 记录流到大西洋的坐标
const flow2 = Array.from({length: m}, () => new Array(n).fill(false)) // 记录流到太平洋的坐标
const dfs = (r, c, flow) => {
flow[r][c] = true
[[r-1, c], [r+1, c], [r, c-1], [r, c+1]].forEach(([nr, nc]) => {
// 保证在矩阵中
if (nr >= 0 && nr < m && nc >= 0 && nc < n
// 防止死循环
&& !flow[nr][nc]
// 逆流而上
&& matrix[nr][nc] >= matrix[r][c]) {
dfs(nr, nc, flow)
}
}
}
// 沿着海岸线逆流而上
for (let r = 0; r < m: r += 1) {
dfs(r, 0, flow1) //第一列 筛选流到太平洋
dfs(r, n - 1, flow2) // 最后一列 流到大西洋
}
for (let c = 0;c < n; c += 1) {
dfs(0, c, flow1) // 第一行 流到太平洋
dfs(m - 1, c, flow2)// 最后一行 流到大西洋
}
// 收集能流到两个大西洋的坐标
const res = []
for(let r = 0; r < m; r += 1) {
for (let c = 0; c < n; c +=1) {
if (flow1[r][c] && flow2[r][c]) {
res.push([r, c])
}
}
}
return res
}
leetcode133:克隆图 【难度:中等】
- 解题思路
- 拷贝所有的节点
- 拷贝所有的边
- 解题步骤
- 深度或广度优先遍历所有的节点
- 拷贝所有的节点,存储起来
- 将拷贝的节点,按照原图的连接方法进行连接
function cloneGraph (node) {
// 时间复杂度O(n) 空间复杂度O(n)
if (!node) return
const visited = new Map()
const dfs = (n) => {
const nCopy = new Node(n.val)
console.log(n.val)
visited.set(n, nCopy)
(n.neighbors || []).forEach(ne => {
if (!visited.has(ne)) {
dfs(ne)
}
nCopy.neighbors.push(visited.get(ne))
})
}
dfs(node)
return visited.get(node)
}