算法笔记(有根树遍历)

 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
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值