二叉树的遍历方法及根据前/中(后/中)序遍历求后(前)序遍历的代码实现

一、前序、中序、后序遍历的特性

前序遍历: 
    1.访问根节点 
    2.前序遍历左子树 
    3.前序遍历右子树 
中序遍历: 
    1.中序遍历左子树 
    2.访问根节点 
    3.中序遍历右子树 
后序遍历: 
    1.后序遍历左子树 
    2.后序遍历右子树 
    3.访问根节点 

二、二叉树的存储结构

 二叉树的存储可分为两种:顺序存储结构和链式存储结构。

1. 顺序存储结构

把一个满二叉树自上而下、从左到右顺序编号,依次存放在数组内,设满二叉树结点在数组中的索引号为i,那么有如下性质。

(1) 如果i = 0,此结点为根结点,无父结点。

(2) 如果i > 0,则其父结点为(i -1) / 2 。(注意,这里的除法是整除,结果中的小数部分会被舍弃。)

(3) 结点i的左孩子为2i + 1,右孩子为2i + 2。

(4) 如果i > 0,当i为奇数时,它是父结点的左孩子,它的兄弟为i + 1;当i为偶数时,它是父结点的右孩子,它的兄弟结点为i – 1。

(5) 深度为k的满二叉树需要长度为2 k-1的数组进行存储。

通过以上性质可知,使用数组存放满二叉树的各结点非常方便,可以根据一个结点的索引号很容易地推算出它的父、孩子、兄弟等结点的编号,从而对这些结点进行访问,这是一种存储二叉满二叉树或完全二叉树的最简单、最省空间的做法。为了用结点在数组中的位置反映出结点之间的逻辑关系,存储一般二叉树时,只需要将数组中空结点所对应的位置设为空即可。

2.链式存储结构

二叉树的链式存储结构可分为二叉链表和三叉链表。二叉链表中,每个结点除了存储本身的数据外,还应该设置两个指针域left和right,它们分别指向左孩子和右孩子。当需要在二叉树中经常寻找某结点的父结点,每个结点还可以加一个指向父结点的指针域parent,这就成了三叉链表。 

三、C#代码实现:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BinaryTreeTest
{
    class Program
    {
        static void Main(string[] args)
        {
            BinaryTree bTree = new BinaryTree("abcdefg###h##ij");
            Console.WriteLine("前序遍历:"+bTree.PreOrder(bTree.Head));
            Console.WriteLine("中序遍历:"+bTree.MidOrder(bTree.Head));
            Console.WriteLine("后序遍历:"+bTree.AftOrder(bTree.Head));
            Console.WriteLine();

            Console.WriteLine("前序遍历:" + bTree.PreOrder(bTree.Head));
            Console.WriteLine("中序遍历:" + bTree.MidOrder(bTree.Head));
            Console.WriteLine("后序遍历:" + bTree.AftOrder(bTree.Head));
            Console.WriteLine();

            Console.Write("后序遍历:");
            BinaryTreeOrder.PreMid("abdehcfgij", "dbehafcigj");
            Console.WriteLine();

            Console.Write("前序遍历:");
            BinaryTreeOrder.AftMid("dhebfijgca", "dbehafcigj");
            Console.ReadLine();
        }
    }

    public class Node
    {
        //成员变量
        private string _data;//节点数据
        private Node _lChild; //左孩子
        private Node _rChild; //右孩子

        //get和set方法
        public string Data
        {
            get { return _data; }
            set { _data = value; }
        }

        public Node lChild
        {
            get { return _lChild; }
            set { _lChild = value; }
        }

        public Node rChild
        {
            get { return _rChild; }
            set { _rChild = value; }
        }

        //构造方法
        public Node() { 
             
        }

        public Node(string data) 
        {
            this._data = data;
        }

    }

    //是一个二叉树的集合类,它属于二叉链表
    public class BinaryTree
    {
        //成员变量
        private Node _head;//头指针
        private string cStr;//用于构造二叉树的字符串
        
        //get方法
        public Node Head
        {
            get { return _head; }
        }


        //构造方法,字符串由满二叉树的方式进行构造,空结点用‘#’号表示
        public BinaryTree(string _cStr)
        {
            this.cStr = _cStr;
            _head = new Node(cStr[0].ToString());
            Add(_head, 0);
        }

        private void Add(Node parentNode, int pindex)
        {
            int lIndex = 2 * pindex + 1;//计算左孩子的索引
            if (lIndex < cStr.Length)   //如果索引没有超出字符串的长度
            {
                if (cStr[lIndex] != '#')//‘#’表示空节点
                {
                    parentNode.lChild = new Node(cStr[lIndex].ToString());//将节点赋给当前节点的左节点
                    Add(parentNode.lChild, lIndex);//递归调用Add方法给左孩子添加孩子
                }
            }

            int rIndex = 2 * pindex + 2;  //计算右孩子的索引
            if (rIndex < cStr.Length)     //若索引没有超出字符串的长度
            {
                if (cStr[rIndex] != '#')  //‘#’表示空节点
                {
                    parentNode.rChild = new Node(cStr[rIndex].ToString());//将当前字符赋给当前节点的右孩子节点
                    Add(parentNode.rChild, rIndex);//递归调用Add方法给右孩子添加孩子
                }
            }
        }

        //先序遍历
        private string _PreStr="";
        public string PreOrder(Node node)
        {
            if (node != null)
            {
                if (node.Equals(Head))
                {
                    _PreStr = "";
                }
                _PreStr += node.Data;

                PreOrder(node.lChild);
                PreOrder(node.rChild);
            }
            return _PreStr;

        }

        //中序遍历
        private string _MidStr="";
        public string MidOrder(Node node)
        {
            if (node != null)
            {
                if (node.Equals(Head))
                {
                    _MidStr = "";
                }

                MidOrder(node.lChild);
                _MidStr += node.Data;
                MidOrder(node.rChild);
            }
            return _MidStr;
        }

        //后序遍历
        private string _AftStr="";
        public string AftOrder(Node node)
        {
            if (node != null)
            {
                if (node.Equals(Head))
                {
                    _AftStr = "";
                }

                AftOrder(node.lChild);
                AftOrder(node.rChild);
                _AftStr += node.Data;
            }
            return _AftStr;
        }

    }


    public class BinaryTreeOrder
    {

        //查找字符串中相应字符的位置
        private static int find(string str, char c)
        {
            for (int i = 0; i < str.Length; i++)
            {
                if (c == str[i])
                    return i;
            }
            return -1;
        }

        /// <summary>
        /// 根据二叉树的前序遍历和中序遍历结果求后序遍历结果
        /// </summary>
        /// <param name="_pre">前序遍历字符串</param>
        /// <param name="_mid">中序遍历字符串</param>
        public static void PreMid(string _pre, string _mid)
        {
            if (_pre.Length == 0)
            {
                return;
            }
            else if(_pre.Length == 1)
            {
                Console.Write(_pre[0]);
            }
            else
            {
                int index = find(_mid, _pre[0]);//找前序中第一个元素(第一个元素为根结点)在中序中的位置

                string preTempPre = _pre.Substring(1, index);
                string midTempPre = _mid.Substring(0, index);
                PreMid(preTempPre, midTempPre);

                string preTempAft = _pre.Substring(index + 1, _pre.Length - index - 1);
                string midTempAft = _mid.Substring(index + 1, _mid.Length - index - 1);
                PreMid(preTempAft, midTempAft);

                Console.Write(_pre[0]);//变成后序遍历要最后输出结点的值

            }
        }        

        /// <summary>
        /// 根据后序和中序遍历结果求前序遍历结果
        /// </summary>
        /// <param name="_aft">后序遍历字符串</param>
        /// <param name="_mid">中序遍历字符串</param>                                                                                     public static void AftMid(string _aft,string _mid)
        {
            if (_aft.Length == 0)
            {
                return;
            }
            else if (_aft.Length == 1)
            {
                Console.Write(_aft[_aft.Length - 1]);
            }
            else
            {
                int index = find(_mid, _aft[_aft.Length - 1]);//找后序中最后一个元素(最后一个元素为根结点)在中序中的位置

                Console.Write(_aft[_aft.Length - 1]);//变成前序遍历要先输出结点的值

                string aftTempPre = _aft.Substring(0, index);
                string midTempPre = _mid.Substring(0, index);
                AftMid(aftTempPre, midTempPre);

                string aftTempAft = _aft.Substring(index, _aft.Length - index - 1);
                string midTempAft = _mid.Substring(index + 1, _mid.Length - index - 1);
                AftMid(aftTempAft, midTempAft);
            }
            
        }

    }
}


 

用前序遍历和中序遍历求后序遍历的注解: 
    假设前序遍历为 abdehcfgij, 中序遍历为 dbehafcigj 
    前序遍历是先访问根节点,然后再访问子树的,而中序遍历则先访问左子树再访问根节点 
    那么把前序的 a 取出来,然后查找 a 在中序遍历中的位置就得到 dbeh a fcigj
    那么我们就知道 dbeh 是左子树 fcigj 是右子树,因为数量要吻合 
    所以前序中相应的 dbeh 是左子树 fcigj 是右子树 
    然后就变成了一个递归的过程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值