数据结构与算法——线索二叉树

线索二叉树

package com.gykalc.tree;

public class ThreadedBinaryTreeDemo {
}


/**
 * 线索二叉树:
 * 通过之前的学习,我们知道,二叉树可以使用数组实现,那是顺序存储二叉树
 * 第n个节点的下标为n(n从0开始),第n个节点的左子节点为2n + 1,第n个节点的右子节点为2n + 2
 * 第n个节点的父节点为 (n - 1) / 2
 * <p>
 * 二叉树还可以使用二叉链表来实现,但是二叉链表中,整个二叉链表存在2n个指针域
 * 而n个节点的二叉链表有n-1条分支线,那么空指针域的个数=2n-(n-1) = n+1个空指针域
 * 从存储空间的角度来看,这n+1个空指针域浪费了内存资源。
 * <p>
 * 从另外一个角度来分析,如果我们想知道按中序方式遍历二叉链表时B节点的前驱节点或者后继节点时,
 * 必须要按中序方式遍历二叉链表才能够知道结果,每次需要结果时都需要进行一次遍历,
 * 是否可以考虑提前存储这种前驱和后继的关系来提高时间效率呢?
 * <p>
 * 综合以上两方面的分析,可以通过充分利用二叉链表中的空指针域,
 * 存放节点在某种遍历方式下的前驱和后继节点的指针。我们把这种指向前驱和后继的指针成为线索,
 * 加上线索的二叉链表成为线索链表,对应的二叉树就成为“线索二叉树(Threaded Binary Tree)” 。
 */
class ThreadedBinaryTree<E> {
    // 根节点
    private Node<E> root;

    // 遍历时,访问到的前一个节点
    private Node<E> preNode = null;

    public static void main(String[] args) {
        Integer[] array = {1,2,3,4,5,6,7};
        ThreadedBinaryTree<Integer> tbt = new ThreadedBinaryTree<>();
        tbt.createBinaryTree(array, 0);
        System.out.println("=====前序排序=====");
        tbt.preOrder();

        System.out.println("=====中序排序=====");
        tbt.midOrder();

        System.out.println("=====后序排序=====");
        tbt.postOrder();

        System.out.println("=====中序线索化=====");
//        tbt.preOrderThreaded();
        tbt.midOrderThreaded();
        System.out.println("======中序线索化后遍历=====");
        tbt.midOrderThreadedList();
    }

    /**
     * 创建一个二叉树
     * @param array
     * @param index
     * @return
     */
    private Node<E> createBinaryTree(E[] array, int index) {
        Node<E> node = null;
        if (index < array.length) {
            node = new Node<>(array[index]);
            node.left = createBinaryTree(array, 2 * index + 1);
            node.right = createBinaryTree(array, 2 * index + 2);
        }
        this.root = node;
        return node;
    }

    public void preOrder() {
        this.preOrder(this.root);
    }

    /**
     * 前序遍历
     * @param root
     */
    public void preOrder(Node<E> root) {
        if (root != null) {
            System.out.println(root.getE());
            preOrder(root.left);
            preOrder(root.right);
        }
    }


    public void preOrderThreaded() {
        this.preOrderThreaded(this.root);
    }
    /**
     * 前序遍历线索化
     * @param root
     */
    public void preOrderThreaded(Node<E> root) {
        if (root != null) {
            boolean leftFlag = false;
            // 如果当前节点的left节点为空,说明它要指向一个前驱节点
            if (root.left == null) {
                // 将left指向前一个节点
                root.left = this.preNode;
                root.ltag = true;
                leftFlag = true;
            }
            if (this.preNode != null && this.preNode.right == null) {
                this.preNode.right = root;
                this.preNode.rtag = true;
            }
            this.preNode = root;
            if (!leftFlag) {
                preOrderThreaded(root.left);
            }
            preOrderThreaded(root.right);
        }
    }

    public void midOrderThreaded() {
        this.midOrderThreaded(this.root);
    }
    /**
     * 中序遍历线索化
     * @param root
     */
    public void midOrderThreaded(Node<E> root) {
        if (root != null) {
            // 首先线索化左子节点
            midOrderThreaded(root.left);
            // 再线索化当前节点
            if (root.left == null) { // left指向前驱节点
                root.left = preNode;
                root.ltag = true;
            }
            if (preNode != null && preNode.right == null) { // right指向后驱节点
                preNode.right = root;
                preNode.rtag = true;
            }
            this.preNode = root;
            // 最后线索化右子节点
            midOrderThreaded(root.right);
        }
    }

    /**
     * 中序线索化二叉树后遍历节点
     */
    public void midOrderThreadedList(Node<E> root) {
        if (root == null) {
            return;
        }
        Node<E> temp = root;
        while (temp != null) {
            while (!temp.ltag) {
                if (temp.left == null) {
                    break;
                }
                temp = temp.left;
            }
            // 打印当前节点
            System.out.println(temp.getE());
            while(temp.rtag) {
                temp = temp.right;  // 后继节点
                System.out.println(temp.getE());
            }
            temp = temp.right;
        }
    }

    public void midOrderThreadedList() {
        this.midOrderThreadedList(this.root);
    }

    public void midOrder() {
        this.midOrder(this.root);
    }
    /**
     * 中序遍历
     * @param root
     */
    public void midOrder(Node<E> root) {
        if (root != null) {
            midOrder(root.left);
            System.out.println(root.getE());
            midOrder(root.right);
        }
    }

    public void postOrder() {
        this.postOrder(this.root);
    }
    /**
     * 后序排序
     * @param root
     */
    public void postOrder(Node<E> root) {
        if(root != null) {
            postOrder(root.left);
            postOrder(root.right);
            System.out.println(root.getE());
        }
    }

    /**
     * 节点类
     *
     * @param <E>
     */
    class Node<E> {
        // 节点中存储的数据
        private E e;
        // 节点的左子节点或者某种遍历方式下的前驱节点
        private Node<E> left;
        // 节点的右子节点或者某种遍历方式下的后继节点
        private Node<E> right;
        // 标记left是指向的左子节点还是前驱节点,false表示是左子节点,true表示前驱节点
        private boolean ltag = false;
        // 标记right是指向的右子节点还是后继节点,false表示是右子节点,true表示后继节点
        private boolean rtag = false;

        public Node() {
        }

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

        public E getE() {
            return e;
        }

        public void setE(E e) {
            this.e = e;
        }

        public Node<E> getLeft() {
            return left;
        }

        public void setLeft(Node<E> left) {
            this.left = left;
        }

        public Node<E> getRight() {
            return right;
        }

        public void setRight(Node<E> right) {
            this.right = right;
        }

        public boolean isLtag() {
            return ltag;
        }

        public void setLtag(boolean ltag) {
            this.ltag = ltag;
        }

        public boolean isRtag() {
            return rtag;
        }

        public void setRtag(boolean rtag) {
            this.rtag = rtag;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值