九、二叉树

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

自学JAVA数据结构笔记,跟学视频为:黑马程序员Java数据结构与java算法全套教程,数据结构+算法教程全资料发布,包含154张java数据结构图_哔哩哔哩_bilibili


提示:以下是本篇文章正文内容,下面案例可供参考

一、树的基本定义

1.含义

树是我们计算机中非常重要的一种数据结构,同时使用树这种数据结构,可以描述现实生活中的很多事物,例如家 谱、单位的组织架构、等等。

树是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就 是说它是根朝上,而叶朝下的。

2.特点

树具有以下特点:

1.每个结点有零个或多个子结点;

2.没有父结点的结点为根结点;

3.每一个非根结点只有一个父结点;

4.每个结点及其后代结点整体上可以看做是一棵树,称为当前结点的父结点的一个子树;

二、树的相关术语

结点的度:

一个结点含有的子树的个数称为该结点的度;

叶结点:

度为0的结点称为叶结点,也可以叫做终端结点

分支结点:

度不为0的结点称为分支结点,也可以叫做非终端结点

结点的层次:

从根结点开始,根结点的层次为1,根的直接后继层次为2,以此类推

结点的层序编号:

将树中的结点,按照从上层到下层,同层从左到右的次序排成一个线性序列,把他们编成连续的自然数。

树的度:

树中所有结点的度的最大值

树的高度(深度):

树中结点的最大层次

森林:

m(m>=0)个互不相交的树的集合,将一颗非空树的根结点删去,树就变成一个森林;给森林增加一个统一的根 结点,森林就变成一棵树

三、二叉查找树的创建

1.结点类

结点类API设计:

类名                 Node

构造方法         Node(Key key, Value value, Node left, Node right):创建Node对象

成员变量         1.public Node left:记录左子结点

                        2.public Node right:记录右子结点

                        3.public Key key:存储键

                        4.public Value value:存储值

代码实现: 

private class Node<Key,Value>{
    //存储键
    public Key key;

    //存储值
    private Value value;

    //记录左子结点
    public Node left;

    //记录右子结点
    public Node right;

    public Node(Key key, Value value, Node left, Node right) {

        this.key = key;
        this.value = value;
        this.left = left;
        this.right = right;

    }
}

 2. 二叉查找树

二叉查找树API设计:

类名                 BinaryTree,Value value>

构造方法         BinaryTree():创建BinaryTree对象

成员变量         1.private Node root:记录根结点

                        2.private int N:记录树中元素的个数

成员方法         1. public void put(Key key,Value value):向树中插入一个键值对

                        2.private Node put(Node x, Key key, Value val):给指定树x上,添加键一个键值对,并返回添 加后的新树

                        3.public Value get(Key key):根据key,从树中找出对应的值

                        4.private Value get(Node x, Key key):从指定的树x中,找出key对应的值                         5.public void delete(Key key):根据key,删除树中对应的键值对

                        6.private Node delete(Node x, Key key):删除指定树x上的键为key的键值对,并返回删除后的新树

                        7.public int size():获取树中元素的个数

 二叉查找树实现:

package BinaryTree;

public class BinaryTree<Key extends Comparable<Key>, Value> {

    //记录根结点
    private Node root;

    //记录树中元素的个数
    private int N;

    //获取树中元素的个数
    public int size() {
        return N;
    }

    //向树中添加元素key-value
    public void put(Key key, Value value) {
        root = put(root, key, value);
    }

    //向指定的树x中添加key-value,并返回添加元素后新的树
    private Node put(Node x, Key key, Value value) {

        //当跟结点为空
        if (x == null) {

            //个数+1
            N++;
            return new Node(key, value, null, null);

        }

        //比较数
        int cmp = key.compareTo(x.key);

        if (cmp > 0) {
            //新结点的key大于当前结点的key,继续找当前结点的右子结点
            x.right = put(x.right, key, value);

        } else if (cmp < 0) {
            //新结点的key小于当前结点的key,继续找当前结点的左子结点
            x.left = put(x.left, key, value);
        } else {
            //新结点的key等于当前结点的key,把当前结点的value进行替换
            x.value = value;
        }
        return x;

    }

    //查询树中指定key对应的value
    public Value get(Key key) {
        return get(root, key);
    }

    //从指定的树x中,查找key对应的值
    public Value get(Node x, Key key) {

        if (x == null) {
            return null;
        }

        int cmp = key.compareTo(x.key);

        if (cmp > 0) {
            //如果要查询的key大于当前结点的key,则继续找当前结点的右子结点;
            return get(x.right, key);
        } else if (cmp < 0) {
            //如果要查询的key小于当前结点的key,则继续找当前结点的左子结点;
            return get(x.left, key);
        }else {
            //如果要查询的key等于当前结点的key,则树中返回当前结点的value。
            return x.value;
        }

    }

    //删除树中key对应的value
    public void delete(Key key) {
        root = delete(root, key);
    }

    //删除指定树x中的key对应的value,并返回删除后的新树
    public Node delete(Node x, Key key) {
        if (x == null) {
            return null;
        }
        int cmp = key.compareTo(x.key);

        if (cmp > 0) {
            //新结点的key大于当前结点的key,继续找当前结点的右子结点
            x.right = delete(x.right, key);
        } else if (cmp < 0) {
            //新结点的key小于当前结点的key,继续找当前结点的左子结点
            x.left = delete(x.left, key);
        } else {
            //新结点的key等于当前结点的key,当前x就是要删除的结点
            //1.如果当前结点的右子树不存在,则直接返回当前结点的左子结点
            if (x.right == null) {
                return x.left;
            }
            //2.如果当前结点的左子树不存在,则直接返回当前结点的右子结点
            if (x.left == null) {
                return x.right;
            }

            //3.当前结点的左右子树都存在
            //3.1找到右子树中最小的结点
            Node minNode = x.right;

            while (minNode.left != null) {
                minNode = minNode.left;
            }

            //3.2删除右子树中最小的结点
            Node n = x.right;

            while (n.left != null) {
                if (n.left.left == null) {
                    n.left = null;
                } else {
                    n = n.left;
                }
            }

            //3.3让被删除结点的左子树称为最小结点minNode的左子树,让被删除结点的右子树称为最小结点minNode的右子树
            minNode.left = x.left;
            minNode.right = x.right;

            //3.4让被删除结点的父节点指向最小结点minNode
            x = minNode;
            //个数-1
            N--;
        }
        return x;
    }
    private class Node {
        //存储键
        public Key key;
        //存储值
        private Value value;
        //记录左子结点
        public Node left;
        //记录右子结点
        public Node right;
        public Node(Key key, Value value, Node left, Node right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }
}

四、二叉树遍历

1.前序遍历

1.1 原理

1.把当前结点的key放入到队列中;

2.找到当前结点的左子树,如果不为空,递归遍历左子树

3.找到当前结点的右子树,如果不为空,递归遍历右子树

1.2 代码

 //使用前序遍历,把指定树x中的所有键放入到keys队列中
    private void preErgodic(Node x, Queue<Key> keys) {

        if (x == null) {
            return;
        }

        //1.把当前结点的key放入到队列中;
        keys.enqueue(x.key);

        //2.找到当前结点的左子树,如果不为空,递归遍历左子树
        if (x.left != null) {

            preErgodic(x.left, keys);
        }

        //3.找到当前结点的右子树,如果不为空,递归遍历右子树
        if (x.right != null) {

            preErgodic(x.right, keys);
        }

    }

2. 中序遍历

2.1 原理

1.找到当前结点的左子树,如果不为空,递归遍历左子树

2.把当前结点的key放入到队列中;

3.找到当前结点的右子树,如果不为空,递归遍历右子树

2.2 代码

//使用中序遍历,获取整个树中的所有键
    public Queue<Key> midErgodic() {

        Queue<Key> keys = new Queue<>();
        midErgodic(root, keys);
        return keys;
    }

    //使用中序遍历,把指定树x中的所有键放入到keys队列中
    private void midErgodic(Node x, Queue<Key> keys) {
        if (x == null) {
            return;
        }

        //1.找到当前结点的左子树,如果不为空,递归遍历左子树
        if (x.left != null) {
            midErgodic(x.left, keys);
        }

        //2.把当前结点的key放入到队列中;
        keys.enqueue(x.key);

        //3.找到当前结点的右子树,如果不为空,递归遍历右子树
        if (x.right != null) {
            midErgodic(x.right, keys);
        }
    }

3.后序遍历

3.1 原理

1.找到当前结点的左子树,如果不为空,递归遍历左子树

2.找到当前结点的右子树,如果不为空,递归遍历右子树

3.把当前结点的key放入到队列中;

3.2 代码

//使用后序遍历,获取整个树中的所有键
    public Queue<Key> afterErgodic() {

        Queue<Key> keys = new Queue<>();
        afterErgodic(root, keys);
        return keys;

    }

    //使用后序遍历,把指定树x中的所有键放入到keys队列中
    private void afterErgodic(Node x, Queue<Key> keys) {
        if (x == null) {
            return;
        }

        //1.找到当前结点的左子树,如果不为空,递归遍历左子树
        if (x.left != null) {
            afterErgodic(x.left, keys);
        }

        //2.找到当前结点的右子树,如果不为空,递归遍历右子树
        if (x.right != null) {
            afterErgodic(x.right, keys);
        }

        //3.把当前结点的key放入到队列中;
        keys.enqueue(x.key);
    }

4.层序遍历

4.1 代码

 //使用层序遍历得到树中所有的键
    public Queue<Key> layerErgodic() {

        Queue<Key> keys = new Queue<>();
        Queue<Node> nodes = new Queue<>();
        nodes.enqueue(root);

        while (!nodes.isEmpty()) {
            Node x = nodes.dequeue();
            keys.enqueue(x.key);
            if (x.left != null) {
                nodes.enqueue(x.left);
            }
            if (x.right != null) {
                nodes.enqueue(x.right);
            }
        }
        return keys;

    }

 

总结

提示:这里对文章进行总结:
 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mikudd3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值