审题
题目:判断一个无向图是否为树?
树的特点是没有环路,并且每个点都有连接,所以点的数量和边的数量有这样一个关系:(edgesCount - 1) * 2 == nodesCount
这道题的重点,就是遍历整张图,遍历一次,统计图中点的数量和边的数量。如果点的数量等于图中所有点的数量,说明所有点都被连接。边的数量等于点的数量减一,说明边没有成环。
所有点都被连接且没有成环,那就是一颗树。
准备数据
首先准备数据,图的数据基于邻接表建立:
javascript
/**
* @typedef {{value: number, next: nodeType}} nodeType
*
* 使用 JSDoc 注释语法定义了一个类型别名 nodeType。
* 它表示一个节点对象,包括一个数值属性 value 和一个指向下一个节点的引用属性 next。
* 这个类型用于构建图数据结构。
*/
/**
* @type {nodeType[]}
*
* 声明一个名为 graphNodes 的数组,类型为 nodeType[]。
* 这个数组用于存储图的节点,初始化长度为 7,初始值为包含 value 和 next 属性的空对象。
* 这些节点将被用于构建图的结构。
*/
const graphNodes = Array(7)
.fill(0)
.map((item, index) => {
const temp = {};
temp.value = index;
temp.next = null;
return temp;
});
/**
* 定义图的边关系。
* 这个对象表示图中节点之间的边关系,以节点编号为键,值为与该节点相邻的节点的数组。
*/
const graphEdges = { 1: [2, 4], 2: [1, 5], 3: [5, 6], 4: [1], 5: [2, 3], 6: [3] };
/**
* 生成图数据结构。
* 使用 generateGraph 函数将图的节点和边关系关联起来。
*/
const generateGraph = (graphNodes, graphEdges) => {
Object.entries(graphEdges).map(([nodeKey, edges]) => {
graphNodes[nodeKey].next = edges.reduce((res, nextEdge) => {
const temp = { value: nextEdge, next: null };
let tempRes = res;
while (tempRes.next) tempRes = tempRes.next;
tempRes.next = temp;
return res;
}, {}).next;
});
return graphNodes;
};
// 生成图数据结构
generateGraph(graphNodes, graphEdges);
javascript
/**
* 创建一个数组 isVisited,用于记录节点是否已被访问。
* 初始状态下,所有节点都未被访问,所以数组的初始值为 false。
*/
const isVisited = Array(7).fill(false);
const NODESCOUNT = 6; // 图中节点的总数
创建好了图的数据结构,并且准备了isVisited
表示顶点是否已经被访问。NODESCOUNT
表示节点数量。
编写代码
javascript
let nodesCount = 0; // 记录访问的节点数
let edgesCount = 0; // 记录边的数量
/**
* @param {nodeType[]} graph - 图数据结构,包含节点和连接关系
* @param {number} startNode - 起始节点,表示从该节点开始进行深度优先搜索
*
* 这个函数实现了深度优先搜索(DFS)算法,用于从起始节点开始遍历图的节点。
*/
const DFS = (graph, startNode) => {
nodesCount++; // 访问节点数加一
isVisited[startNode] = true; // 标记当前节点为已访问
// 遍历当前节点的相邻节点
for (let node = graph[startNode].next; node !== null; node = node.next) {
edgesCount++; // 记录边的数量加一
if (isVisited[node.value] === false) {
// 如果相邻节点未被访问,则递归调用 DFS 函数继续遍历
DFS(graph, node.value);
}
}
};
/**
* @param {nodeType[]} graph - 图数据结构,包含节点和连接关系
*
* 这个函数用于判断图是否是一棵树。
* 它通过进行深度优先搜索(DFS)并检查节点和边的数量来进行判断。
*/
const judgeTreee = (graph) => {
DFS(graph, 1); // 从节点1开始进行深度优先搜索
console.log(nodesCount, edgesCount);
if (nodesCount === NODESCOUNT && edgesCount === 2 * (NODESCOUNT - 1)) {
console.log("it is a tree"); // 如果节点数和边数符合树的定义,则打印 "it is a tree"
} else {
console.log("it is not a tree"); // 否则打印 "it is not a tree"
}
};
代码采用深度优先遍历,在遍历的途中记录点的数量,和边的数量。在遍历结束之后,与NODESCOUNT
进行对比。
如果关系满足,说明是树
下面执行函数:
javascript
// 调用 judgeTreee 函数判断图是否是一棵树
judgeTreee(graphNodes);

可以看到点的数量,和边的数量都打印正确。点的数量确实是 6 个,边的数量确实是 12/2=6 个。但作为一棵树,边的数量需要是 5 个,比点要少一。所以不是树。
从图中可以很简单地看出来,这条多余的边是<4,2>
, 让我们将这条边删掉再看看运行结果:
javascript
// delete <2,4>,<4,2>
const graphEdges = { 1: [2, 4], 2: [1, 5], 3: [5, 6], 4: [1], 5: [2, 3], 6: [3] };

执行函数:
javascript
// 调用 judgeTreee 函数判断图是否是一棵树
judgeTreee(graphNodes);

点的数量(6 个)正确,边的数量(5 条)也正确。所以这是一颗树。
完整代码
javascript
/**@typedef {{value: number, next: nodeType}} nodeType */
/**@type {nodeType[]} */
const graphNodes = Array(7)
.fill(0)
.map((item, index) => {
const temp = {};
temp.value = index;
temp.next = null;
return temp;
});
const graphEdges = { 1: [2, 4], 2: [1, 5], 3: [5, 6], 4: [1], 5: [2, 3], 6: [3] };
const generateGraph = (graphNodes, graphEdges) => {
Object.entries(graphEdges).map(([nodeKey, edges]) => {
graphNodes[nodeKey].next = edges.reduce((res, nextEdge) => {
const temp = { value: nextEdge, next: null };
let tempRes = res;
while (tempRes.next) tempRes = tempRes.next;
tempRes.next = temp;
return res;
}, {}).next;
});
return graphNodes;
};
generateGraph(graphNodes, graphEdges);
const isVisited = Array(7).fill(false);
const NODESCOUNT = 6;
let nodesCount = 0;
let edgesCount = 0;
const DFS = (graph, startNode) => {
nodesCount++;
isVisited[startNode] = true;
for (let node = graph[startNode].next; node !== null; node = node.next) {
edgesCount++;
if (isVisited[node.value] === false) {
DFS(graph, node.value);
}
}
};
const judgeTreee = (graph) => {
DFS(graph, 1);
console.log(nodesCount, edgesCount);
if (nodesCount == NODESCOUNT && edgesCount === 2 * (NODESCOUNT - 1)) {
console.log("it is a tree");
} else {
console.log("it is not a tree");
}
};
judgeTreee(graphNodes);
总结
这次的题目是判断图是否为树
,解题思路是,遍历一次,统计图中点的数量和边的数量,如果点的数量等于图中所有点的数量,说明所有点都被连接。边的数量等于点的数量减一,说明边没有成环。
所有点都被连接且没有成环,那就是一颗树。
同时给出了完整的 JS 代码,方便大家测试