二叉树遍历

作者:喜欢天黑却怕鬼

来源:http://songwenjie.cnblogs.com/

二叉树遍历原理

二叉树的遍历是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。

为什么研究二叉树的遍历?

因为计算机只会处理线性序列,而我们研究遍历,就是把树中的结点变成某种意义的线性序列,这给程序的实现带来了好处。

二叉树的创建

遍历二叉树之前,首先我们要有一个二叉树。要创建一个如下图的二叉树,就要先进行二叉树的扩展,也就是将二叉树每个结点的空指针引出一个虚结点,其值为一个特定值,比如'#'。处理后的二叉树称为原二叉树的扩展二叉树。扩展二叉树的每个遍历序列可以确定一个一颗二叉树,我们采用前序遍历创建二叉树。前序遍历序列:124##5##36##7##。

定义二叉链表结点:

/// <summary>
/// 二叉链表结点类
/// </summary>
/// <typeparam name="T"></typeparam>
public class TreeNode<T>
{
    /// <summary>
    /// 数据域
    /// </summary>
    public T Data { get; set; }
    /// <summary>
    /// 左孩子   
    /// </summary>
    public TreeNode<T> LChild { get; set; } 
    /// <summary>
    /// 右孩子
    /// </summary>
    public TreeNode<T> RChild { get; set; } 

    public TreeNode(T val, TreeNode<T> lp, TreeNode<T> rp)
    {
        Data = val;
        LChild = lp;
        RChild = rp;
    }

    public TreeNode(TreeNode<T> lp, TreeNode<T> rp)
    {
        Data = default(T);
        LChild = lp;
        RChild = rp;
    }

    public TreeNode(T val)
    {
        Data = val;
        LChild = null;
        RChild = null;
    }

    public TreeNode()
    {
        Data = default(T);
        LChild = null;
        RChild = null;
    }
}

先序递归创建二叉树:

/// <summary>
/// 先序创建二叉树
/// </summary>
/// <param name="node"></param>
public static void CreateTree(TreeNode<char> node)
{
    node.Data = Console.ReadKey().KeyChar;

    if (node.Data == '#')
    {
        return;
    }

    node.LChild = new TreeNode<char>();

    CreateTree(node.LChild);

    if (node.LChild.Data == '#')
    {
        node.LChild = null;
    }

    node.RChild = new TreeNode<char>();

    CreateTree(node.RChild);

    if (node.RChild.Data == '#')
    {
        node.RChild = null;
    }
}

二叉树遍历方法

[图片上传失败...(image-b394dd-1544445707477)]

[图片上传失败...(image-a5555b-1544445707477)]

前序遍历

[图片上传失败...(image-9623b4-1544445707477)]

递归方式实现前序遍历

具体过程:

  1. 先访问根节点
  2. 再序遍历左子树
  3. 最后序遍历右子树

代码实现:

public static void PreOrderRecur(TreeNode<char> treeNode)
 {
     if (treeNode == null)
     {
         return;
     }
     Console.Write(treeNode.Data); 
     PreOrderRecur(treeNode.LChild);
     PreOrderRecur(treeNode.RChild);
 }

非递归方式实现前序遍历

具体过程:

  1. 首先申请一个新的栈,记为stack;
  2. 将头结点head压入stack中;
  3. 每次从stack中弹出栈顶节点,记为cur,然后打印cur值,如果cur右孩子不为空,则将右孩子压入栈中;如果cur的左孩子不为空,将其压入stack中;
  4. 重复步骤3,直到stack为空.

代码实现:

 public static void PreOrder(TreeNode<char> head)
{
    if (head == null)
    {
        return;
    }
    Stack<TreeNode<char>> stack = new Stack<TreeNode<char>>();
    stack.Push(head);
    while (!(stack.Count == 0))
    {
        TreeNode<char> cur = stack.Pop();
        Console.Write(cur.Data);

        if (cur.RChild != null)
        {
            stack.Push(cur.RChild);
        }
        if (cur.LChild != null)
        {
            stack.Push(cur.LChild);
        }
    }
}

过程模拟:

[图片上传失败...(image-170082-1544445707477)]

执行结果:[图片上传失败...(image-6b5d83-1544445707477)]

中序遍历

[图片上传失败...(image-7d63c2-1544445707477)]

递归方式实现中序遍历

具体过程:

  1. 先中序遍历左子树
  2. 再访问根节点
  3. 最后中序遍历右子树

代码实现:

public static void InOrderRecur(TreeNode<char> treeNode)
{
    if (treeNode == null)
    {
        return;
    }  
    InOrderRecur(treeNode.LChild);
    Console.Write(treeNode.Data); 
    InOrderRecur(treeNode.RChild);
}

非递归方式实现中序遍历

具体过程:

  1. 申请一个新栈,记为stack,申请一个变量cur,初始时令cur为头节点;
  2. 先把cur节点压入栈中,对以cur节点为头的整棵子树来说,依次把整棵树的左子树压入栈中,即不断令cur=cur.left,然后重复步骤2;
  3. 不断重复步骤2,直到发现cur为空,此时从stack中弹出一个节点记为node,打印node的值,并让cur = node.right,然后继续重复步骤2;
  4. 当stack为空并且cur为空时结束。

代码实现:

public static void InOrder(TreeNode<char> treeNode)
{
    if (treeNode == null)
    {
        return;
    }
    Stack<TreeNode<char>> stack = new Stack<TreeNode<char>>();

    TreeNode<char> cur = treeNode;

    while (!(stack.Count == 0) || cur != null)
    {
        while (cur != null)
        {
            stack.Push(cur);
            cur = cur.LChild;
        }
        TreeNode<char> node = stack.Pop();
        Console.WriteLine(node.Data);
        cur = node.RChild;
    }
}

过程模拟:

[图片上传失败...(image-301552-1544445707476)]

执行结果:[图片上传失败...(image-deb3e9-1544445707476)]

后序遍历

[图片上传失败...(image-6d126c-1544445707476)]

递归方式实现后序遍历

  1. 先后序遍历左子树
  2. 再后序遍历右子树
  3. 最后访问根节点

代码实现:

public static void PosOrderRecur(TreeNode<char> treeNode)
{
    if (treeNode == null)
    {
        return;
    }
    PosOrderRecur(treeNode.LChild);
    PosOrderRecur(treeNode.RChild);
    Console.Write(treeNode.Data); 
}

非递归方式实现后序遍历一

具体过程:

使用两个栈实现

  1. 申请两个栈stack1,stack2,然后将头结点压入stack1中;
  2. 从stack1中弹出的节点记为cur,然后先把cur的左孩子压入stack1中,再把cur的右孩子压入stack1中;
  3. 在整个过程中,每一个从stack1中弹出的节点都放在第二个栈stack2中;
  4. 不断重复步骤2和步骤3,直到stack1为空,过程停止;
  5. 从stack2中依次弹出节点并打印,打印的顺序就是后序遍历的顺序;

代码实现:

public static void PosOrderOne(TreeNode<char> treeNode)
{
    if (treeNode == null)
    {
        return;
    }

    Stack<TreeNode<char>> stack1 = new Stack<TreeNode<char>>();
    Stack<TreeNode<char>> stack2 = new Stack<TreeNode<char>>();

    stack1.Push(treeNode);
    TreeNode<char> cur = treeNode;

    while (!(stack1.Count == 0))
    {
        cur = stack1.Pop();
        if (cur.LChild != null)
        {
            stack1.Push(cur.LChild);
        }
        if (cur.RChild != null)
        {
            stack1.Push(cur.RChild);
        }
        stack2.Push(cur);
    }

    while (!(stack2.Count == 0))
    {
        TreeNode<char> node = stack2.Pop();
        Console.WriteLine(node.Data); ;
    }
}

过程模拟:

[图片上传失败...(image-7742ee-1544445707476)]

执行结果:[图片上传失败...(image-c38e3b-1544445707476)]

非递归方式实现后序遍历二

具体过程:

使用一个栈实现

  1. 申请一个栈stack,将头节点压入stack,同时设置两个变量 h 和 c,在整个流程中,h代表最近一次弹出并打印的节点,c代表当前stack的栈顶节点,初始时令h为头节点,,c为null;
  1. 每次令c等于当前stack的栈顶节点,但是不从stack中弹出节点,此时分一下三种情况:

(1)如果c的左孩子不为空,并且h不等于c的左孩子,也不等于c的右孩子,则吧c的左孩子压入stack中

(2)如果情况1不成立,并且c的右孩子不为空,并且h不等于c的右孩子,则把c的右孩子压入stack中;

(3)如果情况1和2不成立,则从stack中弹出c并打印,然后令h等于c;

  1. 一直重复步骤2,直到stack为空.

代码实现:

public static void PosOrderTwo(TreeNode<char> treeNode)
{
    if (treeNode == null)
    {
        return;
    }

    Stack<TreeNode<char>> stack = new Stack<TreeNode<char>>();
    stack.Push(treeNode);

    TreeNode<char> h = treeNode;
    TreeNode<char> c = null;
    while (!(stack.Count == 0))
    {
        c = stack.Peek();
        //c结点有左孩子 并且 左孩子没被遍历(输出)过 并且 右孩子没被遍历过
        if (c.LChild != null && h != c.LChild && h != c.RChild)
            stack.Push(c.LChild);
        //c结点有右孩子 并且 右孩子没被遍历(输出)过
        else if (c.RChild != null && h != c.RChild)
            stack.Push(c.RChild);
        //c结点没有孩子结点 或者孩子结点已经被遍历(输出)过
        else
        {
            TreeNode<char> node = stack.Pop();
            Console.WriteLine(node.Data);
            h = c;
        }
    }
}

过程模拟:

[图片上传失败...(image-bfa666-1544445707475)]

执行结果:[图片上传失败...(image-1123f9-1544445707475)]

层序遍历

[图片上传失败...(image-ebe3c8-1544445707475)]

具体过程:

  1. 首先申请一个新的队列,记为queue;
  2. 将头结点head压入queue中;
  3. 每次从queue中出队,记为node,然后打印node值,如果node左孩子不为空,则将左孩子入队;如果node的右孩子不为空,则将右孩子入队;
  4. 重复步骤3,直到queue为空。

代码实现:

public static void LevelOrder(TreeNode<char> treeNode)
{
    if(treeNode == null)
    {
         return;
    }
    Queue<TreeNode<char>> queue = new Queue<TreeNode<char>>();
    queue.Enqueue(treeNode);

    while (queue.Any())
    {
        TreeNode<char> node = queue.Dequeue();
        Console.Write(node.Data);

        if (node.Left != null)
        {
            queue.Enqueue(node.Left);
        }

        if (node.Right != null)
        {
            queue.Enqueue(node.Right);
        }
    }
}

执行结果:[图片上传失败...(image-828f7c-1544445707474)]

参考:《大话数据结构》

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342