在开发软件的过程中,遇到一个树形结构的问题,如下图:
由于树形结构太大,只显示部分叶节点,并且报价是叶节点的数量和报价的成积。
问题经分析很显然:变成了如下两个问题:
1从树的根节点开始建树就是一个简单的递归,但现在的问题是从树的叶节点开始如何建树,涉及到一个如何合并子节点的问题。
2如何根据叶节点的数量和报价计算各级父节点的报价?
1如何从树的叶节点开始如何建树?
有一种思路,就是按照正常思路建好后,然后把没有子节点的删掉,很麻烦。
于是想着能不能通过叶节点的递归一层一层向上,最终完成树的创建。
最总实现代码如下:
/// <summary> /// 返回需要的树 /// </summary> /// <param name="listAllNodes">所有的节点信息</param> /// <param name="dicNode">键为父节点编号,值为父节点对应的子节点 ,初始为需要显示的叶节点</param> /// <returns></returns> public static List<TreeNode> GeTree(List<TreeNode> listAllNodes, Dictionary<int, List<TreeNode>> dicNode) { int rootNodeId = -1;//虚拟的根节点编号 while (!dicNode.ContainsKey(rootNodeId)) //如果没有到根节点继续 { dicNode = GetOneLevelNodes(listAllNodes, dicNode); } return dicNode[rootNodeId]; } /// <summary> /// 返回某一层的所有节点,包含子节点 /// </summary> /// <param name="listAllNodes">所有的节点信息</param> /// <param name="dicNode">键为父节点编号,值为父节点对应的子节点</param> /// <returns></returns> private static Dictionary<int, List<TreeNode>> GetOneLevelNodes(List<TreeNode> listAllNodes, Dictionary<int, List<TreeNode>> dicNode) { int rootNodeId = -1; //虚拟的根节点编号 List<int> ParentsIdList = new List<int>(); ParentsIdList.AddRange(dicNode.Keys);//这次需要添加的节点编号 Dictionary<int, List<TreeNode>> dicNode2 = new Dictionary<int, List<TreeNode>>();//键为父节点编号,值为父节点对应的子节点 for (int i = listAllNodes.Count - 1; i >= 0; i--) { TreeNode item = listAllNodes[i]; if (ParentsIdList.Contains(item.ID)) { int parentId = rootNodeId;//默认为顶级节点 if (item.ParentID.HasValue)//如果存在父节点 { parentId = item.ParentID.Value; } List<TreeNode> children = dicNode[item.ID];//获取此节点对应的父节点 item.children = children;//添加子节点 AddOneNode(parentId, dicNode2, item);//添加节点到字典中 listAllNodes.RemoveAt(i);//移除已经处理的节点 } } return dicNode2; } /// <summary> /// 合并具有相同父节点的兄弟节点 /// </summary> /// <param name="parentId">需要处理的节点的父节点</param> /// <param name="dicNode">此层对应的节点字典</param> /// <param name="node">需要处理的节点</param> private static void AddOneNode(int parentId, Dictionary<int, List<TreeNode>> dicNode, TreeNode node) { if (dicNode.ContainsKey(parentId))//如果有 { dicNode[parentId].Add(node);//如果具有相同的父节点,则添加到相应的兄弟节点列表中 } else//如果没有 { List<TreeNode> listChild = new List<TreeNode>(); listChild.Add(node);//兄弟节点列表 dicNode.Add(parentId, listChild); } }
2如何根据叶节点的数量和报价计算各级父节点的报价?
由于每一个节点的报价都是通过它的子节点的报价计算出来的,似乎在树建成后,还需要遍历一次计算报价?如何写代码?似乎还真不好处理?
想不到最总却是通过属性解决的。
核心代码如下:
private double m_Price = 0;//默认为0, public double Price { get { if (m_Price == 0)//如果是默认值,则调用方法计算 { m_Price = GetPrice(); } return m_Price; } set { m_Price = value; } } /// <summary> /// 通过子节点计算此节点的报价 /// </summary> /// <returns></returns> private double GetPrice() { if (m_Price > 0)//已经计算过,直接返回 { return m_Price; } double PriceTemp = 0; if (children != null && children.Count > 0) { foreach (TreeNode node in children)// 通过子节点计算此节点的报价 { if (node.Count.HasValue) { PriceTemp += node.Count.Value * node.Price; } else { PriceTemp += node.Price; } } } return PriceTemp; }
完整代码:
public class TreeNode { private int m_ID; public int ID { get { return ID; } set { ID = value; } } private int? m_ParentID; public int? ParentID { get { return m_ParentID; } set { m_ParentID = value; } } private int? m_Count; public int? Count { get { return m_Count; } set { m_Count = value; } } private string m_PL_Code; public string PL_Code { get { return m_PL_Code; } set { m_PL_Code = value; } } private string m_Name; public string Name { get { return m_Name; } set { m_Name = value; } } private double m_Price = 0;//默认为0, public double Price { get { if (m_Price == 0)//如果是默认值,则调用方法计算 { m_Price = GetPrice(); } return m_Price; } set { m_Price = value; } } /// <summary> /// 通过子节点计算此节点的报价 /// </summary> /// <returns></returns> private double GetPrice() { if (m_Price > 0)//已经计算过,直接返回 { return m_Price; } double PriceTemp = 0; if (children != null && children.Count > 0) { foreach (TreeNode node in children)// 通过子节点计算此节点的报价 { if (node.Count.HasValue) { PriceTemp += node.Count.Value * node.Price; } else { PriceTemp += node.Price; } } } return PriceTemp; } private List<TreeNode> m_children; public List<TreeNode> children { get { return m_children; } set { m_children = value; } } /// <summary> /// 返回需要的树 /// </summary> /// <param name="listAllNodes">所有的节点信息</param> /// <param name="dicNode">键为父节点编号,值为父节点对应的子节点 ,初始为需要显示的叶节点</param> /// <returns></returns> public static List<TreeNode> GeTree(List<TreeNode> listAllNodes, Dictionary<int, List<TreeNode>> dicNode) { int rootNodeId = -1;//虚拟的根节点编号 while (!dicNode.ContainsKey(rootNodeId)) //如果没有到根节点继续 { dicNode = GetOneLevelNodes(listAllNodes, dicNode); } return dicNode[rootNodeId]; } /// <summary> /// 返回某一层的所有节点,包含子节点 /// </summary> /// <param name="listAllNodes">所有的节点信息</param> /// <param name="dicNode">键为父节点编号,值为父节点对应的子节点</param> /// <returns></returns> private static Dictionary<int, List<TreeNode>> GetOneLevelNodes(List<TreeNode> listAllNodes, Dictionary<int, List<TreeNode>> dicNode) { int rootNodeId = -1; //虚拟的根节点编号 List<int> ParentsIdList = new List<int>(); ParentsIdList.AddRange(dicNode.Keys);//这次需要添加的节点编号 Dictionary<int, List<TreeNode>> dicNode2 = new Dictionary<int, List<TreeNode>>();//键为父节点编号,值为父节点对应的子节点 for (int i = listAllNodes.Count - 1; i >= 0; i--) { TreeNode item = listAllNodes[i]; if (ParentsIdList.Contains(item.ID)) { int parentId = rootNodeId;//默认为顶级节点 if (item.ParentID.HasValue)//如果存在父节点 { parentId = item.ParentID.Value; } List<TreeNode> children = dicNode[item.ID];//获取此节点对应的子节点 item.children = children;//添加子节点 AddOneNode(parentId, dicNode2, item);//添加节点到字典中 listAllNodes.RemoveAt(i);//移除已经处理的节点 } } return dicNode2; } /// <summary> /// 合并具有相同父节点的兄弟节点 /// </summary> /// <param name="parentId">需要处理的节点的父节点</param> /// <param name="dicNode">此层对应的节点字典</param> /// <param name="node">需要处理的节点</param> private static void AddOneNode(int parentId, Dictionary<int, List<TreeNode>> dicNode, TreeNode node) { if (dicNode.ContainsKey(parentId))//如果有 { dicNode[parentId].Add(node);//如果具有相同的父节点,则添加到相应的兄弟节点列表中 } else//如果没有 { List<TreeNode> listChild = new List<TreeNode>(); listChild.Add(node);//兄弟节点列表 dicNode.Add(parentId, listChild); } } }