首页 > 其他分享 >线索二叉树(Morris Traversal)

线索二叉树(Morris Traversal)

时间:2023-11-07 14:57:34浏览次数:40  
标签:结点 遍历 Traversal Morris right 二叉树 root 线索

在前面的文章中总结了二叉树的一些操作,提供了二叉树前中后的递归和非递归的实现。在非递归的实现中,基本思想是利用栈来模拟递归调用遍历的过程,本质上和递归实现没有区别,空间复杂度为\(O(n)\)。是否存在一种算法,它不使用栈也不破坏二叉树结构,但是可以完成对二叉树的遍历?即:

  1. 空间复杂度为\(O(1)\)
  2. 二叉树的结构不能被破坏

Morris Traversal 能够解决上面的问题,James H. Morris 提出了二叉树线索化,利用结点的空链域来存储结点的前驱和后继信息。

线索二叉树结构

那么什么是线索二叉树呢?

  • 如果结点有左子树,则其left域指示为其左孩子,否则指示其前驱;
  • 如果结点有右子树,则其right域指示为其右孩子,否则指示其后继

对于上述的概念,我们可以用一个比较具体的例子来分析,下面是一个二叉树:

image

该二叉树中序遍历的结果为:4-2-8-5-1-6-3-7, 上面提到的前驱和后继是针对遍历过程来说的,不同的遍历方式一个结点的前驱和后继可能会不同,前驱和后继被称为线索,对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化

下面是线索二叉树的结点Node,在决定 left 是指向左孩子还是前驱,right 是指向右孩子还是后继,我们是需要一个区分标志的。因此我们在 Node类中增加leftThreadrightThread布尔字段,其中:

  1. leftThread 为 true 时指向前驱,为 false 时指向该结点的左子树;
  2. rightThread 为 true 时指向后继,为 false 时指向该结点的右子树。
public static class Node<E extends Comparable<E>> {
    E item;
    Node<E> left;
    Node<E> right;
    boolean leftThread;
    boolean rightThread;

    public Node (E item) {
        this.item = item;
    }


    @Override
    public String toString() {
        return "item=" + item + " left="
                + ((left != null) ? left.item : "NULL") + " right=" + ((right != null) ? right.item : "NULL")
                + " leftThread=" + leftThread + " rightThread=" + rightThread;
    }
}

针对上面二叉树的中序遍历来说,我们需要先将二叉树线索化,形成线索二叉树:

image

二叉树线索化

了解了二叉树的线索化后,接下来就是用代码来将二叉树线索化,这里我们依然采用中序遍历,并使用递归,代码如下:

/**
 * 中序遍历线索化
 *
 * @param root 二叉树根结点
 */
public void inThreading(Node root) {
    if (root == null) {
        return;
    }

    // 按照中序遍历方向,先处理左子树
    inThreading(root.left);

    // 判断当前节点的左孩子是否为空,为空的话则当前节点的左孩子指向其前驱
    if (root.left == null) {
        root.leftThread = true;
        root.left = pre; // 指向前驱
    }

    // pre的右孩子为空
    if (pre != null && pre.right == null) {
        pre.rightThread = true;
        pre.right = root; // pre的右孩子指向root(后继)
    }
    
    pre = root; 

    // 右子树线索化
    inThreading(root.right);
}

上面代码中,就是根据中序遍历的递归代码改写来的,对于任意一个结点,先访问右子树,接着访问该结点,最后访问右子树。pre代表上次访问的结点(相对于当前结点)。

中序遍历

构造完线索二叉树,我们就可以来利用Morris Traversal遍历线索二叉树了,遍历步骤如下:

  1. 找到中序遍历的起点
  2. 该结点不为空,访问该结点
  3. 判断当前结点的是否有后继
    1. 如果有,那么获取其后继结点作为当前结点
    2. 如果没有,说明当前结点有右子树,那么按照中序遍历访问右子树
  4. 重复3操作,直至线索二叉树输出完毕

代码如下:

/**
 * 中序遍历
 *
 * @param root 根结点
 */
public void inOrderNonRec(Node root) {
    if (root == null) {
        return;
    }
    
    // 查找中序遍历的起始节点
    while (root != null && !root.leftThread) {
        root = root.left;
    }
    
    while (root != null) {
        System.out.println(root);
        // 如果右孩子是线索
        if (root.rightThread) {
            root = root.right;
        } else { // 有右孩子
            root = root.right;
            while (root != null && !root.leftThread) {
                root = root.left;
            }
        }
    }
}

References:


title: 线索二叉树(Morris Traversal)
tags: [数据结构, 二叉树]
author: Mingshan
categories: [数据结构, 二叉树]
date: 2019-04-01

标签:结点,遍历,Traversal,Morris,right,二叉树,root,线索
From: https://www.cnblogs.com/mingshan/p/17793613.html

相关文章

  • LeetCode222.完全二叉树的节点个数
    题目描述给你一棵完全二叉树的根节点root,求出该树的节点个数。完全二叉树的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第h层,则该层包含1~2h个节点。示例提交的代......
  • 二叉树理论基础
    二叉树理论基础二叉树的种类满二叉树、完全二叉树、二叉搜索树、平衡二叉搜索树二叉树的存储方式顺序存储、链式存储二叉树的遍历方式二叉树主要有两种遍历方式:深度优先遍历:先往深走,遇到叶子节点再往回走。广度优先遍历:一层一层的去遍历。那么从深度优先遍历和广度优先......
  • 01_二叉树的递归遍历
    二叉树的递归遍历递归算法的三要素确定递归函数的参数和返回值:确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数,并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。确定终止条件:写完了递归算法,运行的时候,经常会遇到栈溢出的错误,就是没写终......
  • 102. 二叉树的层序遍历(中)
    目录题目法一、BFS法二、DFS题目给你二叉树的根节点root,返回其节点值的层序遍历。(即逐层地,从左到右访问所有节点)。示例1:输入:root=[3,9,20,null,null,15,7]输出:[[3],[9,20],[15,7]]示例2:输入:root=[1]输出:[[1]]示例3:输入:root=[]输出:[]法一、BFS......
  • LeetCode101.对称二叉树
    题目描述给你一个二叉树的根节点root,检查它是否轴对称。示例提条的代码importjava.util.List;importjava.util.ArrayList;importjava.util.Deque;importjava.util.LinkedList;importjava.util.Collections;classSolution{publicbooleanisSymmetric(Tr......
  • 数据结构之树(二叉树的存储方式之链表)
    JavaJava中可以使用链表来实现二叉树的存储。1.链表实现二叉树的原理:   链表是由节点组成的数据结构,每个节点包含一个数据和指向下一个节点的指针。  在链表中,可以将二叉树的每个节点都看作一个链表节点,同时维护一个指向左子节点的指针和一个指向右子节点的指针。通过......
  • Binary Tree Level Order Traversal
    SourceGivenabinarytree,returnthelevelordertraversalofitsnodes'values.(ie,fromlefttoright,levelbylevel).ExampleGivenbinarytree{3,9,20,#,#,15,7},3/\920/\157returnitslevelordertraversala......
  • 101. 对称二叉树
    目录题目题解、前序遍历+递归题目给你一个二叉树的根节点root,检查它是否轴对称。示例1:输入:root=[1,2,2,3,4,4,3]输出:true示例2:输入:root=[1,2,2,null,3,null,3]输出:false题解、前序遍历+递归不仅要判断节点带值的情况,还要考虑空节点位置是否相同clas......
  • 关于二叉树中三种深度遍历方式的理解
    今日刷题,538.把二叉搜索树转换为累加树。明确知道利用二叉搜索树中序遍历的情况下是有序数组这一个特点,进行“逆中序”来累加。但是在递归时却还是有些没有搞清楚一些细节,终究还是没有掌握。问题主要还是在于递归返回值的处理上:在中序遍历的情况下,似乎对于左右两个节点的遍历,不......
  • 104.二叉树的最大深度
    目录题目法一、后序遍历法二、层序遍历题目给定一个二叉树root,返回其最大深度。二叉树的最大深度是指从根节点到最远叶子节点的最长路径上的节点数。示例1:输入:root=[3,9,20,null,null,15,7]输出:3示例2:输入:root=[1,null,2]输出:2法一、后序遍历后序遍历......