尚硅谷-数据结构与算法-二叉树笔记4

顺序存储二叉树

 

代码实现

package com.atguigu.tree;

import java.security.PublicKey;

/**
 *顺序存储二叉树
 * 从数据存储来看,数组的存储方式包括树存储和数组存储可以相互转换,
 * 即数组可以换成树,树也可以换成数组
 * 顺序存储二叉树的特点
 * 1.顺序存储二叉树通常只考虑完全二叉树!!
 * 2.第n个元素的左子节点为2*n+1;注意:这个n指的是原数组的下标,数组是从0开始的
 * 所以,这个也是从0开始的,这个计算的左子节点也是对应元素在数组中的下标。特别注意
 * 3.第n个元素的右子节点为2*n+2;
 * 4.第n个元素的父节点为(n-1)/2
 * 5.n:表示二叉树中的第几个元素(按0开始编号),具体可以看图
 *
 * 习题:给你一个数组{1,2,3,4,5,6,7},要求以二叉树前序遍历的方式进行遍历,
 * 前序遍历的结果应该是1,2,4,5,3,6,7
 *
 *
 * @author WZ
 * @create 2021-10-17 18:17
 */
public class ArrBinaryTreeDemo {
    public static void main(String[] args) {
        int [] arr=new int[]{1,2,3,4,5,6,7};
        //创建一个ArrBinaryTree对象
        ArrBinaryTree arrBinaryTree = new ArrBinaryTree(arr);
//        arrBinaryTree.preOrder(0);//按照根节点,应该先传0进去即可,但是这样写比较麻烦,一般使用重载的方法

        //前序操作
        System.out.println("前序遍历的操作");
        arrBinaryTree.preOrder();
        //中序操作
        System.out.println("中序遍历的操作");
        arrBinaryTree.midOrder();
        //后序操作
        System.out.println("后序遍历的操作");
        arrBinaryTree.postOrder();

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

    //需要一个构造器,待会把数组传给我即可
    public ArrBinaryTree(int[] arr) {
        this.arr = arr;
    }


    //重载前序遍历的方法
    public void preOrder(){
        this.preOrder(0);
    }

    //编写一个方法完成顺序存储二叉树前序遍历操作

    /**
     *
     * @param index 这个表示数组的下标,类似刚才分析的n
     */
    public void preOrder(int index){
        //如果数组为空,或者arr.length=0
        if (arr==null|| arr.length==0){//arr=null,表示arr不指向任何对象,length=0表示指针指向一个长度为0的对象,判断null需要在前防止空指针异常
            System.out.println("数组为空,不能按照二叉树的前序进行遍历");
        }
        //进行前序遍历
        //输出当前的这个元素
        System.out.println(arr[index]);
        //先向左递归遍历
        if ((index*2+1)<arr.length){//加上这个判断是为了防止角标越界
            preOrder(2*index+1);
        }
        //然后向右递归
        if ((index*2+2)<arr.length){
            preOrder(2*index+2);
        }


    }



    //中序方法的重载操作
    public void midOrder(){
        midOrder(0);
    }
    //完成顺序存储二叉树的中序操作
    public void midOrder(int index){
        //先判断数组是否为空或者长度为0
        if (arr==null || arr.length==0){
            System.out.println("数组为空,不能进行二叉树中序遍历操作");
        }
        //先向左递归
        if ((2*index+1)<arr.length){
            midOrder(2*index+1);
        }
        //中间
        System.out.println(arr[index]);
        //向右递归
        if ((2*index+2)<arr.length){
            midOrder(2*index+2);
        }
    }

    //后序方法的重载操作
    public void postOrder(){
        postOrder(0);
    }
    //完成顺序存储二叉树的后序操作
    public void postOrder(int index){
        //先判断数组是否为空或者长度为0
        if (arr==null || arr.length==0){
            System.out.println("数组为空,不能进行二叉树后序遍历操作");
        }
        //先向左递归
        if ((2*index+1)<arr.length){
            postOrder(2*index+1);
        }
        //向右递归
        if ((2*index+2)<arr.length){
            postOrder(2*index+2);
        }
        System.out.println(arr[index]);
    }
}

线索化二叉树

提出问题

 主要是在8,10,14,这三个有左右指针没有利用上,还有6的右指针没有利用,线索二叉树主要是用来解决这个问题.

        对于1)的理解n个节点的二叉链表中含有n+1个空指针域,比如上面总共有,1,2,6,8,10,14这六个节点,对应的空指针域是n+1也就是7个. 

按照某种遍历的方式,如上面这个中序,排序是8,2,10,1,6,14,其中2的前驱节点是8,后驱节点是10

 

这个和遍历线索化二叉树放在了一起,方便一起复习

package com.atguigu.tree.threadebinarytree;

import jdk.dynalink.NamedOperation;

/**线索二叉树
 *
 *     1
 *  3      6
 *8  10    14
 * 中序遍历的结果是8,3,10,1,14,6
 * @author WZ
 * @create 2021-10-18 22:15
 */
public class ThreadeBinaryTreeDemo {
    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);

        //测试中序线索化
        ThreadBinaryTree threadBinaryTree = new ThreadBinaryTree();
        threadBinaryTree.setRoot(root);
        threadBinaryTree.threadNodes();

        //测试,找10这个节点,看其前驱或者后驱节点是否变成了3和1
        HeroNode leftNode = node5.getLeft();
        System.out.println("十号节点的前驱节点是"+leftNode);
        System.out.println("十号节点的后继节点是"+node5.getRight());

        //当线索化二叉树后,不能在使用原来的遍历方法了
//        threadBinaryTree.infixOrder();
        System.out.println("使用线索化的方式遍历线索化二叉树");
        threadBinaryTree.threadList();//8.3.10.1.14.6


    }
}


//定义ThreadBinaryTree 实现了线索化功能的二叉树
class ThreadBinaryTree{
    //只需要一个根节点即可
    private HeroNode root;
    //为了实现线索化,需要创建要给指向当前节点的前驱节点的指针
    //在递归进行线索化时,pre总是保留前一个节点
    private HeroNode pre=null;
    /*这个主要是在进行线索化某个点的时候,比如这里的指向10节点,将要线索的节点
    ,此时,我们需要一个前驱节点,而10的前驱节点在中序遍历的结果是3,就是让一个
    节点去线索化,得有一个指针(属性),它始终是和当前节点构成一个前驱的关系
    也就是如果有一个节点node指向10,那么还得有一个节点pre指向其前驱节点3
    因为只有这样才有可能让node指向前驱节点,从而实现线索化。

     */

    public void setRoot(HeroNode root) {
        this.root = root;
    }
    //重载threadNodes
    public void threadNodes(){
        this.threadeNodes(root);
    }

 /*
    调用此方法,先进去,node不为空,直接进入下一个循环,然后判断左子节点的数值类型,只要为0就一直往下走,直到找到8这个节点,退出循环
    然后打印出8这个节点,接着判断下一个while循环,此时8节点右边类型为1,那么先得到8右边指针指向的节点就是3,然后输出3,此时3的右边节点
    类型是0,那么直接退出循环,替换node节点,也就是此时node节点为3右边的10,
    然后继续从头开始,10左边节点不为0,所以第一个while循环进不去,直接输出10,节点类型,然后进入下一个循环,因为10右节点类型为1,然后得到
    右节点,1,输出1.....依此类推
     */

    //遍历线索化二叉树的方法
    public void threadList(){
        //定义一个变量,存储当前遍历的节点,root开始
        HeroNode node =root;
        while (node!=null){
            //先循环的找到leftType==1的节点,第一个找到的应该是8这个节点
            //后边随着遍历而变化,因为当left==1时,说明该节点是按照线索化
            //处理后的有效节点
            while (node.getLefType()==0){
                node=node.getLeft();//就一直找,直到找到node.getLeftType==1,的时候停下
            }
            //找到之后,打印当前节点
            System.out.println(node);
            //如果当前节点的右指针指向的是后继节点,就一直输出
            while (node.getRightType()==1){
                //获取到当前节点的后继节点
                node=node.getRight();
                System.out.println(node);
            }
            //while循环结束,说明就找到不等于1的,替换遍历的节点
            node=node.getRight();
        }
    }
    //编写对二叉树进行中序线索化的方法

    /**
     *
     * @param node 这个就是当前需要线索化的节点
     */
    public void threadeNodes(HeroNode node){
        //如果node==null,就不能进行线索化
        if (node==null){
            return;
        }
        //中序线索化的步骤
        //一、先线索化左子树
        threadeNodes(node.getLeft());
        //二、线索化当前节点

        //处理当前节点的前驱节点
        //以8节点来理解,8节点.left=null,8节点的.leftType=1
        if (node.getLeft()==null){
            //让当前节点的左指针指向前驱节点
            node.setLeft(pre);
            //修改当前节点的左指针类型,指向前驱节点
            node.setLefType(1);
        }

        //处理后继节点;后继节点处理需要返回递归上一层,8是上层的左节点。。
        //此时node指向3,而pre指向8
        if (pre!=null && pre.getRight()==null){
            //让前驱节点的右指针指向当前节点
            pre.setRight(node);
            //修改前驱节点的右指针类型
            pre.setRightType(1);
        }
        //!!这句话尤其重要,每处理一个节点后,让当前节点是下一个节点的前驱节点
        pre=node;

        //三hou索化右子树
        threadeNodes(node.getRight());

    }

    //前序遍历;真正的调用是从根节点来调的,就想前面所讲的哈希表
    public void preOrder(){
        if (this.root!=null){
            this.root.preOrder();//谁调用指向谁preOrder()指向root
        }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 void delNode(int no){
        //首先判断root是否为空
        if (root!=null){
            //如果只有一个root节点,这里需要立即判断root是不是要删除的节点
            if (root.getNo()==no){
                root=null;//恰好是根节点,直接置空即可
            }else{
                //如果root不是,则就递归删除
                root.delNode(no);
            }
        }else {
            System.out.println("空树,不能删除~~");
        }
    }

}


//先创建HeroNode 节点
class HeroNode{
    private int no;//英雄的编号
    private String name;//英雄的名字
    private HeroNode left;//指向左边的索引,默认null
    private HeroNode right;//指向右边的索引,默认null

    //说明
    //1.如果leftType==0,表示指向左子树,如果为1则表示指向前驱节点
    //2.如果rightType==0,表示指向右子树,如果为1表示指向后继节点
    private int lefType;
    private int rightType;

    public void setLefType(int lefType) {
        this.lefType = lefType;
    }

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

    public int getLefType() {
        return lefType;
    }

    public int getRightType() {
        return rightType;
    }

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

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

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

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

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

    public int getNo() {
        return no;
    }

    public String getName() {
        return name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public HeroNode getRight() {
        return right;
    }

    //需要输出当前节点的内容,所以需要重写toString方法


    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
    //编写,前序遍历的方法
    public void preOrder(){
        //先输出当前节点,不用判断是否为空,因为都进来了肯定不为空
        System.out.println(this);//先输出父节点,不能写root,后面无法递归,第一次this=root
        //递归向左子树前序遍历
        if (this.left!=null){
            this.left.preOrder();
        }
        //向左递归结束后,返回到root,递归向右子树进行遍历
        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);
    }

    //前序遍历查找的方法

    /**
     *
     * @param no 英雄的编号(id)
     * @return 找到返回该英雄node,没找到返回null
     */
    public HeroNode preOrderSearch(int no){
        System.out.println("进入前序遍历查找的方式");
        //比较当前节点是不是
        if (this.no==no){
            return this;//
        }
        //如果不是,判断左子节点是否为空,不为空就左递归;
        // 如果左递归找到,节点,则返回
        HeroNode resNode=null;//假定我们找到的节点就是它,因为我们需要判断左递归找到要返回
        if (this.left!=null){
            resNode=this.left.preOrderSearch(no);
        }
        if (resNode!=null){//这个说明左子树我们已经找到了
            return resNode;
        }
        //左递归没有找到的话,需要继续判断
        //判断当前节点的右子节点是否为空,如果不为空,则继续向右递归查找,找到返回,没有返回null
        if (this.right!=null){
            resNode=this.right.preOrderSearch(no);
        }
        return resNode;//不管找没找到,就可以直接返回

    }
    //中序遍历查找
    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("进入zhong序遍历查找");

        //如果没有找到,就和当前节点比较,如果是,则返回当前节点
        if (this.no==no){
            return this;//说明找到,并返回了当前节点
        }
        //如果没有找到,就向右继续递归中序查找
        if (this.right!=null){
            resNode=this.right.infixOrderSearch(no);
        }
        return resNode;//不管是否为空,直接返回即可
    }
    //后序遍历查找
    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("进入后序查找");//查看遍历的次数,得写在比较语句(this.no==no)的前面,不能写在前面

        //如果左右子树都没有找到,就比较当前节点是不是
        if (this.no==no){
            return this;

        }
        return resNode;
    }

    //递归删除节点
    //规定:1.如果是叶子节点则删除该节点,2如果删除的是非叶子节点,则删除该子树
    public void delNode(int no){
        //先判断左子节点是否为空,再判断是否是要删除的节点
        if (this.left!=null && this.left.no==no){
            this.left=null;
            return;
        }
        //如果左子节点不是,判断右子节点是否为空或者是否是要删除的节点
        if (this.right!=null && this.right.no==no){
            this.right=null;
            return;
        }
        //如果前两个都没有,则需要先向左递归删除
        if (this.left!=null){
            this.left.delNode( no);
        }
        //左递归没有结束的话,就进行右递归进行删除
        if (this.right!=null){
            this.right.delNode(no);
        }
    }


}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值