🧡数据结构与算法🧡从零到有系列《十一》二叉树

195 阅读7分钟

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

为什么需要树中结构呢?

数组存储方式的解析:

1、 优点:通过下标方式访问元素,速度快,对于有序数组还可以使用二分查找提高检索速度。

2、 缺点:如果要检索具体某个值,或者插入值会整理移动,效率较低

image.png

链式存储方式分析:

  1. 优点:在一定程序上对数组存储方式有优化,比如插入一个数值时,只需要讲插入点接到链表中即可,删除效率也是同理效果好。
  2. 缺点:在进行检索时,效率仍然很低,检索某一个值时,需要从链表头一直做检索。

image.png

树存储方式分析:

能提高数据存储,读取的效率,比如可以使用二叉树既可以保证数据检索速度,同时也可以保证数据的插入,删除,修改的速度。

image.png

二叉树

1.二叉树概念

二叉树(Binary tree)是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。二叉树特点是每个结点最多只能有两棵子树,且有左右之分  

2. 树示意图

image.png

常用术语:

1、 结点

2、 根结点

3、 父结点

4、 子结点

5、 叶子结点(没有子结点的结点)

6、 结点的权(结点值)

7、 路径(从root结点找到目标结点的线路)

8、 层

9、 子树

10、 树的高度(最大层数)

11、 森林(多颗子树构成森林)

 

3. 二叉树介绍

1、树有很多种,每个结点最多只能有两个子结点的一种形式称之为二叉树

2、 二叉树分为左结点和右结点

image.png

3、 如果该二叉树的所有叶子结点都在最后一层,并且结点总数是2^n-1,n是层数,则我们称之为满二叉树

image.png

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

image.png

4. 二叉树应用案例

可以使用前序,中序,后序对下面的二叉树进行遍历

1、 前序遍历:先输出父结点,在遍历左子树和右子树

2、 中序遍历:先遍历左子树,在遍历父结点,再遍历右子树

3、 后序遍历:先遍历左子树,再遍历右子树,最后遍历父结点

结论:看父结点输出顺序即是某序遍历

Node

/**

 * author:韩国庆

 * date:2021/3/22 16:55

 * version:1.0

 */

public class Node {

 

    private int no;

    private String name;

    private Node left;

    private Node right;

 

    public Node(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 Node getLeft() {

        return left;

    }

 

    public void setLeft(Node left) {

        this.left = left;

    }

 

    public Node getRight() {

        return right;

    }

 

    public void setRight(Node right) {

        this.right = right;

    }
    @Override

    public String toString() {

        return "Node{" +

                "no=" + no +

                ", name='" + name + '\'' +

                ", left=" + left +

                ", right=" + right +

                '}';

    }

 

    /**

     * 前序遍历

     */

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

    }

}

BinaryTree

/**

 * author:韩国庆

 * date:2021/3/22 16:47

 * version:1.0

 */

 

public class BinaryTree {

 

    private Node root;

 

    public void setRoot(Node root) {

        this.root = root;

    }

 

    /**

     * 前序遍历

     */

    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("二叉树为空,无法遍历");

        }

    }

}

Test

/**

 * author:韩国庆

 * date:2021/3/23 14:23

 * version:1.0

 */

public class Test {

 

    public static void main(String[] args) {

 

        BinaryTree binaryTree = new BinaryTree();

 

        Node root = new Node(1,"孙尚香");

        Node node2 = new Node(2,"夏侯惇");

        Node node3 = new Node(3,"貂蝉");

        Node node4 = new Node(4,"吕布");
        Node node5 = new Node(5,"虞姬");

        Node node6 = new Node(6,"王昭君");

 

        root.setLeft(node2);

        node2.setLeft(node3);

        root.setRight(node4);

        node4.setRight(node5);

        node2.setLeft(node6);

 

 

        binaryTree.setRoot(root);

        /**

         * 前序

         */

        binaryTree.preOrder();

 

        System.out.println("-----------------------------------------------");

 

        binaryTree.infixOrder();

        System.out.println("-----------------------------------------------");

        binaryTree.postOrder();

 

 

    }

}

二叉树 查询结点

根据后序,中序,前序来进行查找

Node

package com.bjpowernode.binary;

 

/**

 * author:韩国庆

 * date:2021/3/22 16:55

 * version:1.0

 */

public class Node {

 

    private int no;

    private String name;

    private Node left;

    private Node right;

 

    public Node(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 Node getLeft() {

        return left;

    }

 

    public void setLeft(Node left) {

        this.left = left;

    }

 

    public Node getRight() {

        return right;

    }

 

    public void setRight(Node right) {

        this.right = right;

    }

 

    @Override

    public String toString() {

        return "Node{" +

                "no=" + no +

                ", name='" + name + '\'' +

                ", left=" + left +

                ", right=" + right +

                '}';
                }

 

    /**

     * 前序遍历

     */

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

    }

 

 

    /**

     * 前序遍历查找

     */

    public Node preOrderSearch(int no){

 

        /**

         * 判断是否是当前结点

         */

        if (this.no == no){

            return this;

        }

 

        /**
        * 判断当前结点的左子结点是否为空,如果不是空,则递归前序查找

            如果左递归前序查找,找到结点,则返回

         */

        Node lNode = null;

        if (this.left !=null){

            lNode = this.left.preOrderSearch(no);

        }

 

        if (lNode !=null){

            return lNode;

        }

 

        /**

         * 左递归前序查找,找到结点,则返回否则继续判断

         *

         * 当前的结点的右子结点是否为空,如果不空,则继续像右递归前序查找

         */

        if (this.right !=null){

            lNode = this.right.preOrderSearch(no);

        }

        return lNode;

 

    }

 

    /**

     * 中序遍历查找

     */

    public Node infixOrderSearch(int no){

        /**

         * 判断当前结点左子结点是否为空,如果不为空,则递归中序查找
         */

        Node node = null;

 

        if (this.left !=null){

            node = this.left.infixOrderSearch(no);

        }

        if (node !=null){

            return node;

        }

 

        if (this.no == no){

            return this;

        }

 

        if (this.right !=null){

            node = this.right.infixOrderSearch(no);

        }

        return node;

    }

 

    /**

     * 后序遍历查找

     */

    public Node postOrderSearch(int no){

        Node node = null;

        if (this.left !=null){

            node = this.left.postOrderSearch(no);

        }

 

        if (node !=null){
        return node;

        }

 

        /**

         * 如果左边子树没有找到,则向右边子树递归进行后序遍历查找

         */

        if (this.right !=null){

            node = this.right.postOrderSearch(no);

        }

        if (node !=null){

            return node;

        }

 

        /**

         * 如果左右子树都没有找到,那么判断当前结点是否是呢

         */

        if (this.no==no){

            return this;

        }

        return node;

    }

 

}

BinaryTree


 

public class BinaryTree {

 

    private Node root;

 

    public void setRoot(Node root) {

        this.root = root;

    }

 

    /**

     * 前序遍历

     */

    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 Node preOrderNode(int no){

        if (root !=null){

            return root.preOrderSearch(no);

        }else {

            return null;

        }

    }

 

    /**

     * 中序

     */

    public Node infixOrderNode(int no){

        if (root !=null){

            return root.infixOrderSearch(no);

        }else {

            return null;

        }

    }
    /**

     * 后序

     */

    public Node postOrderNode(int no){

        if (root !=null){

            return root.postOrderSearch(no);

        }else {

            return null;

        }

    }

 

 

 

 

 

 

}

test


 * author:韩国庆

 * date:2021/3/23 14:23

 * version:1.0

 */

public class Test {

 

    public static void main(String[] args) {
    BinaryTree binaryTree = new BinaryTree();

 

        Node root = new Node(1,"孙尚香");

        Node node2 = new Node(2,"夏侯惇");

        Node node3 = new Node(3,"貂蝉");

        Node node4 = new Node(4,"吕布");

        Node node5 = new Node(5,"虞姬");

        Node node6 = new Node(6,"王昭君");

 

        root.setLeft(node2);

        node2.setLeft(node3);

        root.setRight(node4);

        node4.setRight(node5);

        node2.setLeft(node6);

 

 

        binaryTree.setRoot(root);

        /**

         * 前序

         */

        /*binaryTree.preOrder();

 

        System.out.println("-----------------------------------------------");

 

        binaryTree.infixOrder();

        System.out.println("-----------------------------------------------");

        binaryTree.postOrder();*/

 

 

        /**
        * 根据id  前序查找

         */

        Node node = binaryTree.preOrderNode(5);

        if (node !=null){

            System.out.printf("信息为:id=%d name%s",node.getNo(),node.getName());

        }else {

            System.out.println("没有找到");

        }

 

        /**

         * 后序查找

         */

        Node infix = binaryTree.infixOrderNode(5);

        if (infix !=null){

            System.out.printf("信息为:id=%d name%s",infix.getNo(),infix.getName());

        }else {

            System.out.println("没有找到");

        }

 

 

        Node post = binaryTree.postOrderNode(5);

        if (post !=null){

            System.out.printf("信息为:id=%d name%s",post.getNo(),post.getName());

        }else {

            System.out.println("没有找到");

        }

 

 

 

    }
    }

二叉树 删除结点

删除结点可划分为两种情况:

1、 删除的结点是叶子结点,那么删除当前几点即可。

2、 删除的结点是非叶子结点,那么需要删除该子树。

删除需求:

1、 删除5号结点

Node类增加删除方法

public void delNode(int no){

 

        /**

         * 注意:

         * 1、二叉树是单向的,所以我们是判断当前结点的子结点是否需要删除,而不能去判断当前这个结点是不是需要删除结点

         * 2、如果当前结点左子结点不为空,并且左子结点就是需要删除的结点,则将this.left = null,返回即可

         * 3、如果当前结点右子结点不为空,并且右子结点就是需要删除的结点,就将this.right = null,返回即可

         * 4、如果2,3步没有执行,那么需要向左子树进行递归删除

         * 5、如果第4步也没有删除结点,则向右子树进行递归删除

         */

 

        /**

         * 第2步操作

         */
         if (this.left !=null &&this.left.no==no){

            this.left = null;

            return;

        }

 

        /**

         * 第3步操作

         */

        if (this.right !=null && this.right.no==no){

            this.right = null;

            return;

        }

 

        /**

         * 第4步:向左子树递归删除

         */

        if (this.left !=null){

            this.left.delNode(no);

        }

 

        /**

         * 第5步:向右子树递归删除

         */

        if (this.right !=null){

            this.right.delNode(no);

        }
        
        }

BinaryTree类增加删除方法


 

        if (root !=null){

            if (root.getNo()==no){

                root = null;

            }else {

                root.delNode(no);

            }

        }else {

            System.out.println("树为空,不能操作删除");

        }

    }

测试

image.png