数据结构(七)---二叉树

树的基本介绍

树分成两部分:基础部分+应用部分

(一)基础部分
1)数组存储方式的分析
有点:可以通过下标访问元素,速度快,对于有序数组,还可以使用二分查找提高检索速度
缺点:如果要检索某个具体的值,或者插入值,会导致整体移动,效率很低

2)链式存储方式的分析
优点:插入删除节点时,不会导致整体移动,效率比数组要好
缺点:再检索时,不如有索引的数组块,需要从头开始遍历

3)树存储方式的分析
优点:能提高数据的存储、读取的效率,比如利用二叉排序树,既可以保证数据的检索速度,同时保证数据的插入、删除、修改的速度

如果用“二叉排序树”来存储数据,那么对数据的增删改查的效率都可以提高

(二)二叉树遍历
1)创建一颗二叉树
2)前序遍历
2.1)先输出当前节点(初始的时候是root节点)
2.2)如果左子节点不为空,则递归继续前序遍历
2.3)如果右子节点不为空,则递归继续前序遍历

3)中序遍历
3.1)如果左子节点不为空,则递归继续前序遍历
3.2)输出当前节点
3.3)如果右子节点不为空,则递归继续前序遍历

4)后序遍历
4.1)如果左子节点不为空,则递归继续前序遍历
4.2)如果右子节点不为空,则递归继续前序遍历
4.3)输出当前节点

(三)使用前序、中序、后序的方式来查询指定的结点
前序查找思路:
1)先判断当前节点的no是否是要查找的
2)如果是相等,则返回当前结点
3)如果不相等,则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
4)如果左递归前序查找,找到节点,则返回,否则继续判断,当前的节点的右子节点是否为空,
如果不空,则继续向右递归前序查找

中序查找思路:
1)先判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
2)如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点,否则继续进行右递归的中序查找
3)如果右递归中序查找,找到就返回,找到就返回,否则返回null

后序查找思路:
1)判断当前结点的左子节点是否为空,如果不为空,则递归后续查找
2)如果找到,就返回,如果没有找到,就判断当前结点的右子节点是否为空,如果不为空,则右递归进行后序查找,如果找到,就返回
3)就和当前结点进行,比如,如果是则返回,否则返回null

(四)删除二叉树的结点
规定:
1)如果删除的结点是叶子结点,则删除该节点
2)如果删除的节点是非叶子界定啊,则删除该子树

思路:(判断的不是当前节点,而是当前节点的左右子节点)
首先处理:考虑如果树是空树root,如果只有一个root节点,则等价把二叉树置空
1)因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否是待删除的节点
而不能去判断当前节点是不是待删除的节点
2)如果当前节点的左子节点不为空,并且左子节点就是待删除的节点,就把this.left=null,把左子树置为空
3)如果当前节点的右子节点不为空,并且右子节点就是待删除的节点,就把this.right=null,把右子树置为空
4)如果2,3两步都没有删除节点,那就需要向左子树进行递归删除
5)如果4步向左子树递归删除也没有删除成功,那就需要向右子树进行递归删除

(五)顺序存储二叉树(堆排序就会用到顺序存储二叉树)
基本说明:
从数据存储来看,数组存储方式和树的存储方式可以相互转换,就是说数组可以转换成树,树叶可以转换成数组

要求:
1)二叉树的节点,要求以数组的方式来存放arr
2)要求在遍历数组arr时,仍然可以以前序遍历,中序遍历和后序遍历的方式完成节点的遍历

需要熟悉二叉树的特点:
1)顺序二叉树通常只考虑完全二叉树
2)第n个元素的左子节点为2n+1
3)第n个元素的右子节点为2
n+2
4)第n个元素的父节点为(n-1)/2
5)n:表示二叉树中的第几个元素(按0开始编号)

(六)线索化二叉树
问题分析:
1)当我们对上面的二叉树进行中序遍历时
2)但是好几个节点的左右指针,并没有完全的利用上
3)如果我们希望充分的利用各个节点的左右指针,让各个节点可以指向自己的前后节点怎么办?
4)解决方法就是使用——线索二叉树

线索二叉树的介绍:
1)n个节点的二叉链表中含有n+1个空指针域。如果在这些空指针域中存放指向节点的前驱和后继节点的指针,就可以充分利用
2)二叉链表加上线索(前驱后继指针)==》线索链表,二叉树加上线索就成了线索二叉树
分成:1-前序线索二叉树,2-中序线索二叉树,3-后序线索二叉树
3)一个节点的前一个节点,叫前驱节点
4)一个节点的后一个节点,叫后继节点

思路分析:
比如说中序线索二叉树,先写出来二叉树的中序遍历{8,3,10,1,14,6}
然后根据中序遍历,在二叉树里找到前后关系,画上线索表示前驱和后继

特点说明:
1)left指向的是左子树,也可能是指向的前驱节点
2)right指向的右子树,也可能是指向后继节点

(七)遍历线索化二叉树
说明:对中序线索化的二叉树进行遍历
分析:线索化后,各个节点指向有变化,因此原来的遍历方式不能再用了
这时候要用新的的方式遍历线索化二叉树,各个节点可以通过线型方式遍历
因此,不需要使用递归方式,也就提高了遍历的效率,遍历的次序应当和中序遍历保持一致

(八)堆排序的基本思想
1)把待排序序列构造成一个大顶堆
2)此时,整个序列的最大值就是堆顶的根节点
3)把其与末尾元素进行交换,此时末尾就为最大值
4)然后把剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,就能得到一个有序序列了

举例:{4,6,8,5,9}
1)首先,有一个无序序列结构,在这个基础上进行排序
2)此时我们从最后一个非叶子节点开始(叶子节点不需要调整),从左至右,从上至下进行调整
第一个非叶子节点怎么得来的呢——arr.length/2-1=5/2-1=1
3)编号为1的非叶子节点,假设为6,把6和6的左右子节点(也就是叶子节点)进行比较,
如果6比左右子节点都大,就不需要调整
如果6比右子节点小,那就应该把6和右子节点互换位置
如果6比左右子节点都小,那就应该和最大的那个进行交换
4)接着找第二个非叶子节点,继续进行交换
交换过后,会出现一种情况,新的节点位置可能会影响下面子树的结构
这时候,继续调整
5)到最后,最大的值会移动到根节点的位置
把根节点和树末尾的值交换位置,然后取出这个最大值

前中后遍历二叉树

创建节点类

//先创建节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    //构造器
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }

    //编写递归删除的方法
    //如果删除的节点是叶子节点,就删除该节点
    //如果删除的节点是非叶子节点,就删除该子树
    public void delNode(int no) {
        // 思路:(判断的不是当前节点,而是当前节点的左右子节点)
        // 首先处理:考虑如果树是空树root,如果只有一个root节点,则等价把二叉树置空
        // 1)因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否是待删除的节点
        //         而不能去判断当前节点是不是待删除的节点
        // 2)如果当前节点的左子节点不为空,并且左子节点就是待删除的节点,就把this.left=null,把左子树置为空
        if (this.left != null && this.left.no == no) {
            this.left = null;
            return;
        }
        // 3)如果当前节点的右子节点不为空,并且右子节点就是待删除的节点,就把this.right=null,把右子树置为空
        if (this.right != null && this.right.no == no) {
            this.right = null;
            return;
        }
        // 4)如果2,3两步都没有删除节点,那就需要向左子树进行递归删除
        if (this.left != null) {
            this.left.delNode(no);
        }
        // 5)如果4步向左子树递归删除也没有删除成功,那就需要向右子树进行递归删除
        if (this.right != null) {
            this.right.delNode(no);
        }
    }

    //编写前序遍历的方法
    public void preOrder() {
        //先输出当前节点
        System.out.println(this);
        //再输出左子节点(递归)——递归向左子树前序遍历
        if (this.left != null) {
            this.left.preOrder();
        }
        //最后输出右子节点(递归)——递归向右子树前序遍历
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    //编写中序遍历的方法
    public void infixOrder() {
        //输出左子节点(递归)——递归向左子树中序遍历
        if (this.left != null) {
            this.left.infixOrder();
        }
        //输出父节点
        System.out.println(this);
        //输出右子节点(递归)——递归向右子树中序遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    //编写后序遍历的方法
    public void postOrder() {
        //输出左子节点(递归)——递归向左子树后序遍历
        if (this.left != null) {
            this.left.postOrder();
        }
        //输出右子节点(递归)——递归向右子树后序遍历
        if (this.right != null) {
            this.right.postOrder();
        }
        //输出父节点
        System.out.println(this);
    }

    //

    /**
     * 前序遍历查找
     * 如果找到就返回该Node,如果没有找到返回null
     *
     * @MethodName: preOrderSearch
     * @Author: AllenSun
     * @Date: 2019/11/4 23:24
     */
    public HeroNode preOrderSearch(int no) {
        System.out.println("进入前序遍历");//这句话用来统计一共进行了几次前序遍历

        //比较当前结点是不是要找的节点
        if (this.no == no) {
            return this;
        }
        //1-则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
        //2-如果左递归前序查找,找到节点,则返回
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }
        if (resNode != null) {
            //说明我们左子树找到,得到节点,则返回,否则继续判断
            return resNode;
        }
        //如果左递归没有找到,就判断判断当前结点的右子节点是否为空,如果不为空,则继续向右递归前序查找
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

    /**
     * 中序遍历查找
     *
     * @MethodName:
     * @Author: AllenSun
     * @Date: 2019/11/4 23:41
     */
    public HeroNode infixOrderSearch(int no) {
        //判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入中序遍历");//这句话用来统计一共进行了几次中序遍历
        //如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点
        if (this.no == no) {
            return this;
        }
        //否则继续进行右递归的中序查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }

    /**
     * 后序遍历
     *
     * @MethodName:
     * @Author: AllenSun
     * @Date: 2019/11/4 23:59
     */
    public HeroNode postOrderSearch(int no) {
        //判断当前结点左子节点是否为空,如果不为空,则递归后序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null) {//说明在左子树找到
            return resNode;
        }
        //如果左子树没有找到,那就到右子树去找递归查找
        if (this.right != null) {
            resNode = this.right.postOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入后序遍历");//这句话用来统计一共进行了几次后序遍历
        //如果左右子树都没有找到,那就比较当前的节点
        if (this.no == no) {
            return this;
        }
        return resNode;
    }

}

创建一个树类

//创建一个树
class BinaryTree {
    private HeroNode root;//根节点

    public void setRoot(HeroNode root) {
        this.root = root;
    }



    //删除节点
    public void delNode(int no){
        if(root!=null){
            //如果只有根节点,这里理解判断root是不是就是要删除节点
            if(root.getNo()==no){
                root=null;
            } else {
                //递归删除
                root.delNode(no);
            }
        } else {
            System.out.println("空树,不能删除");
        }
    }

    //前序遍历
    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            return root.preOrderSearch(no);
        } else {
            return null;
        }
    }

    //中序遍历查找
    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            return root.infixOrderSearch(no);
        } else {
            return null;
        }
    }

    //后序遍历查找
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return root.postOrderSearch(no);
        } else {
            return null;
        }
    }

}

测试类

public class BinaryTreeDemo {
    public static void main(String[] args) {
        //先需要创建一颗二叉树
        BinaryTree binaryTree = new BinaryTree();
        //创需要的节点
        HeroNode root = new HeroNode(1, "宋江");
        HeroNode node2 = new HeroNode(2, "吴用");
        HeroNode node3 = new HeroNode(3, "卢俊义");
        HeroNode node4 = new HeroNode(4, "林冲");
        HeroNode node5 = new HeroNode(5, "关胜");

        //说明,我们先手动创建二叉树,后面我们学习递归的方式创建二叉树
        binaryTree.setRoot(root);//设置根节点
        root.setLeft(node2);//设置左子树
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);

        //测试一下前中后遍历
        System.out.println("前序遍历:");
        binaryTree.preOrder();
        System.out.println("中序遍历:");
        binaryTree.infixOrder();
        System.out.println("后序遍历:");
        binaryTree.postOrder();

        //前序遍历查找
        //前序遍历的次数:4次
        System.out.println("前序遍历方式");
        HeroNode resNode01 = binaryTree.preOrderSearch(5);
        if (resNode01 != null) {
            System.out.printf("找到了,信息为no=%d  name=%s\n", resNode01.getNo(), resNode01.getName());
        } else {
            System.out.printf("没有找到信息为no=%d  name=%s 的英雄\n", resNode01.getNo(), resNode01.getName());
        }

        //中序遍历查找
        //中序遍历的次数:3次
        System.out.println("中序遍历方式");
        HeroNode resNode02 = binaryTree.infixOrderSearch(5);
        if (resNode02 != null) {
            System.out.printf("找到了,信息为no=%d  name=%s\n", resNode02.getNo(), resNode02.getName());
        } else {
            System.out.printf("没有找到信息为no=%d  name=%s 的英雄\n", resNode02.getNo(), resNode02.getName());
        }

        //后序遍历查找
        //后序遍历的次数:2次
        System.out.println("后序遍历方式");
        HeroNode resNode03 = binaryTree.postOrderSearch(5);
        if (resNode03 != null) {
            System.out.printf("找到了,信息为no=%d  name=%s\n", resNode03.getNo(), resNode03.getName());
        } else {
            System.out.printf("没有找到信息为no=%d  name=%s 的英雄\n", resNode03.getNo(), resNode03.getName());
        }

        //测试一把删除节点
        System.out.println("删除前,前序遍历");
        binaryTree.preOrder();
        binaryTree.delNode(5);
        System.out.println("删除后,前序遍历");
        binaryTree.preOrder();

    }
}

顺序存储二叉树

编写一个ArrayBinaryTree,实现顺序存储二叉树遍历

//编写一个ArrayBinaryTree,实现顺序存储二叉树遍历
class ArrBinaryTree {
    private int arr[];//存储数据节点的数组

    public ArrBinaryTree(int arr[]) {
        this.arr = arr;
    }

    //重载preOrder
    public void preOrder(){
        this.preOrder(0);
    }

    //编写一个方法,完成顺序存储二叉树的前序遍历
    public void preOrder(int index) {
        //如果数组为空,或者arr.length=0
        if (arr == null || arr.length == 0) {
            System.out.println("数组为空,不能按照二叉树的前序遍历");
        }
        System.out.print(arr[index]+"\t");//输出当前的元素

        //向左递归遍历
        if ((index * 2 + 1) < arr.length) {
            preOrder(2 * index + 1);
        }
        //向右递归遍历
        if ((index * 2 + 2) < arr.length) {
            preOrder(2 * index + 2);
        }
    }

}

测试类

public class ArrBinaryTreeDemo {
    public static void main(String[] args) {
        int arr[] = {1, 2, 3, 4, 5, 6, 7};
        //创建一个ArrBinaryTree
        ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
        arrBinaryTree.preOrder(0);//1,2,4,5,3,6,7
    }
}

线索二叉树

创建节点类

//创建节点HeroNode
class HeroNode {
    private int no;
    private String name;
    private HeroNode left;
    private HeroNode right;

    //线索二叉树说明
    //1-如果leftType==0 表示指向的是左子树,如果1则表示指向前驱节点
    //2-如果rightType==0 表示指向的是右子树,如果1则表示指向后继节点
    private int leftType;
    private int rightType;

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    //构造器
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }

    //编写递归删除的方法
    //如果删除的节点是叶子节点,就删除该节点
    //如果删除的节点是非叶子节点,就删除该子树
    public void delNode(int no) {
        // 思路:(判断的不是当前节点,而是当前节点的左右子节点)
        // 首先处理:考虑如果树是空树root,如果只有一个root节点,则等价把二叉树置空
        // 1)因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否是待删除的节点
        //         而不能去判断当前节点是不是待删除的节点
        // 2)如果当前节点的左子节点不为空,并且左子节点就是待删除的节点,就把this.left=null,把左子树置为空
        if (this.left != null && this.left.no == no) {
            this.left = null;
            return;
        }
        // 3)如果当前节点的右子节点不为空,并且右子节点就是待删除的节点,就把this.right=null,把右子树置为空
        if (this.right != null && this.right.no == no) {
            this.right = null;
            return;
        }
        // 4)如果2,3两步都没有删除节点,那就需要向左子树进行递归删除
        if (this.left != null) {
            this.left.delNode(no);
        }
        // 5)如果4步向左子树递归删除也没有删除成功,那就需要向右子树进行递归删除
        if (this.right != null) {
            this.right.delNode(no);
        }
    }

    //编写前序遍历的方法
    public void preOrder() {
        //先输出当前节点
        System.out.println(this);
        //再输出左子节点(递归)——递归向左子树前序遍历
        if (this.left != null) {
            this.left.preOrder();
        }
        //最后输出右子节点(递归)——递归向右子树前序遍历
        if (this.right != null) {
            this.right.preOrder();
        }
    }

    //编写中序遍历的方法
    public void infixOrder() {
        //输出左子节点(递归)——递归向左子树中序遍历
        if (this.left != null) {
            this.left.infixOrder();
        }
        //输出父节点
        System.out.println(this);
        //输出右子节点(递归)——递归向右子树中序遍历
        if (this.right != null) {
            this.right.infixOrder();
        }
    }

    //编写后序遍历的方法
    public void postOrder() {
        //输出左子节点(递归)——递归向左子树后序遍历
        if (this.left != null) {
            this.left.postOrder();
        }
        //输出右子节点(递归)——递归向右子树后序遍历
        if (this.right != null) {
            this.right.postOrder();
        }
        //输出父节点
        System.out.println(this);
    }

    //

    /**
     * 前序遍历查找
     * 如果找到就返回该Node,如果没有找到返回null
     *
     * @MethodName: preOrderSearch
     * @Author: AllenSun
     * @Date: 2019/11/4 23:24
     */
    public HeroNode preOrderSearch(int no) {
        System.out.println("进入前序遍历");//这句话用来统计一共进行了几次前序遍历

        //比较当前结点是不是要找的节点
        if (this.no == no) {
            return this;
        }
        //1-则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找
        //2-如果左递归前序查找,找到节点,则返回
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.preOrderSearch(no);
        }
        if (resNode != null) {
            //说明我们左子树找到,得到节点,则返回,否则继续判断
            return resNode;
        }
        //如果左递归没有找到,就判断判断当前结点的右子节点是否为空,如果不为空,则继续向右递归前序查找
        if (this.right != null) {
            resNode = this.right.preOrderSearch(no);
        }
        return resNode;
    }

    /**
     * 中序遍历查找
     *
     * @MethodName:
     * @Author: AllenSun
     * @Date: 2019/11/4 23:41
     */
    public HeroNode infixOrderSearch(int no) {
        //判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入中序遍历");//这句话用来统计一共进行了几次中序遍历
        //如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点
        if (this.no == no) {
            return this;
        }
        //否则继续进行右递归的中序查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }

    /**
     * 后序遍历
     *
     * @MethodName:
     * @Author: AllenSun
     * @Date: 2019/11/4 23:59
     */
    public HeroNode postOrderSearch(int no) {
        //判断当前结点左子节点是否为空,如果不为空,则递归后序查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.postOrderSearch(no);
        }
        if (resNode != null) {//说明在左子树找到
            return resNode;
        }
        //如果左子树没有找到,那就到右子树去找递归查找
        if (this.right != null) {
            resNode = this.right.postOrderSearch(no);
        }
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入后序遍历");//这句话用来统计一共进行了几次后序遍历
        //如果左右子树都没有找到,那就比较当前的节点
        if (this.no == no) {
            return this;
        }
        return resNode;
    }

}

创建一个线索二叉树,实现了线索化功能的二叉树

//创建一个线索二叉树,实现了线索化功能的二叉树
class ThreadedBinaryTree {
    private HeroNode root;//根节点

    public void setRoot(HeroNode root) {
        this.root = root;
    }

    //为了实现线索化,需要创建要给指向当前节点的前驱节点的指针
    //在递归进行线索化时,pre 总是保留前一个节点
    private HeroNode pre = null;

    //重载一把threadedNodes方法
    public void threadedNodes() {
        this.threadedNodes(root);
    }

    //遍历线索化二叉树的方法
    public void threadedList() {
        //定义一个变量,存储当前遍历的节点,从root开始
        HeroNode node = root;
        while (node != null) {
            //循环的找到leftType==1的节点,第一个找到就是8节点
            //后面随着遍历而变化,因为当leftType==1时,说明该节点是按照线索化处理后的有效节点
            while (node.getLeftType() == 0) {
                node = node.getLeft();
            }
            //打印当前这个节点
            System.out.println(node);
            //如果当前节点的右指针指向的是后继节点,就一直输出
            while (node.getRightType() == 1) {
                //获取到当前节点和后继节点
                node = node.getRight();
                System.out.println(node);
            }
            //替换这个遍历的节点
            node=node.getRight();

        }
    }

    //编写对二叉树进行中序线索化的方法
    public void threadedNodes(HeroNode node) {
        //如果node==null,不能线索化
        if (node == null) {
            return;
        }
        //(一)先线索化左子树
        threadedNodes(node.getLeft());
        //(二)线索化当前节点(有难度)
        //处理当前节点的前去节点
        if (node.getLeft() == null) {
            //就让当前节点的左指针指向前驱节点
            node.setLeft(pre);
            //修改当前节点的左指针的类型,指向前驱节点
            node.setLeftType(1);
        }
        //处理后继节点
        if (pre != null && pre.getRight() == null) {
            //让前驱节点的右指针指向当前节点
            pre.setRight(node);
            //修改前驱节点的右指针类型
            pre.setRightType(1);
        }

        //!!!每处理一个节点后,让当前节点是下一个节点的前驱节点
        pre = node;

        //(三)再线索化右子树
        threadedNodes(node.getRight());

    }

    //删除节点
    public void delNode(int no) {
        if (root != null) {
            //如果只有根节点,这里理解判断root是不是就是要删除节点
            if (root.getNo() == no) {
                root = null;
            } else {
                //递归删除
                root.delNode(no);
            }
        } else {
            System.out.println("空树,不能删除");
        }
    }

    //前序遍历
    public void preOrder() {
        if (this.root != null) {
            this.root.preOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //中序遍历
    public void infixOrder() {
        if (this.root != null) {
            this.root.infixOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //后序遍历
    public void postOrder() {
        if (this.root != null) {
            this.root.postOrder();
        } else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

    //前序遍历查找
    public HeroNode preOrderSearch(int no) {
        if (root != null) {
            return root.preOrderSearch(no);
        } else {
            return null;
        }
    }

    //中序遍历查找
    public HeroNode infixOrderSearch(int no) {
        if (root != null) {
            return root.infixOrderSearch(no);
        } else {
            return null;
        }
    }

    //后序遍历查找
    public HeroNode postOrderSearch(int no) {
        if (root != null) {
            return root.postOrderSearch(no);
        } else {
            return null;
        }
    }
}

测试类

public class ThreadedBinaryTreeDemo {
    public static void main(String[] args) {
        //测试一下中序线索二叉树的功能
        HeroNode root = new HeroNode(1, "tom");
        HeroNode node2 = new HeroNode(3, "jack");
        HeroNode node3 = new HeroNode(6, "smith");
        HeroNode node4 = new HeroNode(8, "marry");
        HeroNode node5 = new HeroNode(10, "king");
        HeroNode node6 = new HeroNode(14, "dim");

        //手动创建二叉树
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        //测试线索化
        ThreadedBinaryTree threadedBinaryTree = new ThreadedBinaryTree();
        threadedBinaryTree.setRoot(root);
        threadedBinaryTree.threadedNodes();

        //测试:以10号节点测试
        HeroNode leftNode = node5.getLeft();
        HeroNode rightNode = node5.getRight();
        System.out.println("10号节点的前驱节点是:" + leftNode);
        System.out.println("10号节点的后继节点是:" + rightNode);

        //当线索化二叉树后,能在使用原来的遍历方法
        System.out.println("使用线索化的方式遍历线索化二叉树");
        threadedBinaryTree.threadedList();

    }
}
发布了41 篇原创文章 · 获赞 5 · 访问量 647

猜你喜欢

转载自blog.csdn.net/weixin_44823875/article/details/104934871