二叉树浅析


一、什么是二叉树

二叉树是一种特殊的树,那么特殊之处在哪呢:

  • 每个结点最多只能有两个子结点(后文中我用孩子来代替子结点)
    在这里插入图片描述

  • 二叉树是有序树(孩子的前后关系才是我们需要注意的)
    在这里插入图片描述
    这里我们要知道左图中:B是A的左孩子;右图中:C是A的左孩子。


二叉树的所有形态

在这里插入图片描述
上图给出了几种特殊的二叉树形态,从左往右依次是:空树只有根节点的二叉树节点只有左子树节点只有右子树节点的左右子树均存在,一般二叉树都是由上述基本形态结合而形成的。


二、满二叉树

一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数是2^k-1,则它就是满二叉树。
在这里插入图片描述


三、完全二叉树

对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。

注意:满二叉树是一种特殊的完全二叉树
在这里插入图片描述


四、二叉树的遍历

4.1 树的创建

为了方便后续代码的实现,需要先构建二叉树,本篇博客代码以如图所示二叉树实现。
在这里插入图片描述

public class BuildTree {
    public static TreeNode buildTree1() {
        TreeNode a = new TreeNode('A');
        TreeNode b = new TreeNode('B');
        TreeNode c = new TreeNode('C');
        TreeNode d = new TreeNode('D');
        TreeNode e = new TreeNode('E');
        TreeNode f = new TreeNode('F');
        TreeNode g = new TreeNode('G');
        TreeNode h = new TreeNode('H');
        TreeNode i = new TreeNode('I');
        TreeNode j = new TreeNode('J');
        a.left = b;
        a.right = c;
        b.left = d;
        d.right = g;
        g.left = j;
        c.left = e;
        c.right = f;
        f.left = h;
        f.right = i;
        return a;
    }
}

4.2 深度优先搜索算法DFS

深度优先搜索算法(Depth-First-Search,缩写为 DFS),是一种利用递归实现的搜索算法。

首先我们要明确是怎么遍历二叉树的。二叉树的前中后序遍历,真的真的很好理解,以下图为例:
在这里插入图片描述

从根结点出发的箭头,沿着虚线遍历整个二叉树,最终回到了根节点。我们会发现,在遍历途中,每个结点其实被经过了三次前序遍历就是第一次碰到结点的顺序(标号1),中序遍历就是第二次碰到结点的顺序(标号2),后序遍历就是第三次碰到结点的顺序(标号3)。这样讲是不是很容易理解。

所以我们明确了前 / 中 / 后序遍历顺序的不同之处:
前序遍历:【根】【左子树】【右子树】
中序遍历:【左子树】【根】【右子树】
后序遍历:【左子树】【右子树】【根】

了解到了这些,对于后文的代码就清晰明了了。


前 / 中 / 后序遍历(递归实现)

public class TreeTraversal {
    /**
     *前序遍历
     */
    public static void preTraversal(TreeNode root) {
        //处理前提
        if(root != null) {
            //处理根节点
            System.out.print(root.value);
            //根据前序的方式,递归处理该结点的左子树
            preTraversal(root.left);
            //根据前序的方式,递归处理该结点的右子树
            preTraversal(root.right);
        }
    }
    
    /**
     *中序遍历
     */
    public static void inTraversal(TreeNode root) {
        if(root != null) {
            inTraversal(root.left);
            System.out.print(root.value);
            inTraversal(root.right);
        }
    }
    
    /**
     *后序遍历
     */
    public static void postTraversal(TreeNode root) {
        if(root != null) {
            postTraversal(root.left);
            postTraversal(root.right);
            System.out.print(root.value);
        }
    }
    
   public static void main(String[] args) {
        TreeNode root = BuildTree.buildTree1();
        System.out.println("前序遍历:");
        preTraversal(root);
        System.out.println();
        System.out.println("中序遍历:");
        inTraversal(root);
        System.out.println();
        System.out.println("后序遍历:");
        postTraversal(root);
    }
}

class TreeNode {
    char value;
    TreeNode left;
    TreeNode right;

    public TreeNode(char value) {
        this.value = value;
    }
}

运行结果:
在这里插入图片描述

前 / 中 / 后序遍历(非递归实现)

/**
     * 前序遍历
     */
    public static void preOrder(TreeNode root) {
        Deque<TreeNode> stacks = new LinkedList<>();
        TreeNode current = root;

        while (!stacks.isEmpty() || current != null) {
            while (current != null) {
                System.out.print(current.value);
                stacks.push(current);
                current  =current.left;
            }
            TreeNode top = stacks.pop();
            current = top.right;
        }
    }

    /**
     * 中序遍历
     */
    public static void inOrder(TreeNode root) {
        Deque<TreeNode> stacks = new LinkedList<>();
        TreeNode current = root;

        while (!stacks.isEmpty() || current != null) {
            while (current != null) {
                stacks.push(current);
                current  =current.left;
            }
            TreeNode top = stacks.pop();
            System.out.print(top.value);
            current = top.right;
        }
    }

    /**
     * 后序遍历
     */
    public static void postOrder(TreeNode root) {
        Deque<TreeNode> stacks = new LinkedList<>();
        TreeNode current = root;
        TreeNode last = null;

        while (!stacks.isEmpty() || current != null) {
            while (current != null) {
                stacks.push(current);
                current  =current.left;
            }
            TreeNode top = stacks.peek();
            if(top.right == null) {
                stacks.pop();
                last = top;
                System.out.print(top.value);
            } else if(top.right == last) {
                stacks.pop();
                last = top;
                System.out.print(top.value);
            } else {
                current = top.right;
            }
        }
    }
    
    public static void main(String[] args) {
        TreeNode root = BuildTree.buildTree1();
        System.out.println("前序遍历:");
        preOrder(root);
        System.out.println();
        System.out.println("中序遍历:");
        inOrder(root);
        System.out.println();
        System.out.println("后序遍历:");
        postOrder(root);
    }
}

运行结果:
在这里插入图片描述


4.3 广度优先搜索算法BFS

广度优先搜索算法(Breadth-First-Search,缩写为 BFS),是一种利用队列实现的搜索算法。

层序遍历

是否是完全二叉树

层序遍历和层号

import java.util.LinkedList;
import java.util.Queue;

public class TreeLevelOrder {
    /**
     * 层序遍历
     */
    public static void levelTraversal(TreeNode root) {
        if(root == null) {
            return;
        }
        //队列的元素类型是树的结点
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);

        while (!queue.isEmpty()) {
            TreeNode node = queue.remove();
            //这个node就是我们层序遍历经过的结点
            System.out.print(node.value);

            if(node.left != null) {
                queue.add(node.left);
            }

            if(node.right != null) {
                queue.add(node.right);
            }
        }
    }

    /**
     * 是否是完全二叉树
     */
    public static boolean isCompleteTree(TreeNode root){
        if(root == null || (root.left == null && root.right == null)){
            return true;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        TreeNode node = null;
        while(!queue.isEmpty()){
            node = queue.peek();
            if(node.left != null){
                queue.add(node.left);
            }else{
                break;
            }
            if(node.right != null){
                queue.add(node.right);
            }else{
                break;
            }
            queue.poll();
        }
        if(queue.peek().right == null){
            queue.poll();
        }
        while(!queue.isEmpty()){
            node = queue.poll();
            if(node.left != null || node.right != null){
                return false;
            }
        }
        return true;
    }

    /**
     * 层序遍历+层数
     */
    public static void levelOrderWithLevel(TreeNode root) {
        if(root == null) {
            return;
        }
        Queue<NL> queue = new LinkedList<>();
        queue.add(new NL(root,1));

        while (!queue.isEmpty()) {
            NL nl = queue.remove();
            TreeNode node = nl.node;
            int level = nl.level;

            System.out.print(level);
            System.out.print(node.value);

            if(node.left != null) {
                queue.add(new NL(node.left,level+1));
            }

            if(node.right != null) {
                queue.add(new NL(node.right,level+1));
            }
        }
    }

    static class NL {
        TreeNode node;
        int level;

        NL(TreeNode node,int level) {
            this.node = node;
            this.level = level;
        }
    }

    public static void main(String[] args) {
        TreeNode root = BuildTree.buildTree1();
        System.out.print("层序遍历:");
        levelTraversal(root);
        System.out.println();
        System.out.print("层序遍历和层号:");
        levelOrderWithLevel(root);
        System.out.println();
        System.out.println("是否是完全二叉树:"+isCompleteTree(root));
    }
}

运行结果:
在这里插入图片描述


五、问题求解

二叉树中结点个数

二叉树中叶子结点个数

二叉树第K层的结点个数

二叉树的高度

二叉树中是否包含某个值

二叉树中包含某值所在的节点

结点是不是以root为根的二叉树上的结点

public class SomeMethod {
    private static int n = 0;

    /**
     * 返回root为根的树中,有多少个结点
     */
    private static void preOrder(TreeNode root) {
        //1.一个树的结点 2.没有结点
        if(root != null) {
            n++;
            preOrder(root.left);
            preOrder(root.right);
        }
    }

    public static int sumTreeNodeSize(TreeNode root) {
        preOrder(root);
        return n;
    }

    public static int sumTreeNodeSize2(TreeNode root) {
        if(root != null) {
            int rootNodeSize = 1;
            int leftSubTreeNodeSize = sumTreeNodeSize2(root.left);
            int rightSubTreeNodeSize = sumTreeNodeSize2(root.right);
            return rightSubTreeNodeSize+leftSubTreeNodeSize+rootNodeSize;
        }
        return 0;
    }

    /**
     * 求叶子结点个数
     */
    private static int m = 0;

    private static void preOrder2(TreeNode root) {
        if(root != null) {
            if(root.left == null && root.right == null) {
                m++;
            }
            preOrder2(root.left);
            preOrder2(root.right);
        }
    }

    public static int sumTreeLeafNodeSize(TreeNode root) {
        m = 0;//保证下次打印
        preOrder2(root);
        return m;
    }

    public static int sumTreeLeafNodeSize2(TreeNode root) {
        if(root == null) {
            return 0;
        } else if(root.left == null && root.right == null) {
            return 1;
        } else {
            int leftSubTreeNodeSize = sumTreeLeafNodeSize2(root.left);
            int rightSubTreeNodeSize = sumTreeLeafNodeSize2(root.right);
            return rightSubTreeNodeSize + leftSubTreeNodeSize;
        }
    }

    /**
     *求二叉树第K层的结点个数
     */
    public static int sumKLevelNodeSize(TreeNode root,int k) {
        if(root == null) {
            return 0;
        } else if(k == 1) {
            return 1;
        } else {
            int leftSubTreeK_1 = sumKLevelNodeSize(root.left,k-1);
            int rightSubTreeK_1 = sumKLevelNodeSize(root.right,k-1);
            return leftSubTreeK_1 + rightSubTreeK_1;
        }
    }

    /**
     * 求二叉树的高度
     */
    public static int getHeight(TreeNode root) {
        if(root == null) {
            return 0;
        } else if(root.left == null && root.right == null) {//可以和下面else合并
            return 1;
        } else {
            int leftTreeHeight = getHeight(root.left);
            int rightTreeHeight = getHeight(root.right);
            return Math.max(leftTreeHeight,rightTreeHeight)+1;
        }

    }

    /**
     * 二叉树中是否包含某个数
     */
    public static boolean containsTree(TreeNode root,char v) {
        if(root == null) {
            return false;
        } else {
            if(root.value == v) {
                return true;
            } else {
                boolean leftSubTreeContains = containsTree(root.left, v);
                if(leftSubTreeContains) {
                    return true;
                } else {
                    boolean rightSubTreeContains = containsTree(root.right,v);
                    if(rightSubTreeContains) {
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        }
    }

    public static boolean containsTree2(TreeNode root,char v) {
        if(root == null) {
            return false;
        }

        if(root.value == v) {
            return true;
        }

        boolean left = containsTree2(root.left,v);
        if(left) {
            return true;
        }
        return containsTree2(root.right,v);
    }

    /**
     * 返回包含v所在的节点
     */
    public static TreeNode containsTree3(TreeNode root,char v) {
        if(root == null) {
            return null;
        }

        if(root.value == v) {
            return root;
        }

        TreeNode leftContains = containsTree3(root.left, v);
        if(leftContains != null) {
            return leftContains;
        }
        return containsTree3(root.right,v);
    }

    /**
     * 返回node是不是以root为根的二叉树上的一个结点
     * 空树一律返回false,即使找的是null
     */
    public static boolean containsTree4(TreeNode root,TreeNode node) {
        if(root == null) {
            return false;
        }
        if(root == node) {
            return true;
        }
        boolean leftVontains = containsTree4(root.left, node);
        if(leftVontains) {
            return true;
        }

        return containsTree4(root.right,node);
    }

    public static void main(String[] args) {
        TreeNode root = BuildTree.buildTree1();
        System.out.println("二叉树的结点个数:"+sumTreeNodeSize(root));//10
        System.out.println("二叉树的结点个数:"+sumTreeNodeSize2(root));//10
        System.out.println("二叉树的叶子结点个数:"+sumTreeLeafNodeSize(root));//4
//        System.out.println("二叉树的叶子结点个数:"+sumTreeLeafNodeSize(root));//8
        System.out.println("二叉树的叶子结点个数:"+sumTreeLeafNodeSize2(root));//4
        System.out.println("二叉树的第一层有:"+sumKLevelNodeSize(root, 1)+"个结点");
        System.out.println("二叉树的第四层有:"+sumKLevelNodeSize(root, 4)+"个结点");
        System.out.println("二叉树的高度:"+getHeight(root));
        System.out.println("二叉树中是否包含F:"+containsTree(root,'F'));
        System.out.println("二叉树中是否包含F:"+containsTree2(root,'F'));
        System.out.println("二叉树中包含F的结点:"+containsTree3(root, 'F').value);
        System.out.println("结点是否是以root为根的二叉树上的节点:"+containsTree4(root, root));
    }
}

运行结果:
在这里插入图片描述


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值