博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
线索二叉树
阅读量:6971 次
发布时间:2019-06-27

本文共 4052 字,大约阅读时间需要 13 分钟。

1. 基本概念

      在链式存储中,发现二叉链表中存在大量的空指针,如果利用这些空指针指向其直接前驱或后继的指针,则可以更方便地运用某些二叉树操作算法。二叉树的线索化,是为了加快查找结点前驱和后继的速度。

      在有N个结点的二叉树中,存在N+1个空指针。每个叶结点有2个空指针,度为1的结点有1个空指针,总的空指针为2N0+N1,又有N0=N2+1,所以总的空指针为N0+N1+N2+1=N+1。

      二叉树线索化规则:若无左子树,令lchild指向其前驱结点;若无右子树,令rchild指向其后继结点。如图,增加两个标志域,用来表明当前指针指向左右结点还是前驱后继结点。

 

其中标志位含义为:

 

线索二叉树的结点:

private class Node{    int data;    Node lchild, rchild;    int ltag = 0, rtag = 0;}

      以这种结点构成的二叉链表作为二叉树的存储结构,叫做线索链表,其中指向前驱和后继的指针,叫做线索,加上线索的二叉树叫做线索二叉树,对二叉树以某种次序遍历使其变为线索二叉树的过程叫做线索化

2. 二叉树的构造及线索化

      构造:直接使用完全二叉树序列递归创建二叉树,不存在的使用0,这里不再赘述。直接给出代码。

public class ThreadTree {        private Integer[] nodes; // 存储完全二叉树序列    private int n; // 树结点数    Node root; // 根结点    Node pre; // 前一个访问的结点    private class Node{        int data;        Node lchild, rchild;        int ltag = 0, rtag = 0;    }        public ThreadTree(){        System.out.println("输入一个完全二叉树序列,不存在的结点用0代替,使用逗号隔开:");//        String[] ins = StdIn.readString().split(",");        String[] ins  = "1,2,3,0,4,0,5".split(",");        nodes = new Integer[ins.length];        for (int i = 0; i < ins.length; i++) {            nodes[i] = Integer.valueOf(ins[i]);        }        n = ins.length;        root = build(1);        TreeUtil.print(depth(root), n, nodes);    }        /**     * 递归创建一棵二叉树     * 

* 使用完全二叉树序列 */ public Node build(int index){ if(index > n) { return null; } if(nodes[index-1]==0){ return null; } Node node = new Node(); node.data = nodes[index-1]; node.lchild = build(2 * index); node.rchild = build(2 * index + 1); return node; }}

      线索化一个二叉树其实就是遍历一次二叉树,遍历过程中,检查当前结点左右指针是否为空,若为空,将它们改为指向前驱或后继结点。

      以中序遍历为例,那么结点的前驱和后继的结点就是二叉树中序遍历序列中的前后结点

算法描述:node指向当前结点,pre指向前一个访问的结点

(1)若node的左孩子为空,则修改左指针指向pre,置ltag为1

(2)若pre不为空,且不存在右孩子,则修改右指针指向node,置rtag为1

(3)使pre指向刚刚访问过的结点node,即pre = node

public void inThreaded(){    inThreaded(root);    pre.rchild = null; // 单独处理下最后一个结点    pre.rtag = 1;}/** * 中序线索化一个二叉树 */public void inThreaded(Node node){    if (node != null) {        inThreaded(node.lchild); // 线索化左子树,找到左侧一个没有左孩子的结点        if(node.lchild == null){ // 左孩子为空            node.lchild = pre; // 修改指向其前驱结点            node.ltag = 1; // 修改标志位        }        if(pre != null && pre.rchild == null){ // 如果前一个访问的结点不为空,且没有右孩子            pre.rchild = node; // 修改指向其后继结点            pre.rtag = 1; // 修改标志位        }        pre = node;        inThreaded(node.rchild); // 线索化右子树    }}

3. 线索化的遍历

     中序线索化二叉树主要目的就是加快访问前驱和后继的速度,这种遍历就不需要借助栈,因为结点中隐含了前驱和后继结点的信息。不含头结点的线索二叉树遍历算法如下:

(1)首先找到中序线索二叉树中的第一个结点,左侧没有左孩子的结点,不一定是叶结点,根据ltag标志为判断

(2)找结点的后继结点,根据rtag判断

/** * 中序线索二叉树遍历,非递归 * @param node */public void inOrder(Node node){    Node tmp = firstNode(node);    while(tmp != null){        System.out.print(tmp.data+" ");        tmp = nextNode(tmp);    }}/** * 求第一个结点 * @param node * @return */public Node firstNode(Node node){    while(node.ltag == 0){ // 最左下结点,不一定是叶子结点        node = node.lchild;    }    return node;}/** * 求后继结点 * @param node * @return */public Node nextNode(Node node){    if(node.rtag == 0){ // 存在右孩子        return firstNode(node.rchild); // 以此结点为根找最左下结点    }    return node.rchild; // rtag = 1 直接返回后继}/** * 倒序非递归遍历 * @param node */public void inOrderO(Node node){    Node tmp = lastNode(node);    while(tmp != null){        System.out.print(tmp.data+" ");        tmp = preNode(tmp);    }}/** * 求最后一个结点 * @return */public Node lastNode(Node node){    while(node.rtag == 0){        node = node.rchild;    }    return node;}/** * 求前驱结点 * @return */public Node preNode(Node node){    if(node.ltag == 0){        return lastNode(node.lchild);    }    return node.lchild;}

4. 测试

public static void main(String[] args) {    ThreadTree tree = new ThreadTree();    System.out.print("二叉树的结点总数:" + tree.nodes(tree.root));    System.out.print("\n中序遍历递归:");    tree.inOrderRecur(tree.root);    tree.inThreaded();    System.out.print("\n线索化...\n中序线索化二叉树遍历非递归:");    tree.inOrder(tree.root);    System.out.print("\n非递归遍历(倒序):");    tree.inOrderO(tree.root);}

4.1 输出结果

 

转载于:https://www.cnblogs.com/cwane/p/5537162.html

你可能感兴趣的文章
数据类型和Json格式
查看>>
CodeIgniter连接数据库
查看>>
vi vim配置
查看>>
PP日志-Day 3
查看>>
eclipse 调试 jdk 看不到变量的值
查看>>
如何解决分配到Autoconfiguration IPV4 地址
查看>>
.NET 远程操作MSMSQ无权限或操作出错问题解决
查看>>
浅谈企业信息泄密:信任不存,效益焉在?
查看>>
Mysql密码管理及授权
查看>>
JAVA线程安全之synchronized关键字的正确用法
查看>>
springmvc+mybatis+dubbo分布式平台-maven构建根项目
查看>>
一个小常识
查看>>
Nginx防盗链 Nginx访问控制 Nginx解析php相关配置 Nginx代理
查看>>
解决虚拟机中使用ntpdate报错:ntpdate[46700]: no server suitab
查看>>
Docker 快速删除所有容器
查看>>
【OCP认证12c题库】CUUG 071题库考试原题及答案(27)
查看>>
OSS支持IPV6/IPV4双栈访问域名
查看>>
阿里云应用实时监控 ARMS 再升级,支持 Prometheus 开源生态
查看>>
最全面的IGMP协议总结!
查看>>
你还在 Select * 吗?
查看>>