构造无限级树并深度遍历查找指定节点
前言
最近接到一个关于树的需求,想起了大学那会儿听过俞敏洪有关树的演讲,老师重复在课上放了很多次,N年过去了印象还是很深。
演讲内容是介样的,让我们一起来喝汤~
人的生活方式有两种,
第一种方式是像草一样活着,
你尽管活着,
每年还在成长,
但是你毕竟是一棵草,
你吸收雨露阳光,
但是却长不大。
人们可以踩过你,
但是人们不会因为你的痛苦,而他产生痛苦;
人们不会因为你被踩了,而来怜悯你,
因为人们本身就没有看到你。
所以我们每一个人,
都应该像树一样的成长,
即使我们现在什么都不是,
但是只要你有树的种子,
即使你被踩到泥土中间,
你依然能够吸收泥土的养分,
自己成长起来。
当你长成参天大树以后,
遥远的地方,人们就能看到你;
走近你,你能给人一片绿色。
活着是美丽的风景,
死了依然是栋梁之才,
活着死了都有用。
回到今天的主题,需求是这样的:构造无限级树型结构,界面类似下边这样的结构
然后,还需要把所有的橙色标记的叶子节点从垂直方向筛选出来,最终效果就是得到一个这样顺序的一级数组
KKKQQQJJJHHHGGGLLL
需要根据AAA这个节点和他所有子节点的数据,构造出一个无限级树型结构,如下
数据
- 顶级AAA数据
{"id":348019,"name":"AAA","is_leaf":2,"parent_id":347234,"type":0,"order":["348020"]}
- AAA所有的孩子节点
[{"id":348020,"name":"BBB","is_leaf":2,"parent_id":348019,"type":0,"order":["348037","348033","348021","348023","348034"]},{"id":348021,"name":"CCC","is_leaf":2,"parent_id":348020,"type":0,"order":["348022","348035"]},{"id":348022,"name":"DDD","is_leaf":2,"parent_id":348021,"type":0,"order":["348024","348032","348030"]},{"id":348024,"name":"EEE","is_leaf":2,"parent_id":348022,"type":0,"order":["348025","348036"]},{"id":348025,"name":"FFF","is_leaf":2,"parent_id":348024,"type":0,"order":["348031"]},{"id":348030,"name":"HHH","is_leaf":1,"parent_id":348022,"type":11,"order":[]},{"id":348023,"name":"GGG","is_leaf":1,"parent_id":348020,"type":11,"order":[]},{"id":348031,"name":"QQQ","is_leaf":1,"parent_id":348025,"type":11,"order":[]},{"id":348032,"name":"JJJ","is_leaf":1,"parent_id":348022,"type":11,"order":[]},{"id":348035,"name":"MMM\u65e0\u53f6\u5b50","is_leaf":2,"parent_id":348021,"type":0,"order":[]},{"id":348036,"name":"NNN\u65e0\u53f6\u5b50","is_leaf":2,"parent_id":348024,"type":0,"order":[]},{"id":348033,"name":"KKK","is_leaf":1,"parent_id":348020,"type":11,"order":[]},{"id":348037,"name":"OOO","is_leaf":2,"paread8nt_id":348020,"type":0,"order":[]},{"id":348034,"name":"LLL","is_leaf":1,"parent_id":348020,"type":11,"order":[]}]
节点数据库结构都一样,核心是有一个parent_id,每个节点会保存直属叶子的排序order
实现
其实这里考察的递归构造树、广度优先遍历、深度优先遍历
其实不管啥语言,思想都是这样,因为这个需求是php系统写的接口,就用php给出实现代码,气氛搞起来~
构造无限级
private function getTree($list, $node){$tree = [];foreach ($list as $k => $v) {if ($v['parent_id'] == $node['id']) {$v['children'] = $this->getTree($list, $v);$tree[] = $v;}}if (empty($node['order'])) {return [];}//注意:为了保证所有的叶子节点是根据已有的order排序,这里我们重新摆放一下$treeMap = [];foreach ($tree as $v) {$treeMap[$v['id']] = $v;//建立id和数据的map}//根据order重新排序$orderTree = [];foreach ($node['order'] as $id) {//根据order的顺序重新摆放$orderTree[] = $treeMap[$id];}return $orderTree;}
我们来使用一下,使用如下
最终输出的json如下
可以看到,已经构造出了一个无限级的树,并且和我们的界面是一模一样的层级和顺序
拉下来现在我们要遍历这棵树,为了获取到所有is_leaft=1的节点,我们先用广度优先遍历来试一下,因为这个对于找节点来说性能比较高
广度优先遍历
代码如下
private function bfsFindLeafs($node){$result = [];//满足条件的组合$q = [];$q[] = $node;while (count($q) > 0) {$size = count($q);for ($i = 0; $i < $size; $i++) {$current = array_pop($q);//判断节点如果是满足条件,加入路径if ($current['is_leaf'] == 1) {$result[] = $current;}//把所有的子节点加入队列foreach ($current['children'] as $v) {$q[] = $v;}}}56creturn $result;}
我们来使用一下
$getAllLeafs = $this->bfsFindLeafs($topNode);echo json_encode($getAllLeafs);
运行后输出
[{"id": 348034,"name": "LLL","is_leaf": 1,"parent_id": 348020,"type": 11,"order": [],"children": []},{"id": 348023,"name": "GGG","is_leaf": 1,"parent_id": 348020,"type": 11,"order": [],"children": []},{"id": 348030,"name": "HHH","is_leaf": 1,"parent_id": 348022,"type": 11,"order": [],"children": []},{"id": 348032,"name": "JJJ","is_leaf": 1,"parent_id": 348022,"type": 11,"order": [],"children": []},{"id": 348031,"name": "QQQ","is_leaf": 1,"parent_id": 348025,"type": 11,"order": [],"children": []},{"id": 348033,"name": "KKK","is_leaf": 1,"parent_id": 348020,"type": 11,"order": [],"children": []}]
好,我们已经可以获取到所有is_leaf为1的节点了,但是这里有一个问题,不是从上到下输出的
LLLGGGHHHJJJQQQKKK这时候,我们就需要一个深度优先遍历来实现了
深度优先遍历
深度优先遍历其实就是回滚算法的一种,为了从上到下输出,需要先序遍历
private function dfsFindLeafs($node){$trackList = [];foreach ($node['children'] as $v) {if ($v['is_leaf'] == 1) {$trackList[] = $v;}if (empty($v['children'])) {continue;}$trackList = array_merge($trackList,$this->dfsFindLeafs($v));}return $trackList;}
我们再来运行一下
$getAllLeafs = $this->dfsFindLeafs($topNode);echo json_encode($getAllLeafs);
输出
[{"id": 348033,"name": "KKK","is_leaf": 1,"parent_id": 348020,"type": 11,"order": [],"children": []},{"id": 348031,"name": "QQQ","is_leaf": 1,"parent_id": 348025,"type": 11,"order": [],"children": []},{"id": 348032,"name": "JJJ","is_leaf": 1,"parent_id": 348022,"type": 11,"order": [],ad0"children": []},{"id": 348030,"name": "HHH","is_leaf": 1,"parent_id": 348022,"type": 11,"order": [],"children": []},{"id": 348023,"name": "GGG","is_leaf": 1,"parent_id": 348020,"type": 11,"order": [],"children": []},{"id": 348034,"name": "LLL","is_leaf": 1,"parent_id": 348020,"type": 11,"order": [],"children": []}]
顺序为
KKKQQQJJJHHHGGGLLL
这次顺序终于对了,oh yeah~,和我们之前的界面垂直顺序一致
完整代码
下面给出完整代码
<?php/*** Class Test* @author chenqionghe*/class Test{public function run(){$topNodeJson = '{"id":348019,"name":"AAA","is_leaf":2,"parent_id":347234,"type":0,"order":["348020"]}';$childrenJson = '[{"id":348020,"name":"BBB","is_leaf":2,"parent_id":348019,"type":0,"order":["348037","348033","348021","348023","348034"]},{"id":348021,"name":"CCC","is_leaf":2,"parent_id":348020,"type":0,"order":["348022","348035"]},{"id":348022,"name":"DDD","is_leaf":2,"parent_id":348021,"type":0,"order":["348024","348032","348030"]},{"id":348024,"name":"EEE","is_leaf":2,"parent_id":348022,"type":0,"order":["348025","348036"]},{"id":348025,"name":"FFF","is_leaf":2,"parent_id":348024,"type":0,"order":["348031"]},{"id":348030,"name":"HHH","is_leaf":1,"parent_id":348022,"type":11,"order":[]},{"id":348023,"name":"GGG","is_leaf":1,"parent_id":348020,"type":11,"order":[]},{"id":348031,"name":"QQQ","is_leaf":1,"parent_id":348025,"type":11,"order":[]},{"id":348032,"name":"JJJ","is_leaf":1,"parent_id":348022,"type":11,"order":[]},{"id":348035,"name":"MMM\u65e0\u53f6\u5b50","is_leaf":2,"parent_id":348021,"type":0,"order":[]},{"id":348036,"name":"NNN\u65e0\u53f6\u5b50","is_leaf":2,"parent_id":348024,"type":0,"order":[]},{"id":348033,"name":"KKK","is_leaf":1,"parent_id":348020,"type":11,"order":[]},{"id":348037,"name":"OOO","is_leaf":2,"parent_id":348020,"type":0,"order":[]},{"id":348034,"name":"LLL","is_leaf":1,"parent_id":348020,"type":11,"order":[]}]';$topNode = json_decode($topNodeJson, true);$childrenList = json_decode($childrenJson, true);$topNode['children'] = $this->getTree($childrenList, $topNode);$getAllLeafs = $this->dfsFindLeafs($topNode);echo json_encode($getAllLeafs);}/*** 递归构造无限级树** @param $li19d6st* @param $node* @return array*/private function getTree($list, $node){$tree = [];foreach ($list as $k => $v) {if ($v['parent_id'] == $node['id']) {$v['children'] = $this->getTree($list, $v);$tree[] = $v;}}if (empty($node['order'])) {return [];}//注意:为了保证所有的叶子节点是根据已有的order排序,这里我们重新摆放一下$treeMap = [];foreach ($tree as $v) {$treeMap[$v['id']] = $v;//建立id和数据的map}//根据order重新排序$orderTree = [];foreach ($node['order'] as $id) {//根据order的顺序重新摆放$orderTree[] = $treeMap[$id];}return $orderTree;}/*** 广度优先遍历获取所有叶子* @param $node* @return array*/private function bfsFindLeafs($node){$result = [];//满足条件的组合$q = [];$q[] = $node;while (count($q) > 0) {$size = count($q);for ($i = 0; $i < $size; $i++) {$current = array_pop($q);//判断节点如果是满足条件,加入路径if ($current['is_leaf'] == 1) {$result[] = $current;}//把所有的子节点加入队列foreach ($current['children'] as $v) {$q[] = $v;}}}return $result;}/*** 获取所有的叶子路径(深度度优先遍历)*/private function dfsFindLeafs($node){$trackList = [];foreach ($node['children'] as $v) {if ($v['is_leaf'] == 1) {$trackList[] = $v;}if (empty($v['children'])) {continue;}$trackList = array_merge($trackList, $this->dfsFindLeafs($v));}return $trackList;}}(new Test())->run();
ok,到这里我们就已经学会如何构造无限级树型结构,并学会用深度优先遍历垂直输出指定条件节点,还知道了怎么用广度优先遍历,giao~
- 二叉树的创建,遍历,查找,查找父节点,深度,大小等的递归实现
- 可双向并指定开始结点的treeview节点遍历查找方法类
- 二叉树 遍历|统计叶子节点|求深度|交换左右子树|查找是否存在某个特定叶子节点练习题
- 二叉树的中序、先序、后序、层序遍历 & 二叉树的深度 & 节点查找
- JavaScript中,二叉树的三种遍历,及查找最小/大值,指定值,删除指定节点
- 非递归层次遍历方法实现二叉树中指定节点的层次数查找
- 数据结构-二叉树操作(创建、先序、中序、后序遍历、计算叶子节点数目、计算二叉树深度、左右子树交换、随机数列产生排序树、查找结点、删除节点、广度遍历、非递归先序遍历)C语言源码(全)
- 二叉树各种相关操作(建立二叉树、前序、中序、后序、求二叉树的深度、查找二叉树节点,层次遍历二叉树等)(C语言版)
- 二叉树的相关操作一(建树,遍历(非递归),查找指定节点)
- 二叉树的创建,遍历,叶节点数,总结点数,深度
- linux(c)深度优先递归遍历指定目录
- 二叉树的各种操作 先序 中序 后续 层次 遍历 求树高度 节点深度 知先序中序求后续 二叉排序树
- leetcode 623. Add One Row to Tree二叉树添加指定深度元素+ 深度优先遍历DFS
- 从XML对象中查找指定节点的值
- SQL SERVER中查找指定节点的所有子节点的示例函数
- 数据结构 —— 二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现
- 数据结构入门----构造无向图,深度和广度遍历
- 数据结构之树的层次遍历(附带查找)、深度求值
- 二叉树,递归、非递归遍历,求深度,输出叶子节点
- python遍历文件夹,指定遍历深度与忽略目录