Java数据结构之深度解析二叉树


提示:以下是本篇文章正文内容,Java系列学习将会持续更新

一、基本概念

1-1 什么是二叉树?

 一棵二叉树是结点的一个有限集合,该集合或者为空,或者是由一个根节点加上两棵别称为左子树和右子树的二叉树组成。

二叉树的特点
 每个结点最多有两棵子树,即二叉树不存在度大于 2 的结点。
 二叉树的子树有左右之分,其子树的次序不能颠倒,因此二叉树是有序树。

1-2 两种特殊的二叉树

在这里插入图片描述
满二叉树的性质
 当树的高度为n时,共有(2^n - 1)个节点。
 第i层的节点数是:2^(n - 1)

1-3 二叉树节点的存储

二叉树的存储结构分为:顺序存储和类似于链表的链式存储。
二叉树的链式存储是通过一个个的节点引用起来的,常见的表示方式有二叉和三叉表示方式。

//孩子表示法
class Node {
	int val;   // 数据域
	Node 1eft; // 左孩子的引用,常常代表左孩子为根的整棵左子树
	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
}
//孩子双亲表示法
class Node {
	int val;   // 数据域
	Node 1eft; // 左孩子的引用,常常代表左孩子为根的整棵左子树
	Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
	Node parent; // 当前节点的根节点
}

回到目录…

二、二叉树的递归操作

/**
 * 基础二叉树的操作
 */
public class MyBinTree {
    /**
     * 前序遍历:根左右
     * 传入一颗二叉树的根节点,就可以按照先序遍历的方式来输出节点值
     */
    public static void prevOrder(TreeNode root) {
        //边界条件
        if(root == null){
            return;
        }
        //先输出根节点
        System.out.print(root.val + " ");
        // 按照先序遍历的方式递归访问左子树
        prevOrder(root.left);
        // 按照先序遍历的方式递归访问右子树
        prevOrder(root.right);
    }
    //中序遍历:左根右
    public static void inOrder(TreeNode root) {
        if(root == null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
    //后序遍历:左右根
    public static void postOrder(TreeNode root) {
        if(root == null){
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val + " ");
    }

    /** 返回二叉树的节点数 **/
    public static int getNodes(TreeNode root) {
        if(root == null){
            return 0;
        }
        return 1 + getNodes(root.left) + getNodes(root.right);
    }

    /** 返回二叉树的叶子节点数 **/
    public static int getLeafNodes(TreeNode root) {
        if(root == null){
            return 0;
        }
        if(root.left == null && root.right == null){
            return 1;
        }
        return getLeafNodes(root.left) + getLeafNodes(root.right);
    }

    /** 判断该二叉树是否有val元素 **/
    public static boolean contains(TreeNode root, char val) {
        if(root == null){
            return false;
        }
        if(root.val == val){
            return true;
        }
        return contains(root.left,val) || contains(root.right,val);
    }

    /** 返回该二叉树的深度 **/
    public static int height(TreeNode root) {
        if(root == null){
            return 0;
        }
        return 1 + Math.max(height(root.left),height(root.right));
    }

    /** 返回该二叉树k层的节点数 **/
    public static int getKLevelNodes(TreeNode root,int k) {
        if(root == null || k<=0 || k > height(root)){
            return 0;
        }
        if(k == 1){
            return 1;
        }
        return getKLevelNodes(root.left,k-1) + getKLevelNodes(root.right,k-1);
    }
}

回到目录…

三、二叉树的迭代遍历

例:在这里插入图片描述

3-1 前序遍历 (prevOrder)

根左右;结果集中,第一个遍历的节点就是根节点
牛客地址

public class Solution {
    public int[] preorderTraversal (TreeNode root) {
        if(root == null) {
            return new int[0];
        }
        List<Integer> list = new ArrayList<>();
        // 使用栈
        Deque<TreeNode> stack = new ArrayDeque<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode cur = stack.pop();
            list.add(cur.val);
            // 因为栈先进后出,所以先压入右孩子
            if(cur.right != null){
                stack.push(cur.right);
            }
            if(cur.left != null){
                stack.push(cur.left);
            }
        }
        int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
        return arr;
    }
}

回到目录…

3-2 中序遍历 (inOrder)

左根右;左子树的遍历结果在根节点的左侧,右子树的遍历结果在根节点的右侧
牛客地址

public class Solution {
    public int[] inorderTraversal (TreeNode root) {
        if(root == null) {
            return new int[0];
        }
        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        while(root != null || !stack.isEmpty()) {
            // 每次先找最左节点
            while(root != null){
                stack.push(root);
                root = root.left;
            }
            TreeNode node = stack.pop();
            list.add(node.val);
            // 进入右节点
            root = node.right;
        }
        return list.stream().mapToInt(Integer::intValue).toArray();
    }
}

回到目录…

3-3 后序遍历 (postOrder)

左右根;结果集中,最后一个遍历的节点就是根节点
牛客地址

public class Solution {
    public int[] postorderTraversal (TreeNode root) {
        if(root == null) {
            return new int[0];
        }
        List<Integer> list = new ArrayList<>();
        Deque<TreeNode> stack = new ArrayDeque<>();
        //当前节点
        TreeNode cur = root;
        //上一个完全处理过(左右根)的节点
        TreeNode prev = null;
        while(cur != null || !stack.isEmpty()){
            //先向左走到尽头
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            //此时cur==null,让cur取栈顶元素
            cur = stack.pop();
            //判断右树是否为null或完全处理过的节点
            if(cur.right == null || prev == cur.right){
                list.add(cur.val);
                prev = cur;
                cur = null;
            }else{
                //此时右树不为空且未完全处理过,则继续处理右树
                stack.push(cur);
                cur = cur.right;
            }
        }
        return list.stream().mapToInt(Integer::intValue).toArray();
    }
}

回到目录…

3-4 层序遍历 (levelOrder)

逐层访问,从左到右
牛客地址

public class Solution {
    public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) {
        ArrayList<ArrayList<Integer>> order = new ArrayList<>(); 
        if(root == null){
            return order;
        }
        // 使用队列
        Queue<TreeNode> queue = new ArrayDeque<>();
        queue.offer(root);
        while(!queue.isEmpty()){
            // 统计当前层的元素个数
            int count = queue.size();
            // 建立链表存储当前层的元素
            ArrayList<Integer> list = new ArrayList<>();
            // 将当前层逐个出队加入链表中,并让它们的孩子入队
            for(int i = 0; i < count; i ++) {
                TreeNode cur = queue.poll();
                list.add(cur.val);
                if(cur.left != null){
                    queue.offer(cur.left);
                }
                if(cur.right != null){
                    queue.offer(cur.right);
                }
            }
            order.add(list);
        }
        return order;
    }
}

回到目录…


总结:
提示:这里对文章进行总结:
以上就是今天的学习内容,本文是Java数据结构的学习,认识了二叉树,以及二叉树的相关操作(前序、中序、后序、层序遍历)。之后的学习内容将持续更新!!!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一只咸鱼。。

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

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

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

打赏作者

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

抵扣说明:

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

余额充值