回溯法很好用,可以查找出全部符合目标的答案,属于暴力求解的一种。
const tree = [
{
id: '1',
name: '广东省',
children: [
{
id: '11',
name: '深圳市',
children: [
{
id: '111',
name: '南山区',
},
{
id: '112',
name: '南山区',
},
],
},
{
id: '12',
name: '广州市',
children: [
{
id: '121',
name: '天河区',
},
{
id: '122',
name: '越秀区',
},
],
},
],
},
{
id: '2',
name: '江西省',
children: [
{
id: '21',
name: '南昌市',
children: [
{
id: '211',
name: '南昌1区',
},
{
id: '212',
name: '南昌2区',
},
],
},
{
id: '22',
name: '赣州市',
children: [
{
id: '221',
name: '赣州1区',
},
{
id: '222',
name: '赣州2区',
},
],
},
],
},
];
查找目标节点
直接深度遍历即可,简单。
const findTargetNode = targetId => {
let res = null;
function dfs(targetId, item) {
if (item.id === targetId) {
res = item;
return;
}
if (item.children?.length) {
item.children.forEach(subItem => {
dfs(targetId, subItem);
});
}
}
const root = { id: 'NONE', children: tree };
dfs(targetId, root);
return res;
};
findTargetNode('112') // => { id: '112', name: '南山区' }
findTargetNode('222') // => { id: '222', name: '赣州2区' }
findTargetNode('333') // => null
findTargetNode('21') // =>
// {
// id: '21',
// name: '南昌市',
// children: [ { id: '211', name: '南昌1区' }, { id: '212', name: '南昌2区' } ]
// }}
查找包含目标节点的路径
使用回溯法可以轻松搞定。(这里假设目标节点就是叶子节点,如果不是叶子节点那么只能返回自顶到目标节点的路径)
const findPath = targetId => {
let res = [];
function dfs(targetId, item, subPath) {
if (subPath.includes(targetId)) {
res = JSON.parse(JSON.stringify(subPath)); // 地址引用,这里一定要深拷贝
return;
}
if (item.children?.length) {
item.children.forEach(subItem => {
subPath.push(subItem.id);
dfs(targetId, subItem, subPath);
subPath.pop();
});
}
}
const root = { id: 'NONE', children: tree };
dfs(targetId, root, []);
return res;
};
findPath('112'); // => [1, 11, 112]
findPath('222'); // => [2, 22, 222]
findPath('333'); // => []
findPath('21'); // => [ '2', '21' ]