程序员

X8-1、java数据结构---二叉树【2020-12-31】

2020-11-22  本文已影响0人  鄙人_阿K

总目录:地址如下看总纲

https://www.jianshu.com/p/929ca9e209e8

1、树的常用术语

image.png

1、节点:就是节点,没什么鸡毛好讲
2、根节点:就是最上层,祖宗
3、父节点:
4、子节点:
5、叶子节点:没有子节点的节点
6、节点的权:就是节点值(大多时候是个对象)
7、路径:从root节点找到该节点的路线
8、层
9、子树
10、树的高度:最大层数
11、森林 :多颗子树构成森林

2、二叉树的概念

  1. 树有很多种,每个节点最多只能有两个子节点的一种形式称为二叉树。
  2. 二叉树的子节点分为左节点和右节点。
  3. 以下三种均为二叉树:


    image.png
  4. 如果该二叉树的所有叶子节点都在最后一层,并且结点总数= 2^n -1 , n 为层数,则我们称为满二叉树


    image.png
  5. 如果该二叉树的所有叶子节点都在最后一层或者倒数第二层,而且最后一层的叶子节点在左边连续,倒数第二层的叶子节点在右边连续,我们称为完全二叉树。


    image.png

3、二叉树的遍历说明

  1. 前序遍历: 先输出父节点,再遍历左子树和右子树
  2. 中序遍历: 先遍历左子树,再输出父节点,再遍历右子树
  3. 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点
  • 小结: 看输出父节点的顺序,就确定是前序,中序还是后序

4、前,中,后序遍历详解

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


    image.png

5、前,中,后序代码实现

package com.kk.datastructure.tree;

/**
 * title: 二叉树的前,中,后序遍历
 * @author 阿K
 * 2020年12月29日 下午11:29:56 
 */
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, "关胜");

        // 加入树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);
        
        System.out.println("前序遍历,预计结果:1,2,3,5,4");
        binaryTree.perOrder();

        System.out.println("中序遍历,预计结果:2,1,5,3,4");
        binaryTree.infixOrder();

        System.out.println("后序遍历,预计结果:2,5,4,3,1");
        binaryTree.postOrder();
    }
}

// 定义二叉树 
class BinaryTree {

    private HeroNode root;// 根节点

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

    // 前序遍历
    public void perOrder() {
        if (this.root != null) {
            this.root.perOrder();
        } 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("二叉树为空,无法遍历~");
        }
    }
}

// 定义节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left; // 左节点(默认为null)
    private HeroNode right; // 右节点(默认为null)

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

    // 节点的前序遍历
    public void perOrder() {
        // 1.先输出入父节点
        System.out.println(this);
        // 2.递归左子树前序遍历
        if (this.left != null) {
            this.left.perOrder();
        }
        // 3.递归右子树前序遍历
        if (this.right != null) {
            this.right.perOrder();
        }
    }

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

    // 节点的后序遍历
    public void postOrder() {
        // 1.递归左子树后序遍历
        if (this.right != null) {
            this.left.postOrder();
        }
        // 2.递归右子树后序遍历
        if (this.right != null) {
            this.right.postOrder();
        }
        // 3.输出父节点
        System.out.println(this);
    }
    
    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 + "]";
    }
}

6、前,中,后序查找思路

  1. 前序查找思路:
    1、先判断当前结点的 no 是否等于要查找的
    2、如果是相等,则返回当前结点
    3、如果不等,则判断当前结点的左子节点是否为空,如果不为空,则递归前序查找4、如果左递归前序查找,找到结点,则返回,否继续判断,当前的结点的右子节点是否为空,如果不空,则继续向右递归前序查找.
  2. 中序查找思路:
    1、判断当前结点的左子节点是否为空,如果不为空,则递归中序查找
    2、如果找到,则返回,如果没有找到,就和当前结点比较,如果是则返回当前结点,否则继续进行右递归的中序查找
    3、如果右递归中序查找,找到就返回,否则返回 null
  3. 后序查找思路
    1、判断当前结点的左子节点是否为空,如果不为空,则递归后序查找
    2、如果找到,就返回,如果没有找到,就判断当前结点的右子节点是否为空,如果不为空,则右递归进行后序查找,如果找到,就返回
    3、最后和当前结点进行比较,是则返回,否则返回 null


    image.png

7、前,中,后序查找代码

package com.kk.datastructure.tree;


/**
 * title: 前中后序查找
 * @author 阿K
 * 2020年12月30日 下午11:49:44 
 */
public class BinaryTreeDemo2 {

    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, "关胜");

        // 加入树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);

        System.out.println("前序遍历,预计统计次数:4 ===> 查找顺序 1,2,3,5,4");
        binaryTree.perOrderSearch(5);

        System.out.println("中序遍历,预计统计次数:3 ===》 查找顺序 2,1,5,3,4");
        binaryTree.infixOrderSearch(5);
//
        System.out.println("后序遍历,预计统计次数:2 ===》 查找顺序 2,5, 4,3,1");
        binaryTree.postOrderSearch(5);
    }
}

// 定义二叉树 
class BinaryTree {

    private HeroNode root;// 根节点

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

    // 前序遍历查找
    public HeroNode perOrderSearch(int no) {
        if (root != null) {
            return root.perOrderSearch(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;
        }
    }
}

// 定义节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left; // 左节点(默认为null)
    private HeroNode right; // 右节点(默认为null)

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

    /**
     * 须知:关于查找次数的统计
     * 为了有效的统计出正确的次数,应该放在 if(this.no == no) 上面
     * @param no
     * @return
     */

    // 节点的前序遍历查找
    public HeroNode perOrderSearch(int no) {
        System.out.println("记录前序遍历查找,打印次数决定查找运行次数!");
        // 比较当前节点是否
        if (this.no == no) {
            return this;
        }
        // 1、判断当前节点的左子节点是否为空,若不为空,则左递归前序查找
        // 2、若左递归前序查找,找到节点则返回
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.perOrderSearch(no);
        }
        if (resNode != null) {// 说明在左子树上找到了
            return resNode;
        }
        // 1、当前节点的右子树是否为空,若不为空,则右递归前序查找
        // 2、若右递归前序查找,找到节点则返回
        if (this.right != null) {
            resNode = this.right.perOrderSearch(no);
        }
        return resNode;
    }

    // 节点的中序遍历查找
    public HeroNode infixOrderSearch(int no) {
        // 1、判断当前左节点是否为空,若不为空,则继续左递归中序遍历查找
        HeroNode resNode = null;
        if (this.left != null) {
            resNode = this.left.infixOrderSearch(no);
        }
        // 2、若不为空,则返回(既找到)
        if (resNode != null) {
            return resNode;
        }
        System.out.println("进入中序查找的次数统计");
        // 3、和当前节点进行比较,若是则返回
        if (this.no == no) {
            return this;
        }
        // 4、若不是,则继续右递归中序遍历查找
        if (this.right != null) {
            resNode = this.right.infixOrderSearch(no);
        }
        return resNode;
    }

    // 节点的后序遍历查找
    public HeroNode postOrderSearch(int no) {
        // 1、判断当前左节点是否为空,若不为空,则继续左递归后序遍历查找
        HeroNode resNode =null;
        if(this.left!=null) {
            resNode = this.left.postOrderSearch(no);
        }
        // 2、若不为空,则返回
        if(resNode !=null) {
            return resNode;
        }
        // 3、若当前左子树没有找到,则开始右子树后序递归遍历查找
        if(this.right!=null) {
            resNode = this.right.postOrderSearch(no);
        }
        // 4、若不为空,则返回
        if(resNode!=null) {
            return resNode;
        }
        System.out.println("进入后序号查找次数统计");
        // 5、若左右子树都没有找到,就比较当前节点是否
        if(this.no==no) {
            return this;
        }
        return resNode;
    }

    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 + "]";
    }
}

8、删除树节点思路分析(入门版)

  1. 规定
    1、若删除的节点是叶子节点,则删除该节点
    2、若删除的节点是非叶子节点,则删除该子树
  2. 思路:
    1、判空,若只有一个 root 根节点存在,则等价于二叉树置空
    2、故当前入门级二叉树是单向的,所以我们是只判断当前节点的子节点是否为需要删除的
    3、若当前节点的左子节点不为空,并且左子节点就是要删除的节点,就将 this.left =null(既删除),并返回(return 结束递归)
    4、若当前节点的右子节点不为空,并且右子节点就是要删除的节点,就将 this.rigth=null (既删除),并返回(return 结束递归)
    5、若 以上两步都没有删除节点,那么就进行左子树递归删除
    6、如若以上三步都没有删除,则进行右子树递归删除

9、删除树节点代码(入门版)

package com.kk.datastructure.tree;


/**
 * title: 删除节点
 * @author 阿K
 * 2020年12月31日 下午11:57:45 
 */
public class BinaryTreeDemo3 {

    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, "关胜");

        // 加入树
        root.setLeft(node2);
        root.setRight(node3);
        node3.setRight(node4);
        node3.setLeft(node5);
        binaryTree.setRoot(root);
        
        // 删除测试。。
        System.out.println("删除前:");
        binaryTree.perOrder();
        binaryTree.delNode(3);
        System.out.println("删除后:");
        binaryTree.perOrder();

    }
}

// 定义二叉树 
class BinaryTree {

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

    private HeroNode root;// 根节点

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

    // 删除节点
    public void delNode(int no) {
        if (this.root != null) {
            // 1、判空,若只有一个 root 根节点存在,则等价于二叉树置空
            if (root.getNo() == no) {
                root = null;
            } else {
                // 否则 递归删除
                root.delNode(no);
            }

        } else {
            System.out.println("当前二叉树为空,删个鸡毛??");
        }
    }

}

// 定义节点
class HeroNode {
    private int no;
    private String name;
    private HeroNode left; // 左节点(默认为null)
    private HeroNode right; // 右节点(默认为null)

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

    // 节点删除
    // 1、若删除的节点是叶子节点,则删除该节点
    // 2、若删除的节点是非叶子节点,则删除该子树
    public void delNode(int no) {

//      2、故当前入门级二叉树是单向的,所以我们是只判断当前节点的子节点是否为需要删除的
//      3、若当前节点的左子节点不为空,并且左子节点就是要删除的节点,就将 this.left =null(既删除),并返回(return 结束递归)
//      4、若当前节点的右子节点不为空,并且右子节点就是要删除的节点,就将 this.rigth=null (既删除),并返回(return 结束递归)
//      5、若 以上两步都没有删除节点,那么就进行左子树递归删除
//      6、如若以上三步都没有删除,则进行右子树递归删除
        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);
        }

    }

    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;
    }

    // 节点的前序遍历
    public void perOrder() {
        // 1.先输出入父节点
        System.out.println(this);
        // 2.递归左子树前序遍历
        if (this.left != null) {
            this.left.perOrder();
        }
        // 3.递归右子树前序遍历
        if (this.right != null) {
            this.right.perOrder();
        }
    }

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

上一篇下一篇

猜你喜欢

热点阅读