算法笔记(有根树遍历)

来源:互联网 发布:iphone视频滤镜软件 编辑:程序博客网 时间:2024/05/08 16:41
 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