public class TreeNode
{
public string Key { get; set; }
public object Data { get; set; }
public TreeNode Parent;
public List<TreeNode> Children { get; set; }
public TreeNode(string Key,object Data)
{
Children = new List<TreeNode>();
this.Key = Key;
this.Data = Data;
}
public void AddChild(TreeNode node)
{
this.Children.Add(node);
node.Parent = this;
}
public void DelChild(TreeNode node)
{
this.Children.Remove(node);
}
}
public class Trees
{
public TreeNode Root { get; set; }
/// <summary>
/// 非递归遍历树方法,这里采用的先父后子的顺序遍历.如果要先子后父的话,则需要用到两个栈,两个循环.
/// </summary>
/// <param name="ListCurrNode"></param>
public void ListChildren(Action<TreeNode> ListCurrNode)
{
if (Root == null)
{
return;
}
//先父后子
Stack<TreeNode> _nodesStack1 = new Stack<TreeNode>();
while (_nodesStack1.Count > 0)
{
TreeNode node = _nodesStack1.Pop();
ListCurrNode(node);
foreach (var item in node.Children)
{
_nodesStack1.Push(item);
}
}
//先子后父.其实对于二叉树的左序和右序访问,也需要两个栈来实现.
_nodesStack1.Clear();
_nodesStack1.Push(Root);
Stack<TreeNode> _nodesStack2 = new Stack<TreeNode>();
while (_nodesStack1.Count > 0)
{
TreeNode node = _nodesStack1.Pop();
_nodesStack2.Push(node);
foreach (var item in node.Children)
{
_nodesStack1.Push(item);
}
}
while (_nodesStack2.Count > 0)
{
TreeNode node = _nodesStack1.Pop();
ListCurrNode(node);
}
}
/// <summary>
/// 非递归中序,线性时间,常数额外辅助空间方式,不能修改树(临时的也不行),可以遍历任意的叉树.对于K叉树,每次退回的比较时间为<=K,
/// 因为每个节点至多需退回一次,则退回比较为<=Kn次,树遍历本身的O(n).遍历还是线性时间。
/// 其实如果可以修改树,比如设置访问标志,则直接可以退回,然后继续访问第1个未访问的子节点。
/// 下面的算法其实就是利用这种思想,只是不是直接设置标志,而是利用了数组指针的特性。
/// 对于左子右兄弟的表达是任意叉树也可以实现,只是退回遍历方法不同而已(更简单).
/// 如果采用的是K个孩子指针变量的表示孩子节点的方式,也是适用的,只是需要默认一个访问逻辑顺序即可.
/// 注意:这里其实采用的是先父后子的访问顺序,如果需要先子后父,也是可以的,需要增加一个子访问完成退回标记,稍稍修改
/// 一下代码即可.
/// </summary>
/// <param name="ListCurrNode">遍历访问者</param>
public void ListAll1(Action<TreeNode> ListCurrNode)
{
int theQuitFrom = -1;//-1表示从父节点来,0-n表示从第k个孩子回来.
TreeNode theCurrNode = Root;
while (theCurrNode != null)
{
TreeNode theTmp = null;
if (theQuitFrom < 0)//刚从父节点过来(theQuitFrom=-1)
{
ListCurrNode(theCurrNode);
//从父节点过来时,访问第1个孩子(索引为0).
if (theCurrNode.Children.Count > 0)
{
theTmp = theCurrNode.Children[0];
}
else
{
theTmp = null;
}
}
else //从孩子节点遍历退回来,则去下一个孩子节点。
{
if (theQuitFrom + 1 < theCurrNode.Children.Count)
{
theTmp = theCurrNode.Children[theQuitFrom + 1];
theQuitFrom = -1;
}
else//孩子节点访问完成,theTmp置空,继续执行后退策略.
{
theTmp = null;//如果要先子后父,需要在这里做标记
}
}
if (theTmp == null) //如果theTmp为空,则表示无法继续向下遍历,需后退访问兄弟节点
{
//如果当前节点是root,表示已经需从root退回,则表示整个树遍历完成,
//这里也保证了GetChildrenIndex不会返回-1的情况.
if (theCurrNode == Root)
break;
theQuitFrom = GetChildrenIndex(theCurrNode.Parent, theCurrNode);
theCurrNode = theCurrNode.Parent;
}
else //不为空,则将theTmp节点作为当前节点进行访问。
{
//这种情况下,theQuitFrom一定是-1.因为初始为-1,只有退回的时候才会改变这个值,
//如果退回有下一个兄弟可访问,则theQuitFrom一定会被设置为-1,如果没有,则只能继续
//退.如果能继续访问,theQuitFrom一定保持着-1.
theCurrNode = theTmp;
}
}
}
/// <summary>
/// 获取孩子节点在父节点中的位置.
/// </summary>
/// <param name="Parent"></param>
/// <param name="Child"></param>
/// <returns></returns>
private int GetChildrenIndex(TreeNode Parent, TreeNode Child)
{
for (int i = 0; i < Parent.Children.Count; i++)
{
if (Parent.Children[i] == Child)
{
return i;
}
}
//实际上这里放回-1是没法执行的.
return -1;
}
}
本文参考了CSDN网友的文章:
http://blog.csdn.net/acrazer/article/details/4454508
算法笔记(有根树遍历)
最新推荐文章于 2024-01-04 14:50:21 发布