树结构的子树合并(不考虑子树顺序)
来源:互联网 发布:外教口语网络平台 编辑:程序博客网 时间:2024/05/03 13:24
问题提出
最近工作上遇到个问题,分析数据链路的服务调用情况,并对相同的服务路径进行合并,具体的一次服务调用的部分数据如下(省略部分字段),一条数据链路通常会有成千上万条类似的记录,表示一个前台请求被处理所调用的服务流程,一个记录代表依次服务调用。rpc_id是一个关键的字段,代表了服务调用层次,比如9.4.1是被9.4调用,而9.4又被9调用,9就是发起服务调用的根节点,这是一个典型的树结构。展示成千上万个节点的树结构没太大意义,因为有很多节点可以合并,并且子树也可以合并,合并之后的服务调用树并不大。关于合并规则,节点是否等价要求满足:①、有相同的父节点,②、server字段相同,③、method字段相同;等价的子树可以合并,子树等价条件是要求从子树根节点到叶节点的路径上对应子节点等价,不考虑子节点之间的顺序。
问题分析
代码实现
下面给出Java实现代码,使用具体的语言描述问题可以展现更多细节,故不再使用伪代码。
import java.util.LinkedList;import java.util.List;public class TreeNode { private String key; //存储rpc_id,标识树节点的结构层次 private String value; //存储树节点的值,简化的数据模型 private List<TreeNode> children; //存储子节点 public TreeNode(String strKey,String strValue) { children = new LinkedList<TreeNode>(); key = strKey; value = strValue; } public int getChildrenCount() { return this.children.size(); } public String getValue() { return this.value; } //合并子树 public void MergeChildren() { //对树进行后序遍历,先合并子树 int cCount = this.children.size(); for(int i=0;i<cCount;i++) { TreeNode node = children.get(i); node.MergeChildren(); } //保证子树节点中没有等价的子树,即完成 //子树节点的子树合并,再来进行本节点的子树合并 //合并子树中所有可以合并的子树 for(int i=0;i<children.size()-1;i++) { TreeNode child1 = children.get(i); for(int j=i+1;j<children.size();j++) { TreeNode child2 = children.get(j); if(child1.IsTheSameSubTree(child2)) { children.remove(j); j--; } } } } //判断子树是是否等价 private boolean IsTheSameSubTree(TreeNode another) { //一个为null,一个不为null,不等价 if(another==null) return false; //子节点数量不同,不等价 if(this.getChildrenCount() != another.getChildrenCount()) return false; //子节点的值不相等,不等价 if(!IsNodeSame(another)) return false; //完成树节点的先序遍历,再就行子节点的遍历 List<TreeNode> anotherChildren = another.getChildren(); for(TreeNode myChild : children) { //对本节点的每一个子树,在对比节点中寻找等价的子树, //如果找不到,则本节点与对比节点不等价 boolean bFind = false; for(TreeNode anotherChild:anotherChildren) { if(myChild.IsTheSameTrace(anotherChild)) { bFind = true; break; } } if(bFind==false) return false; } return true; } private boolean IsNodeSame(TreeNode another) { return value.equals(another.getValue()); }}
读者可以执行TreeNode(1.1.1).IsTheSameSubTree(TreeNode(1.1.2)),节点不多,很容易得出是否等价的结果,可知1.1.1和1.1.2子树等价直接将子树1.1.2删除,即合并1.1.1和1.1.2;再执行TreeNode(1.1.1).IsTheSameSubTree(TreeNode(1.1.3)),子树不等价;回溯至根节点,判断TreeNode(1.1).IsTheSameSubTree(TreeNode(1.2));可知1.1与1.2亦等价,删除子树1.2。再合并1.2和1.1时要保证完成了对1.2和1.1的子节点的合并操作,否则无法判断1.2和1.1节点是否等价,合并使用树的后序遍历。