Python 每日一记247>>>Java二叉树最大深度算法

一、思路

求二叉树最大深度,就是最长的从上到下的一条线右几个节点。
从根节点出发,递归调用算法,求左右子树的最大深度,每次计算深度都是取左右子树深度较大的一个+1,当递归到最后时,递归就会慢慢返回,原问题得到结果。

二、Java代码实现

直接看42行代码即可

package mypackage;

import java.util.Iterator;

//队列类,用链表实现,遍历二叉树时需要用来装键
class Queue<T> implements Iterable<T>{
    //    节点个数,头节点,尾节点
    private int N;
    private Node head;
    private Node last;
    //节点类
    public class Node {
        public T data;
        public Node next;

        public Node(T data, Node next) {
            this.data = data;
            this.next = next;
        }
    }
    //构造方法,初始化
    public Queue() {
        this.N = 0;
        this.head = new Node(null,null);
        this.last = null;
    }
    //队列长度
    public int size(){
        return N;
    }
    //队列是否为空
    public boolean isEmpty(){
        return N==0;
    }
    //入队列
    public void enqueue(T data){
//        如果队列为空,说明尾节点为空,让新节点为尾节点,头借点指向尾节点
        if (isEmpty()){
            last=new Node(data,null);
            head.next=last;
//            如果队列不为空,让新节点为尾节点,老的尾节点指向新尾节点
        }else {
            Node oldlast=last;
            last=new Node(data,null);
            oldlast.next=last;
        }
//        最后元素+1
        N++;
    }
    //出队列,注意先入先出,每次出的节点就是head指向的第一个节点,然后让head只想第二个节点即可
//    且注意,如果队列为空,要将last=null
    public T dequeue(){
//        如果为空,返回null
        if (isEmpty()){
            return null;
//            如果不为空,让head只想第二个节点,元素-1,且如果队列为空,要将last=null
        }else {
            Node oldfirst=head.next;
            head.next=oldfirst.next;
            N--;

            if (isEmpty()){
                last=null;
            }
//            返回弹出的元素
            return oldfirst.data;
        }
    }

    //    遍历
    @Override
    public Iterator iterator() {
        return new QIterator();
    }
    //    创建一个内部类实现Iterator接口
    public class QIterator implements Iterator {
        //        定义一个遍历的节点
        private Node n;

        public QIterator() {
//            初始化为0索引位置
            this.n = head;
        }

        //重写两个方法
        @Override
        public boolean hasNext() {
//            这个方法判断是否超出最大索引,如果超出会停止遍历
            return n.next != null;
        }

        @Override
        public Object next() {
//            这个方法会遍历得每个节点
            n = n.next;
            return n.data;
        }
    }
}

//二叉树类
class Btree<key extends Comparable, value> {

    public Node root;
    private int N;

    //    节点类,包括键值对,左子节点和右子节点
    public class Node {
        public key key;
        public 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;
        }
    }

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

    //   向树中添加元素,这个是对外调用的方法,从root开始递归,添加元素
//    核心是下面的put(Node node, key key, value value)方法
    public void put(key key, value value) {
        root = put(root, key, value);
    }

    //   向指定树中添加元素
    public Node put(Node node, key key, value value) {
//        如果子树node为空,直接返回新节点
//        其实这个是递归结束的条件之一
        if (node == null) {
            N++;
            return new Node(key, value, null, null);
        }

//        判断如果key大于node.key,向右子树递归
        int compare1 = key.compareTo(node.key);
        if (compare1 > 0) {
//            注意为什么不put(node.right, key, value),还要加上node.right = ?
//            这样才能实现节点节点之间的连接,因为put(node.right, key, value);返回的是node.right
            node.right = put(node.right, key, value);
            //        判断如果key小于node.key,向左子树递归
        } else if (compare1 < 0) {
            node.left = put(node.left, key, value);
            //        判断如果key==node.key,替换这个值
        } else {
            node.value = value;
        }
//        返回这个节点
        return node;
    }

    //    查询某key的值这个是对外调用的方法,从root开始递归,查询元素
//   核心是下面的get(Node node, key key)方法
    public value get(key key){
        return get(root,key);
    }

    //    查询指定子树某key的值
    public value get(Node node, key key){
//        如果为空,无法找到,返回null
        if (node==null){
            return null;
        }
        int compare1 = key.compareTo(node.key);
//        判断如果key大于node.key,向右子树递归
        if (compare1 > 0) {
            return get(node.right,key);
            //        判断如果key小于node.key,向左子树递归
        } else if (compare1 < 0) {
            return get(node.left,key);
            //        判断如果key==node.key,找到这个值
        } else {
            return node.value;
        }
    }

    //    删除操作,比较麻烦的操作,
//    主要是因为删除一个节点后,原本这个节点有左右子树的话,要找一个节点去填充这个节点,
//    找哪个节点去填充呢,找右子数种最小的节点,这个节点能满足大于左子树种中的所有节点,小于右子树的所有节点
//    如果这个节点没有右子树,只有左子树,让上一个节点连接这个节点的下一个节点
//    如果没有左、右子树,直接删除就行了
    public void delete(key key){
        root=delete(root,key);
    }

    //    删除指定数种的key对应的value,并返回删除后的新树
    public Node delete(Node node,key key){
//        如果节点不存在就返回null
        if (node==null){
            return null;
        }

        int compare1 = key.compareTo(node.key);
//        判断如果key大于node.key,向右子树递归
        if (compare1 > 0) {
//            递归,结果返回删除后的新树,让node.right指向返回的新树
            node.right = delete(node.right, key);
            //        判断如果key小于node.key,向左子树递归
        } else if (compare1 < 0) {
//            递归,结果返回删除后的新树,让node.left指向返回的新树
            node.left = delete(node.left, key);
//            判断如果key==node.key,找到了这个要删除的值,之后要找到一个值来填充这个位置
        } else {
            N--;
//            如果没有右子树,返回的新树为当前结点的左子树即可
            if (node.right==null){
                return node.left;
            }
//            如果没有左子树,返回的新树为当前结点的右子树即可
            if (node.left==null){
                return node.right;
            }
//            如果有左子树和右子树,去找右子树中的最小的值
            Node minNode=node.right;
//            一直找左子树就是最小的值,如果没有左子树,那么当前就是最小的值
            while (minNode.left!=null){
                minNode=minNode.left;
            }

//            上面找到了最小节点,现在要删除这个最小节点,然后放到待删除的节点处覆盖
//            怎么删除呢,就是要找到这个节点的上一个节点,让他指向null
//            因此关键就是要找到最小节点的上一个节点
            Node cur=node.right;
            Node pre=node;
//            如果待删除节点的右子树有左节点,那么就去找这个最小的左节点
//            然后让最小节点的上一个节点指向null
            if (cur.left!=null){
                while (cur.left!=null){
                    pre=cur;
                    cur=cur.left;
                }
                pre.left=null;
//                如果待删除节点的右子树没有左节点,这个时候又要分两种情况
//                一种情况是没有左节点且没有右节点,这时让node.right指向null即可
//                另外一种情况是没有左节点但是有右节点,因此要让node.right指向node.right.right
            }else {
                if (cur.right==null){
                    pre.right=null;
                }else {
                    pre.right=cur.right;
                }
            }

//            删除最小元素后,让最小元素左子树为待删除元素的左子树,让最小元素右子树为待删除元素的右子树
            minNode.left=node.left;
            minNode.right=node.right;
//            这个是让node的上一个节点指向这个最小的节点,此时这个最小的节点已经替换了待删除的元素的位置
            node= minNode;
        }
//        这个返回值至关重要,作为这个方法的返回值,
//        用作上一级的node.right = delete(node.right, key)或者node.left = delete(node.left, key)的返回值
        return node;
    }

    //    查找树中最小的key,调用重载方法
    public key getmin(){
        return getmin(root).key;
    }
    //    查找指定树中最小的key的节点
//    递归去找右子树
    public Node getmin(Node node){
        if (node.left!=null){
            return getmin(node.left);
        }else {
            return node;
        }
    }

    //    查找树中最大的key,调用重载方法
    public key getmax(){
        return getmax(root).key;
    }
    //    查找指定树中最大的key的节点
    //    递归去找右子树
    public Node getmax(Node node){
        if (node.right!=null){
            return getmax(node.right);
        }else {
            return node;
        }
    }

    //    前序遍历,顺序:根节点-左子树-右子树
//    这个是对外调用的方法,底层是preErgodic(Node node,Queue<key> keys)方法
    public Queue<key> preErgodic(){
//        创建一个队列,用于装二叉树的所有节点的键
        Queue<key> keys=new Queue<>();
//        调用重载方法
        preErgodic(root,keys);
//        方法运行完毕后,返回队列keys就是二叉树所有的键了
        return keys;
    }
    //    重载方法,这个是核心,期间用到了递归,很重要
    public void preErgodic(Node node,Queue<key> keys){
//        如果为空,直接返回
        if (node==null){
            return;
        }else {
//            先将根节点的键装入队列
            keys.enqueue(node.key);
//            如果有左子树,递归调用这个方法
            if (node.left!=null){
                preErgodic(node.left,keys);
            }
//            如果有右子树,递归调用这个方法
            if (node.right!=null){
                preErgodic(node.right,keys);
            }
        }
    }
    //    中序遍历,顺序:左子树-根节点-右子树
//    这个是对外调用的方法,底层是preErgodic(Node node,Queue<key> keys)方法
    public Queue<key> midErgodic(){
//        创建一个队列,用于装二叉树的所有节点的键
        Queue<key> keys=new Queue<>();
//        调用重载方法
        midErgodic(root,keys);
//        方法运行完毕后,返回队列keys就是二叉树所有的键了
        return keys;
    }
    //    重载方法,这个是核心,期间用到了递归,很重要
    public void midErgodic(Node node,Queue<key> keys){
//        如果为空,直接返回
        if (node==null){
            return;
        }else {
//            如果有左子树,递归调用这个方法
            if (node.left!=null){
                midErgodic(node.left,keys);
            }
            // 根节点的键装入队列
            keys.enqueue(node.key);
//            如果有右子树,递归调用这个方法
            if (node.right!=null){
                midErgodic(node.right,keys);
            }
        }
    }
    //    后序遍历,顺序:左子树-右子树-根节点
//    这个是对外调用的方法,底层是preErgodic(Node node,Queue<key> keys)方法
    public Queue<key> afterErgodic(){
//        创建一个队列,用于装二叉树的所有节点的键
        Queue<key> keys=new Queue<>();
//        调用重载方法
        afterErgodic(root,keys);
//        方法运行完毕后,返回队列keys就是二叉树所有的键了
        return keys;
    }
    //    重载方法,这个是核心,期间用到了递归,很重要
    public void afterErgodic(Node node,Queue<key> keys){
//        如果为空,直接返回
        if (node==null){
            return;
        }else {
//            如果有左子树,递归调用这个方法
            if (node.left!=null){
                afterErgodic(node.left,keys);
            }
//            如果有右子树,递归调用这个方法
            if (node.right!=null){
                afterErgodic(node.right,keys);
            }
            // 根节点的键装入队列
            keys.enqueue(node.key);
        }
    }

    //    层序遍历
    public Queue<key> layerErgodic(){
        //创建2个队列,用于装二叉树的所有节点的键和节点
        Queue<key> keys=new Queue<>();
        Queue<Node> nodes=new Queue<>();
//        先把root根节点放入队列中
        nodes.enqueue(root);
//        开始循环,这里是重点
//        如果nodes队列不为空,弹出一个节点,注意是先进先出的
//        然后将这个节点的键装入keys队列
//        判断如果有左节点,将左节点装入node队列
//        判断如果有右节点,将右节点装入node队列
//        注意每循环一次,其实n都是不一样的
        while(!nodes.isEmpty()){
            Node n=nodes.dequeue();
            keys.enqueue(n.key);
            if (n.left!=null){
                nodes.enqueue(n.left);
            }
            if (n.right!=null){
                nodes.enqueue(n.right);
            }
        }
//        循环完毕后返回整个keys队列
        return keys;
    }

    //    寻找树的最大深度,即从上到下,一条线最多几个节点
//    对外调用方法,核心是maxDepth(Node node)重载方法
    public int maxDepth(){
        return maxDepth(root);
    }
    //    如果node为空,返回0
//    如果有左右节点,递归
    public int maxDepth(Node node){
        if (node==null){
            return 0;
        }
//        先定义三个数,用于记录总深度,左子树深度和右子树深度
        int max=0;
        int maxleft=0;
        int maxright=0;
        if (node.left!=null){
            maxleft=maxDepth(node.left);
        }
        if (node.right!=null){
            maxright=maxDepth(node.right);
        }
//        取最大的一个,+1,就是指定数的深度
        max=Math.max(maxleft,maxright)+1;
//        最后返回总深度
        return max;
    }
}

//测试
public class MyJava {

    public static void main(String[] args) {
        Btree<Integer,String>btree=new Btree();
//        插入元素
        btree.put(6,"刘备");
        btree.put(4,"关羽");
        btree.put(9,"张飞");
        btree.put(1,"曹操");
        btree.put(5,"吕布");
        btree.put(7,"黄忠");
        btree.put(10,"赵云");
        System.out.println("插入完毕后元素的个数为:"+btree.size());

        //        前序遍历
        Queue<Integer> preKeys=btree.preErgodic();
        System.out.print("前序遍历的结果:");
        for (Integer prekeys:preKeys){
            System.out.print(prekeys+",");
        }
        System.out.println();
        System.out.println("--------------------------------");
//        中序遍历
        Queue<Integer> midKeys=btree.midErgodic();
        System.out.print("中序遍历的结果:");
        for (Integer midkeys:midKeys){
            System.out.print(midkeys+",");
        }
        System.out.println();
        System.out.println("--------------------------------");
//        后序遍历
        Queue<Integer> afterKeys=btree.afterErgodic();
        System.out.print("后序遍历的结果:");
        for (Integer afterkeys:afterKeys){
            System.out.print(afterkeys+",");
        }
        System.out.println();
        System.out.println("--------------------------------");
        //层序遍历
        Queue<Integer> layerKeys=btree.layerErgodic();
        System.out.print("层序遍历的结果:");
        for (Integer layerkeys:layerKeys){
            System.out.print(layerkeys+",");
        }
        System.out.println();
        System.out.println("--------------------------------");
//        查询元素
        System.out.println("查询key=4元素:"+btree.get(4));

//        找最小的key
        System.out.println("最小的key:"+btree.getmin());
//        找最大的key
        System.out.println("最大的key:"+btree.getmax());

//        删除元素
        btree.delete(4);
        System.out.println("删除后元素个数:"+btree.size());
        System.out.println("删除后查询4元素:"+btree.get(4));

//        求最大深度
        System.out.println("树最大深度:"+btree.maxDepth());
    }
}

在这里插入图片描述

发布了235 篇原创文章 · 获赞 24 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/weixin_44663675/article/details/105640947