likes
comments
collection
share

每日一练--判断一个无向图是否为树

作者站长头像
站长
· 阅读数 9

审题

题目:判断一个无向图是否为树?

树的特点是没有环路,并且每个点都有连接,所以点的数量和边的数量有这样一个关系:(edgesCount - 1) * 2 == nodesCount这道题的重点,就是遍历整张图,遍历一次,统计图中点的数量和边的数量。如果点的数量等于图中所有点的数量,说明所有点都被连接。边的数量等于点的数量减一,说明边没有成环。

所有点都被连接且没有成环,那就是一颗树。

准备数据

首先准备数据,图的数据基于邻接表建立:每日一练--判断一个无向图是否为树

/** 
 * @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);
/** 
 * 创建一个数组 isVisited,用于记录节点是否已被访问。
 * 初始状态下,所有节点都未被访问,所以数组的初始值为 false。
 */
const isVisited = Array(7).fill(false);

const NODESCOUNT = 6; // 图中节点的总数

创建好了图的数据结构,并且准备了isVisited表示顶点是否已经被访问。NODESCOUNT表示节点数量。

编写代码

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进行对比。

如果关系满足,说明是树

下面执行函数:

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

每日一练--判断一个无向图是否为树

可以看到点的数量,和边的数量都打印正确。点的数量确实是 6 个,边的数量确实是 12/2=6 个。但作为一棵树,边的数量需要是 5 个,比点要少一。所以不是树。每日一练--判断一个无向图是否为树从图中可以很简单地看出来,这条多余的边是<4,2>, 让我们将这条边删掉再看看运行结果:

// delete <2,4>,<4,2>
const graphEdges = { 1: [2, 4], 2: [1, 5], 3: [5, 6], 4: [1], 5: [2, 3], 6: [3] };

每日一练--判断一个无向图是否为树执行函数:

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

每日一练--判断一个无向图是否为树点的数量(6 个)正确,边的数量(5 条)也正确。所以这是一颗树。

完整代码

/**@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 代码,方便大家测试

转载自:https://juejin.cn/post/7286815560827306021
评论
请登录