树的深度优先遍历(Depth-First Search, DFS)和广度优先遍历(Breadth-First Search, BFS)是两种常见的树遍历算法。
将一个数组转换成一棵树是一个常见的任务,特别是在处理层次数据结构时。假设数组中的每个元素是一个对象,包含 id 和 parentId 属性,其中 parentId 表示该节点的父节点的 id。根节点的 parentId 通常为 null 或者特定的值(例如 -1)。
下面是一个示例数组:
const array = [
{ id: 1, parentId: null, name: 'A' },
{ id: 2, parentId: 1, name: 'B' },
{ id: 3, parentId: 1, name: 'C' },
{ id: 4, parentId: 2, name: 'D' },
{ id: 5, parentId: 2, name: 'E' },
{ id: 6, parentId: 3, name: 'F' }
];
目标是将这个数组转换成一棵树结构,例如:
const tree = [
{
id: 1,
parentId: null,
name: 'A',
children: [
{
id: 2,
parentId: 1,
name: 'B',
children: [
{ id: 4, parentId: 2, name: 'D', children: [] },
{ id: 5, parentId: 2, name: 'E', children: [] }
]
},
{
id: 3,
parentId: 1,
name: 'C',
children: [
{ id: 6, parentId: 3, name: 'F', children: [] }
]
}
]
}
];
下面是实现这一转换的JavaScript代码:
function arrayToTree(array) {
const map = new Map();
const tree = [];
// 先将所有节点放入 map 中,方便查找
array.forEach(item => {
map.set(item.id, { ...item, children: [] });
});
// 再次遍历数组,构建树结构
array.forEach(item => {
const node = map.get(item.id);
if (item.parentId === null) {
tree.push(node);
} else {
const parent = map.get(item.parentId);
if (parent) {
parent.children.push(node);
}
}
});
return tree;
}
const array = [
{ id: 1, parentId: null, name: 'A' },
{ id: 2, parentId: 1, name: 'B' },
{ id: 3, parentId: 1, name: 'C' },
{ id: 4, parentId: 2, name: 'D' },
{ id: 5, parentId: 2, name: 'E' },
{ id: 6, parentId: 3, name: 'F' }
];
const tree = arrayToTree(array);
console.log(JSON.stringify(tree, null, 2));
解释
- 创建 Map:首先,我们将数组中的每个节点放入一个
Map中,键为节点的id,值为节点对象,并且初始化children数组为空。 - 构建树结构:再次遍历数组,对于每个节点:
- 如果
parentId为null,则该节点是根节点,将其添加到tree数组中。 - 否则,找到其父节点,并将该节点添加到父节点的
children数组中。
- 如果
输出
运行上述代码后,tree 变量将包含转换后的树结构,输出结果如下:
[
{
"id": 1,
"parentId": null,
"name": "A",
"children": [
{
"id": 2,
"parentId": 1,
"name": "B",
"children": [
{
"id": 4,
"parentId": 2,
"name": "D",
"children": []
},
{
"id": 5,
"parentId": 2,
"name": "E",
"children": []
}
]
},
{
"id": 3,
"parentId": 1,
"name": "C",
"children": [
{
"id": 6,
"parentId": 3,
"name": "F",
"children": []
}
]
}
]
}
]
深度优先遍历(DFS)
深度优先遍历有三种常见的遍历顺序:前序遍历、中序遍历和后序遍历。这里以二叉树为例进行说明。
1. 前序遍历(Pre-order Traversal)
- 访问根节点
- 递归地前序遍历左子树
- 递归地前序遍历右子树
function preOrderTraversal(node) {
if (node === null) {
return;
}
console.log(node.value); // 访问根节点
preOrderTraversal(node.left); // 遍历左子树
preOrderTraversal(node.right); // 遍历右子树
}
2. 中序遍历(In-order Traversal)
- 递归地中序遍历左子树
- 访问根节点
- 递归地中序遍历右子树
function inOrderTraversal(node) {
if (node === null) {
return;
}
inOrderTraversal(node.left); // 遍历左子树
console.log(node.value); // 访问根节点
inOrderTraversal(node.right); // 遍历右子树
}
3. 后序遍历(Post-order Traversal)
- 递归地后序遍历左子树
- 递归地后序遍历右子树
- 访问根节点
function postOrderTraversal(node) {
if (node === null) {
return;
}
postOrderTraversal(node.left); // 遍历左子树
postOrderTraversal(node.right); // 遍历右子树
console.log(node.value); // 访问根节点
}
广度优先遍历(BFS)
广度优先遍历通常使用队列来实现,从根节点开始,逐层访问所有节点。
function breadthFirstTraversal(root) {
if (root === null) {
return;
}
const queue = [root];
while (queue.length > 0) {
const node = queue.shift();
console.log(node.value); // 访问当前节点
if (node.left !== null) {
queue.push(node.left); // 将左子节点加入队列
}
if (node.right !== null) {
queue.push(node.right); // 将右子节点加入队列
}
}
}
示例树结构
假设我们有一个如下的二叉树结构:
1
/ \
2 3
/ \
4 5
对应的JavaScript对象表示如下:
const tree = {
value: 1,
left: {
value: 2,
left: { value: 4, left: null, right: null },
right: { value: 5, left: null, right: null }
},
right: { value: 3, left: null, right: null }
};
测试遍历
preOrderTraversal(tree); // 输出: 1, 2, 4, 5, 3
inOrderTraversal(tree); // 输出: 4, 2, 5, 1, 3
postOrderTraversal(tree); // 输出: 4, 5, 2, 3, 1
breadthFirstTraversal(tree); // 输出: 1, 2, 3, 4, 5
以上是树的深度优先遍历和广度优先遍历的实现方法。希望这些示例对你有所帮助。如果有任何疑问或需要进一步的解释,请随时提问。
PS:学会了记得,点赞,评论,收藏,分享