在開發軟體的過程中,遇到一個樹形結構的問題,如:
由於樹形結構太大,只顯示部分葉節點,並且報價是分葉節點的數量和報價的成積。
問題經分析很顯然:變成了如下兩個問題:
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); } }
由於每一個節點的報價都是通過它的子節點的報價計算出來的,似乎在樹建成後,還需要遍曆一次計算報價?如何寫代碼?似乎還真不好處理?
想不到最總卻是通過屬性解決的。
核心代碼如下:
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); } } }