在开发软件的过程中,遇到一个树形结构的问题,如下图:
由于树形结构太大,只显示部分叶节点,并且报价是叶节点的数量和报价的成积。
问题经分析很显然:变成了如下两个问题:
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);
}
}
}