数据结构--树形结构(2)

二叉树的实现与遍历
本文介绍了二叉树及其节点的实现方法,并详细解释了如何通过代码实现二叉树的基本操作,如插入、删除节点及判断空树和叶子节点。此外,还探讨了二叉树的遍历算法,包括递归与非递归实现。

二叉树以及其节点的实现

1.
二叉树节点的定义及实现
根据二叉树的特点可知,二叉树的子节点最多有两个子树,分别是左子树和右子树。所以,一个二叉树的节点应有三个部分组成:

² 当前节点数据<T>

² 左子节点的引用;

² 右子节点的引用。

 

如下图左部分的Node类定义。


 

Node类的实现:

ContractedBlock.gif ExpandedBlockStart.gif Code
/// <summary>
    
/// 二叉树节点代码
    
/// </summary>
    
/// <typeparam name="T"></typeparam>
    public class Node<T>
    {
        
private T _data;
        
private Node<T> _lChild;
        
private Node<T> _rChild;

        
/// <summary>
        
/// 二叉树当前节点的数据
        
/// </summary>
        public T Data
        {
            
get { return _data; }
            
set { _data = value; }
        }

        
/// <summary>
        
/// 二叉树左子节点的引用
        
/// </summary>
        public Node<T> LChild
        {
            
get { return _lChild; }
            
set { _lChild = value; }
        }


        
/// <summary>
        
/// 二叉树右子节点的引用
        
/// </summary>
        public Node<T> RChild
        {
            
get { return _rChild; }
            
set { _rChild = value; }
        }

        
public Node()
        {
            _data 
= default(T);
            _lChild 
= null;
            _rChild 
= null;
        }

        
public Node(T _data)
        {
            
this._data = _data;
            _lChild 
= null;
            _rChild 
= null;
        }

        
public Node(T _data, Node<T> _lChild, Node<T> _rChild)
        {
            
this._data = _data;
            
this._lChild = _lChild;
            
this._rChild = _rChild;
        }

        
public Node(Node<T> _lChild, Node<T> _rChild)
        {
            
this._lChild = _lChild;
            
this._rChild = _rChild;
        }

    }

 

2 二叉树的实现

二叉树的实现主要有能下几个功能:

Ø 构造一个新的二叉树

Ø 插入指定节点的左子节点;

Ø 插入指定节点的右子节点;

Ø 删除指定节点的左子节点;

Ø 删除指定节点的右子节点;

Ø 判断二叉树是否为空;

Ø 判断节点是否为叶子节点。

代码实现如下: 

ContractedBlock.gif ExpandedBlockStart.gif Code
  1ExpandedBlockStart.gifContractedBlock.gif /**//// <summary>
  2    /// 二叉树的操作实现
  3    /// </summary>
  4    /// <typeparam name="T"></typeparam>

  5    public class BinaryTree<T>
  6ExpandedBlockStart.gifContractedBlock.gif    {
  7        private Node<T> _header;
  8
  9
 10ContractedSubBlock.gifExpandedSubBlockStart.gif        ..构造函数..#region ..构造函数..
 11
 12        public BinaryTree()
 13ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 14            _header = null;
 15        }

 16
 17        public BinaryTree(T _data)
 18ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 19            Node<T> _headerNode = new Node<T>(_data);
 20            this._header = _headerNode;
 21        }

 22
 23        public BinaryTree(T _data, Node<T> _lChild, Node<T> _rChild)
 24ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 25            Node<T> _headerNode = new Node<T>(_data, _lChild, _rChild);
 26            this._header = _headerNode;
 27        }

 28
 29        #endregion

 30
 31ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 32        /// 二叉树根节点(头节点)
 33        /// </summary>

 34        public Node<T> Header
 35ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 36ExpandedSubBlockStart.gifContractedSubBlock.gif            get return _header; }
 37ExpandedSubBlockStart.gifContractedSubBlock.gif            set { _header = value; }
 38        }

 39
 40ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 41        /// 插入左子节点
 42        /// </summary>
 43        /// <param name="_fNode"></param>
 44        /// <param name="_data"></param>

 45        public void InsertL(Node<T> _fNode, T _data)
 46ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 47ExpandedSubBlockStart.gifContractedSubBlock.gif            /**//*
 48             * 步骤:
 49             * 1 New新的节点
 50             * 2 新节点引用要原父节点的左子节点
 51             * 3 父节点的左子节点引用新节点
 52             */

 53            Node<T> _node = new Node<T>(_data);
 54            _node.LChild = _fNode.LChild;
 55            _fNode.LChild = _node;
 56        }

 57
 58ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 59        /// 插入右子节点
 60        /// </summary>
 61        /// <param name="_fNode"></param>
 62        /// <param name="_data"></param>

 63        public void InsertR(Node<T> _fNode, T _data)
 64ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 65            //思路同插入左子节点
 66            Node<T> _node = new Node<T>(_data);
 67            _node.LChild = _fNode.RChild;
 68            _fNode.RChild = _node;
 69        }

 70
 71
 72ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 73        /// 删除指定节点的左子节点
 74        /// </summary>
 75        /// <param name="node"></param>

 76        public Node<T> DeleteL(Node<T> node)
 77ExpandedSubBlockStart.gifContractedSubBlock.gif        {
 78            if (node == null || node.LChild == null)
 79ExpandedSubBlockStart.gifContractedSubBlock.gif            {
 80                return null;
 81            }

 82ExpandedSubBlockStart.gifContractedSubBlock.gif            /**//*
 83             * 步骤:
 84             * 1 获取左子节点的引用
 85             * 2 把指定节点的左子节点引用设为NULL
 86             * 3 返回左子节点的引用
 87             */

 88            Node<T> lChild = node.LChild;
 89            node.LChild = null;
 90            return lChild;
 91        }

 92
 93ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
 94        /// 删除指定节点的右子节点
 95        /// </summary>
 96        /// <param name="node"></param>
 97        /// <returns></returns>

 98        public Node<T> DeleteR(Node<T> node)
 99ExpandedSubBlockStart.gifContractedSubBlock.gif        {
100            if (node == null || node.RChild == null)
101ExpandedSubBlockStart.gifContractedSubBlock.gif            {
102                return null;
103            }

104            //步骤同删除指定节点的左子节点相同,只不过是首先获取的是右子节点的引用
105            Node<T> rChild = node.RChild;
106            node.RChild = null;
107            return rChild;
108        }

109
110ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
111        /// 判断是否为空
112        /// </summary>
113        /// <returns></returns>

114        public bool IsEmpty()
115ExpandedSubBlockStart.gifContractedSubBlock.gif        {
116            return _header == null;
117        }

118
119ExpandedSubBlockStart.gifContractedSubBlock.gif        /**//// <summary>
120        /// 判断指定的节点是否为叶子节点
121        /// </summary>
122        /// <param name="_node"></param>
123        /// <returns></returns>

124        public bool IsLeaf(Node<T> _node)
125ExpandedSubBlockStart.gifContractedSubBlock.gif        {
126            //叶子结点(Leaf Node):度为0的结点,也叫终端结点
127            return (_node != null && _node.LChild == null && _node.RChild == null);
128        }

129    }

 

3 二叉树的遍历
二叉树的遍历是按照某种顺序对树中的每个节点访问且只能访问一次的过程,包括查询、修改、计算等。
二叉树的遍历实际是将非线性结构线性化,它是二叉树的各种计算和操作的基础。

 

由二叉树的定义可知,一棵二叉树由根结点、左子树和右子树三部分组成,若规定DLR分别代表遍历根结点、遍历左子树、遍历右子树,则二叉树的遍历方式有6种:DLRDRLLDRLRDRDLRLD。由于先遍历左子树和先遍历右子树在算法设计上没有本质区别,所以,只讨论三种方式:DLR(先序遍历)、LDR(中序遍历)和LRD(后序遍历)

                  先序遍历:先访问根节点,再访问左子树,再访问右子树。

                  中序遍历:先访问左子树,再访问根节点,最后访问右子树。

                  后序遍历:先访问左子以,再访问右子树,最后访问根节点。

 

 

3.1 先序遍历(DLR

先序遍历的基本思想是:首先访问根结点,然后先序遍历其左子树,最后先序遍历其右子树。先序遍历的递归算法实现如下。(这里只是获取节点的值) 

ContractedBlock.gif ExpandedBlockStart.gif Code
 /// <summary>
        
/// 先序遍历之递归
        
/// </summary>
        
/// <param name="node"></param>
        
/// <returns></returns>
        public string perOrderNode(Node<char> node)
        {
            
if (node == null)
                
return null;

            StringBuilder nodeValues 
= new StringBuilder();
            nodeValues.Append(node.Data);

            
//递归调用
            nodeValues.Append(perOrderNode(node.LChild));
            nodeValues.Append(perOrderNode(node.RChild));

            
return nodeValues.ToString();
        }


        
public string perOrderRecursion(BinaryTree.BinaryTree<char> tree)
        {
            
return perOrderNode(tree.Header);
        }

  非递归算法实现: 

ContractedBlock.gif ExpandedBlockStart.gif Code
 /// <summary>
        
/// 二叉树先序遍历之非递归算法
        
/// 利用栈实现二叉树的遍历.
        
/// </summary>
        
/// <param name="tree"></param>
        
/// <returns></returns>
        public string perOrderNoRecursion(BinaryTree.BinaryTree<char> tree)
        {
            StringBuilder result 
= new StringBuilder();

            Stack
<Node<char>> rightStack = new Stack<Node<char>>();

            Node
<char> curNode = tree.Header;
            
while (curNode != null || rightStack.Count != 0)
            {

                
if (curNode != null)
                {
                    result.Append(curNode.Data);
                    
if (curNode.RChild != null
                    {
                        
//如果右子节点不为NULL,则先放入Stack中.
                        rightStack.Push(curNode.RChild);
                    }
                    curNode 
= curNode.LChild;
                }
                
else
                {
                    curNode 
= rightStack.Pop();
                }
            }
            
return result.ToString();
        }

   

3.2 中序遍历(LDR

中序遍历的基本思想是:首先中序遍历根结点的左子树,然后访问根结点,最后中序遍历其右子树。中序遍历的递归算法实现如下: (附件)

3.3 后序遍历(LRD

后序遍历的基本思想是:首先后序遍历根结点的左子树,然后后序遍历根结点的右子树,最后访问根结点。后序遍历的递归算法实现如下:  (附件)
代码下载

转载于:https://www.cnblogs.com/wservices/archive/2009/08/23/1552427.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值