您当前的位置:首页 > 电脑百科 > 程序开发 > 算法

二叉树的java实现,超级简单讲解版

时间:2021-04-12 10:43:19  来源:今日头条  作者:代码小当家

二叉树的基本定义

简而言之:二叉树就是度不能超过2的树(每个树只能有两个节点)

满二叉树

一个二叉树,如果每一个层的结点树达到最大值,则在这个树就是满二叉树

完全二叉树:

叶结点只能出现在最下层和次下层,并且最下面那一层的结点都集中在该层最左边的若干位置的二叉树

二叉查找树:

二叉查找树是一种特殊的二叉树,相对较小的值保存在左结点中,较大的值保存在右结点中。
根据对图的观察,我们发现二叉树其实就是由一个一个的结点及其之间的关系组成的,按照面向对象的思想,我们设计一个结点来描述结点这个事务。

首先我们先想着实现二叉树需要一些什么参数?

    private static class Node{
        public Node left;
        public Node right;
        public Integer key;
        public String value;

        public Node(Node left, Node right, Integer key, String value) {
            this.left = left;
            this.right = right;
            this.key = key;
            this.value = value;
        }
    }

在上面我们定义了left,right,key,value四个参数,并且定义了这个类的构造方法

我们看插入方法put思想:

  1. 如果当树中没有任何一个结点,则直接把新结点当根结点使用
  2. 如果当前树不为空,就从根结点开始
    2-1:如果要查询的key,小于当前结点的key,则继续查找当前结点的左子结点2-2:如果新结点的key大于当前结点的key,则继续找当前结点的右子结点2-3:如果新结点的key等于当前结点的key,则树中已经存在这样的结点,替换该结点的value值就好

那么我们要怎么实现呢?

    //向树中插入一个键值对
    public void put(Integer key,String value){
        root=put(root,key,value);
    }
    //给指定的数x添加一个键,添加一个键值对,并返回添加后的新数
    private Node put(Node tree,Integer key,String value){
        if(tree==null){
            //个数加1
            n++;
            //直接把新结点当成根结点使用
            return new Node(null,null,key,value);
        }
        //比较key,如果新结点大于当前结点的key,继续寻找当前结点的右子结点
        if(key > tree.key){
            tree.right=put(tree.right,key,value);
        }else if(key<tree.key){
            //新结点的key小于当前结点的key,继续找当前结点的左子结点
            tree.left=put(tree.left,key,value);
        }else{
            //新结点的key等于当前结点的key
            tree.value=value;
        }
        return tree;
    }

在上面的代码块中,我们定义了两个put方法,一个是给其他类操作的,一个是给自己类操作的,对于外部的用户来说,他只需要将键值对传递进来就好了,排序是我们程序员的事,所以,我们这里的put只有两个参数:

public void put(Integer key,String value)

然后我们通过这个put方法再去调用我们内部的put方法,在这个方法中,我们首先要把我们的根结点root传递进去,然后再将前端传给我们的key:value传递进去

private Node put(Node tree,Integer key,String value)

这样,我们就可以在这个put上进行我们一开始定义的开发流程了:

  1. 如果树中没有结点,就把当前插入的结点当成首节点使用:
        if(tree==null){
            //个数加1
            n++;
            //直接把新结点当成根结点使用
            return new Node(null,null,key,value);
        }
  1. 如果树不为空,就从根结点开始遍历,也就是root结点
    2-1. 如果要查询的key,小于当前结点的key,则继续查找当前结点的左子结点
        //比较key,如果新结点大于当前结点的key,继续寻找当前结点的右子结点
        if(key > tree.key){
            tree.right=put(tree.right,key,value);
        }

2-2. 如果新结点的key大于当前结点的key,则继续找当前结点的右子结点

else if(key<tree.key){
            //新结点的key小于当前结点的key,继续找当前结点的左子结点
            tree.left=put(tree.left,key,value);
        }

2-3:如果新结点的key等于当前结点的key,则树中已经存在这样的结点,替换该结点的value值就好

else{
            //新结点的key等于当前结点的key
            tree.value=value;
        }

现在put方法就执行完毕了,我们把一个前端传递过来的值放入了二叉树中.

上面我们已经实现了二叉树中的put方法,按照我的习惯的话呢接下来我们还是先讲思想,讲get方法和delete方法:

查询方法get实现思想:
从根结点开始:

  1. 如果要查询的key小于当前结点的key,则继续查找当前结点的左子结点
  2. 如果要查询的key大于当前结点的key,则继续找当前结点的右子结点
  3. 如果要查询的key等于当前结点的key,则树中返回当前结点的value

删除方法delete实现思想:

  1. 找到被删除结点
  2. 找到被删除结点右子树的最小结点
  3. 删除右子树中的最小结点
  4. 让被删除结点的左子树称为最小结点的左子树,让被删除结点的右子树称为最小结点的子树
  5. 让被删除节点的父结点指向最小结点

按照从简单到困难的准则,我们先从简单的开始,get方法相对于delete而言要简单一点,所以我们先实现get方法

    //从树中找到对应的值
    public String get(Integer key){
        return get(root,key);
    }
    private String get(Node tree,Integer key){
        if(root==null){
            return null;
        }
        //比较key,如果新结点大于当前结点的key,继续寻找当前结点的右子结点
        if(key > tree.key){
            
            return get(tree.right,key);
        }else if(key<tree.key){
            //比较key,如果新结点大于当前结点的key,继续寻找当前结点的左子结点
         
            return get(tree.left,key);
        }else{
            //要查找的key和当前结点的key相等,返回value
            return tree.value;
        }
    }

通过不停的递归调用get方法,我们就可以不断的查找树的左右结点,从而最终返回get到的结果值,这个非常简单,没什么好说的。

接下来比较重要的是delete方法:

    //从指定的树中,根据key删除键中的键值对
    public void delete(Integer key){

        root=delete(root,key);
    }
    private Node delete(Node tree,Integer key){
        if(tree==null){
            return null;
        }
        if(key > tree.key){
            tree.right=delete(tree.right,key);
        }else if(key<tree.key){
            tree.left=delete(tree.left,key);
        }else{
            //待删除的key等于当前结点的key,说明当前结点就是要删除的结点
            //1、如果当前结点的右子树不存在,则直接返回当前结点的左子结点
            if(tree.right==null){
                n--;
                return tree.left;
            }
            //2、如果当前结点的左子树不存在,则直接返回当前结点的右子结点
            if(tree.left==null){
                n--;
                return  tree.right;
            }
            //当前结点的左右子树都存在
            //找到右子树中的最小结点
            Node minNode=tree.right;
            //二叉查找树的左结点一定比右结点小
            if(minNode.left!=null){
                minNode=minNode.left;
            }
            //到这里就找到了当前结点右子树中最小的结点minNode
            //删除右子树中最小的结点
            Node node=tree.right;
            while (node.left!=null){
                if(node.left.left==null){
                    //说明N的左结点就是我们要找的最小结点
                    node.left=null;
                }else{
                    node=node.left;
                }
            }
            //到这里,最小结点已经被删除了
            //让被删除结点的左子树成为最小结点的左子树,让被删除结点的右子树,成为最小结点的右子树
            minNode.left=tree.left;
            minNode.right=tree.right;
            //让被删除结点的父结点指向最小结点
            tree=minNode;
            //个数减1
            n--;
        }
        return tree;
    }

上面的这段代码看着很长,且听我与你一一分解

首先我们通过public方法方便别的类调用,用户只需要传递key值进入我们的后台,我们就可以通过后台的查找方法来查找二叉树中的元素,然后对其进行删除。

这同样使用了递归的思想。通过不断的查找二叉树中的元素,找到要删除的那个数据。

        if(key > tree.key){
            tree.right=delete(tree.right,key);
        }else if(key<tree.key){
            tree.left=delete(tree.left,key);
        }else{
            //待删除的key等于当前结点的key,说明当前结点就是要删除的结点
            //1、如果当前结点的右子树不存在,则直接返回当前结点的左子结点
            if(tree.right==null){
                n--;
                return tree.left;
            }
            //2、如果当前结点的左子树不存在,则直接返回当前结点的右子结点
            if(tree.left==null){
                n--;
                return  tree.right;
            }
            //当前结点的左右子树都存在
            //找到右子树中的最小结点
            Node minNode=tree.right;
            //二叉查找树的左结点一定比右结点小
            if(minNode.left!=null){
                minNode=minNode.left;
            }
            //到这里就找到了当前结点右子树中最小的结点minNode
            //删除右子树中最小的结点
            Node node=tree.right;
            while (node.left!=null){
                if(node.left.left==null){
                    //说明N的左结点就是我们要找的最小结点
                    node.left=null;
                }else{
                    node=node.left;
                }
            }
            //到这里,最小结点已经被删除了
            //让被删除结点的左子树成为最小结点的左子树,让被删除结点的右子树,成为最小结点的右子树
            minNode.left=tree.left;
            minNode.right=tree.right;
            //让被删除结点的父结点指向最小结点
            tree=minNode;
            //个数减1
            n--;
        }

如果当前递归到的这个结点的元素值小于我们用户传递进来的key的话我们将其往左进行递归

        if(key > tree.key){
            tree.right=delete(tree.right,key);
        }

如果大于的话我们就往右进行递归

else if(key<tree.key){
            tree.left=delete(tree.left,key);
        }

如果说用户传递过来的key与当前结点的key值相等的话,那么说明当前的这个结点就是我们要删除的这个结点

else{
            //待删除的key等于当前结点的key,说明当前结点就是要删除的结点
            //1、如果当前结点的右子树不存在,则直接返回当前结点的左子结点
            if(tree.right==null){
                n--;
                return tree.left;
            }
            //2、如果当前结点的左子树不存在,则直接返回当前结点的右子结点
            if(tree.left==null){
                n--;
                return  tree.right;
            }
            //当前结点的左右子树都存在
            //找到右子树中的最小结点
            Node minNode=tree.right;
            //二叉查找树的左结点一定比右结点小
            if(minNode.left!=null){
                minNode=minNode.left;
            }
            //到这里就找到了当前结点右子树中最小的结点minNode
            //删除右子树中最小的结点
            Node node=tree.right;
            while (node.left!=null){
                if(node.left.left==null){
                    //说明N的左结点就是我们要找的最小结点
                    node.left=null;
                }else{
                    node=node.left;
                }
            }
            //到这里,最小结点已经被删除了
            //让被删除结点的左子树成为最小结点的左子树,让被删除结点的右子树,成为最小结点的右子树
            minNode.left=tree.left;
            minNode.right=tree.right;
            //让被删除结点的父结点指向最小结点
            tree=minNode;
            //个数减1
            n--;
        }
        return tree;
    }

现在又要研究二叉树中的删除方法中结点的性质了,我们既然要把这个元素进行删除操作的话,那么是不是,我们就要将他的子结点的层级往上提升一级,那么我们接着研究:

            //待删除的key等于当前结点的key,说明当前结点就是要删除的结点
            //1、如果当前结点的右子树不存在,则直接返回当前结点的左子结点
            if(tree.right==null){
                n--;
                return tree.left;
            }
            //2、如果当前结点的左子树不存在,则直接返回当前结点的右子结点
            if(tree.left==null){
                n--;
                return  tree.right;
            }

如果当前结点的右子树不存在,那么我们就把该结点的左子树给他提上去
如果当前结点的左子树不存在,那么我们就把他的右子树提上去

如果说当前结点的左右子树都存在的话,那么就有点小麻烦了,那么我们就要从要被删除的这个结点的右子树中找到他的最小元素,然后把他的最小元素给他提上去。

            //到这里就找到了当前结点右子树中最小的结点minNode
            //删除右子树中最小的结点
            Node node=tree.right;
            while (node.left!=null){
                if(node.left.left==null){
                    //说明N的左结点就是我们要找的最小结点
                    node.left=null;
                }else{
                    node=node.left;
                }
            }
            //到这里,最小结点已经被删除了
            //让被删除结点的左子树成为最小结点的左子树,让被删除结点的右子树,成为最小结点的右子树
            minNode.left=tree.left;
            minNode.right=tree.right;
            //让被删除结点的父结点指向最小结点
            tree=minNode;
            //个数减1
            n--;
        }

最后在我们所有操作都已经执行完毕之后,我们只要返回这个改变之后的tree就好了

好了,现在我们创建一个外部类Test1来验证此程序的正确性

class Test1{
    public static void main(String[] args) {
        BinaryTree tree=new BinaryTree();
        tree.put(8,"鸡霸");
        tree.put(7,"田七");
        tree.put(9,"吴久");
        tree.put(3,"张三");
        tree.put(6,"陆远");
        System.out.println(tree.get(7));
        tree.delete(6);
        tree.delete(9);
        tree.delete(3);
        System.out.println(tree.size());
    }
}

然后我放出全部代码方便大家实验:

package com.gm.tree;

public class BinaryTree {
    //记录一个根结点
    private Node root;
    //记录树中的元素个数
    private int n;

    public BinaryTree() {
    }
    //向树中插入一个键值对
    public void put(Integer key,String value){
        root=put(root,key,value);
    }
    //给指定的数x添加一个键,添加一个键值对,并返回添加后的新数
    private Node put(Node tree,Integer key,String value){
        if(tree==null){
            //个数加1
            n++;
            //直接把新结点当成根结点使用
            return new Node(null,null,key,value);
        }
        //比较key,如果新结点大于当前结点的key,继续寻找当前结点的右子结点
        if(key > tree.key){
            tree.right=put(tree.right,key,value);
        }else if(key<tree.key){
            //新结点的key小于当前结点的key,继续找当前结点的左子结点
            tree.left=put(tree.left,key,value);
        }else{
            //新结点的key等于当前结点的key
            tree.value=value;
        }
        return tree;
    }
    //从树中找到对应的值
    public String get(Integer key){
        return get(root,key);
    }
    private String get(Node tree,Integer key){
        if(root==null){
            return null;
        }
        //比较key,如果新结点大于当前结点的key,继续寻找当前结点的右子结点
        if(key > tree.key){
            return get(tree.right,key);
        }else if(key<tree.key){
            //比较key,如果新结点大于当前结点的key,继续寻找当前结点的左子结点
            return get(tree.left,key);
        }else{
            //要查找的key和当前结点的key相等,返回value
            return tree.value;
        }
    }
    //从指定的树中,根据key删除键中的键值对
    public void delete(Integer key){

        root=delete(root,key);
    }
    private Node delete(Node tree,Integer key){
        if(tree==null){
            return null;
        }
        if(key > tree.key){
            tree.right=delete(tree.right,key);
        }else if(key<tree.key){
            tree.left=delete(tree.left,key);
        }else{
            //待删除的key等于当前结点的key,说明当前结点就是要删除的结点
            //1、如果当前结点的右子树不存在,则直接返回当前结点的左子结点
            if(tree.right==null){
                n--;
                return tree.left;
            }
            //2、如果当前结点的左子树不存在,则直接返回当前结点的右子结点
            if(tree.left==null){
                n--;
                return  tree.right;
            }
            //当前结点的左右子树都存在
            //找到右子树中的最小结点
            Node minNode=tree.right;
            //二叉查找树的左结点一定比右结点小
            if(minNode.left!=null){
                minNode=minNode.left;
            }
            //到这里就找到了当前结点右子树中最小的结点minNode
            //删除右子树中最小的结点
            Node node=tree.right;
            while (node.left!=null){
                if(node.left.left==null){
                    //说明N的左结点就是我们要找的最小结点
                    node.left=null;
                }else{
                    node=node.left;
                }
            }
            //到这里,最小结点已经被删除了
            //让被删除结点的左子树成为最小结点的左子树,让被删除结点的右子树,成为最小结点的右子树
            minNode.left=tree.left;
            minNode.right=tree.right;
            //让被删除结点的父结点指向最小结点
            tree=minNode;
            //个数减1
            n--;
        }
        return tree;
    }
    public int size(){
        return n;
    }
    private static class Node{
        public Node left;
        public Node right;
        public Integer key;
        public String value;

        public Node(Node left, Node right, Integer key, String value) {
            this.left = left;
            this.right = right;
            this.key = key;
            this.value = value;
        }
    }
}
class Test1{
    public static void main(String[] args) {
        BinaryTree tree=new BinaryTree();
        tree.put(8,"雷霸天");
        tree.put(7,"田七");
        tree.put(9,"吴久");
        tree.put(3,"张三");
        tree.put(6,"陆远");
        System.out.println(tree.get(7));
        tree.delete(6);
        tree.delete(9);
        tree.delete(3);
        System.out.println(tree.size());
    }
}


Tags:二叉树   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
对于二叉树,它的特点就是任何一个节点,左节点小于父节点、右节点大于父节点遍历二叉树有多种方式,如中序遍历、层序遍历、后序遍历,中序遍历的思路就是左-->父--->右的顺序,下面...【详细内容】
2021-11-02  Tags: 二叉树  点击:(38)  评论:(0)  加入收藏
二叉树的基本定义简而言之:二叉树就是度不能超过2的树(每个树只能有两个节点)满二叉树:一个二叉树,如果每一个层的结点树达到最大值,则在这个树就是满二叉树完全二叉树:叶结点只能...【详细内容】
2021-04-12  Tags: 二叉树  点击:(246)  评论:(0)  加入收藏
作者 | BoCong-Deng题图 | 视觉中国出品 | CSDN博客树结构对于程序员来说应该不陌生,特别是二叉树,基本只要接触算法这一类的都一定会碰到的,所以我打算通过一篇文章,对二叉树结...【详细内容】
2020-06-19  Tags: 二叉树  点击:(52)  评论:(0)  加入收藏
前文介绍了,二叉树、二叉排序树,需要了解的不妨关注下小JIA。 AVL是一种高度平衡的二叉排序树。对于任意节点左子树与右子树高度差不超过1,AVL的高度与节点数量为O(logn)关系。...【详细内容】
2019-10-15  Tags: 二叉树  点击:(109)  评论:(0)  加入收藏
1 题目描述给定一个二叉树,判断其是否为一个完全二叉树。来自Wikipedia的完全二叉树定义:在一个完全二叉树中,除了最后一层可能未被完全填充外,其它所有层均被完全填充,且最后一...【详细内容】
2019-09-18  Tags: 二叉树  点击:(150)  评论:(0)  加入收藏
盗图前言最近准备面试 ,复习了一下数据结构 中的二叉树,整理了二叉树的前序、中序、后序、深度和广度遍历以及递归和非递归实现方法,如有好的方案大家可以一起讨论。前序遍历...【详细内容】
2019-09-09  Tags: 二叉树  点击:(154)  评论:(0)  加入收藏
▌简易百科推荐
前言Kafka 中有很多延时操作,比如对于耗时的网络请求(比如 Produce 是等待 ISR 副本复制成功)会被封装成 DelayOperation 进行延迟处理操作,防止阻塞 Kafka请求处理线程。Kafka...【详细内容】
2021-12-27  Java技术那些事    Tags:时间轮   点击:(1)  评论:(0)  加入收藏
博雯 发自 凹非寺量子位 报道 | 公众号 QbitAI在炼丹过程中,为了减少训练所需资源,MLer有时会将大型复杂的大模型“蒸馏”为较小的模型,同时还要保证与压缩前相当的结果。这就...【详细内容】
2021-12-24  量子位    Tags:蒸馏法   点击:(9)  评论:(0)  加入收藏
分稀疏重建和稠密重建两类:稀疏重建:使用RGB相机SLAMOrb-slam,Orb-slam2,orb-slam3:工程地址在: http://webdiis.unizar.es/~raulmur/orbslam/ DSO(Direct Sparse Odometry)因为...【详细内容】
2021-12-23  老师明明可以靠颜值    Tags:算法   点击:(7)  评论:(0)  加入收藏
1. 基本概念希尔排序又叫递减增量排序算法,它是在直接插入排序算法的基础上进行改进而来的,综合来说它的效率肯定是要高于直接插入排序算法的;希尔排序是一种不稳定的排序算法...【详细内容】
2021-12-22  青石野草    Tags:希尔排序   点击:(6)  评论:(0)  加入收藏
ROP是一种技巧,我们对execve函数进行拼凑来进行system /bin/sh。栈迁移的特征是溢出0x10个字符,在本次getshell中,还碰到了如何利用printf函数来进行canary的泄露。ROP+栈迁移...【详细内容】
2021-12-15  星云博创    Tags:栈迁移   点击:(19)  评论:(0)  加入收藏
一、什么是冒泡排序1.1、文字描述冒泡排序是一种简单的排序算法。它重复地走访要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地...【详细内容】
2021-12-15    晓掌柜丶韶华  Tags:排序算法   点击:(16)  评论:(0)  加入收藏
在了解golang的map之前,我们需要了解哈希这个概念。哈希表,又称散列表(Hash table),是根据键(key)而直接访问在内存储存位置的数据结构。也就是说,它通过计算出一个键值的函数,将...【详细内容】
2021-12-07  一棵梧桐木    Tags:哈希表   点击:(13)  评论:(0)  加入收藏
前面文章在谈论分布式唯一ID生成的时候,有提到雪花算法,这一次,我们详细点讲解,只讲它。SnowFlake算法据国家大气研究中心的查尔斯&middot;奈特称,一般的雪花大约由10^19个水分子...【详细内容】
2021-11-17  小心程序猿QAQ    Tags:雪花算法   点击:(24)  评论:(0)  加入收藏
导读:在大数据时代,对复杂数据结构中的各数据项进行有效的排序和查找的能力非常重要,因为很多现代算法都需要用到它。在为数据恰当选择排序和查找策略时,需要根据数据的规模和类型进行判断。尽管不同策略最终得到的结果完...【详细内容】
2021-11-04  华章科技    Tags:排序算法   点击:(37)  评论:(0)  加入收藏
这是我在网上找的资源的一个总结,会先给出一个我看了觉得还行的关于算法的讲解,再配上实现的代码: Original author: Bill_Hoo Original Address: http://blog.sina.com.cn/s/bl...【详细内容】
2021-11-04  有AI野心的电工和码农    Tags: KMP算法   点击:(36)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条