目录
第九章 数据结构之“图”
9.1 图简介
一条边最多能连接两个节点。
也可以用链表来构建图。
9.2 图的深度与广度优先遍历
在tu.js文件里面新建一个图:
// 利用objct和Array构建一个图
var tu = {
0: [1, 2],
1: [2],
2: [0, 3],
3: [3]
}
module.exports = tu;
对图进行深度优先遍历:
var tu = require("./tu");//1、引入构造图的JS文件
var visited = new Set();//4、新建一个空字典,用来存放已经访问过的节点
var dfs = (n) => {//2、声明一个箭头函数,把要访问的节点传进去
console.log(n);//3、访问当前节点
visited.add(n);//5、把已经访问过的节点,放到字典中,方便以后不再访问
tu[n].forEach(c => {//6、访问当前节点的相邻节点,但要排除已经访问过的节点
if (!visited.has(c)) {//7、排除已经访问过的节点,之前访问过的节点就不再访问了
dfs(c);//8、递归调用它自己,重复之前的操作
}
});
}
dfs(2);//9、从2节点开始,2相当于根节点
在tu.js文件里面新建一个图:
// 利用objct和Array构建一个图
var tu = {
0: [1, 2],
1: [2],
2: [0, 3],
3: [3]
}
module.exports = tu;
对图进行广度优先遍历:
var tu = require("./tu");//1、引入构造图的JS文件
var visited = new Set();//3、新建一个空字典,用来存放已经访问过的节点
var queue = [2];//2、把根节点入队
visited.add(2);//4、把根节点添加到字典中,表示已经访问过了
while (queue.length) {//5、在队列在值的情况下进行当前元素的出队并访问操作
var n = queue.shift(n);//6、在队头出队
console.log(n);//7、访问当前元素
tu[n].forEach(c => {//8、访问当前节点的相邻节点,但要排除已经访问过的节点
if (!visited.has(c)) {//9、排除已经访问过的节点,之前访问过的节点就不再访问了
queue.push(c);//10、没有访问过的节点入队
visited.add(c);//11、把入队的节点添加到字典中,表示已经访问过了
}
});
}
9.3 LeetCode: 65. 有效数字
代码实现:
//有效数字
var isNumber = function (s) {
var 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 = 'digit';
} else if (c === ' ') {
c = 'blank';
} else if (c === '+'||c==='-') {
c = 'sign';
}
state = graph[state][c];
if(state===undefined){
return false;
}
}
if(state==3||state===5||state===6){
return true;
}
return false;
};
代码解读:
构建一个图:
反把相应的字符对应相应的操作:
9.4 LeetCode: 417. 太平洋大西洋水流问题
代码实现:
// 太平洋
var pacificAtlantic = function (matrix) {
if (!matrix || !matrix[0]) { return []; }
const m = matrix.lenth;
const n = matrix[0].lenth;
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;
};
代码解读:
排除矩阵为空或传进来的是一维数组的情况:
构建两个自己的矩阵:
得到矩阵的行数和列数:
9.5 LeetCode: 133. 克隆图
法一:使用深度优先遍历做。
代码实现:
var cloneGraph=function(node){
if(!node) return;
const visited = new Map();
visited.set(node,new Node(node.val));
const q=[node];
while(q.length){
const n=q.shift();
(n.neighbors||[]).forEach(ne => {
if(!visited.has(ne)){
q.push(ne);
visited.set(ne,new Node(ne.val));
}
visited.get(n).neighbors.push(visited.get(ne));
});
}
return visited.get(node);
};
代码解读:
深度优先遍历图:
复制每个节点,但还没有建立关系:
建立原生节点和复制后节点的映射关系,但还没有建立边。
建立边:
法二:使用广度优先遍历来做。
代码实现:
广度优先遍历图:
9.6 图总结