【数据结构和算法15】二叉树的实现

二叉树是这么一种树状结构:每个节点最多有两个孩子,左孩子和右孩子

重要的二叉树结构

  • 完全二叉树(complete binary tree)是一种二叉树结构,除最后一层以外,每一层都必须填满,填充时要遵从先左后右

  • 平衡二叉树(balance binary tree)是一种二叉树结构,其中每个节点的左右子树高度相差不超过 1

1、存储

存储方式分为两种

  1. 定义树节点与左、右孩子引用(TreeNode)

  2. 使用数组,前面讲堆时用过,若以 0 作为树的根,索引可以通过如下方式计算

    • 父 = floor((子 - 1) / 2)

    • 左孩子 = 父 * 2 + 1

    • 右孩子 = 父 * 2 + 2

二叉树的节点结构

java 复制代码
/**
 * 二叉树节点
 *
 * @author zjj_admin
 */
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;
​
    public TreeNode(int val) {
        this.val = val;
    }
​
    public TreeNode(TreeNode left, int val, TreeNode right) {
        this.left = left;
        this.val = val;
        this.right = right;
    }
}

2、遍历

遍历也分为两种

  1. 广度优先遍历(Breadth-first order):尽可能先访问距离根最近的节点,也称为层序遍历

  2. 深度优先遍历(Depth-first order):对于二叉树,可以进一步分成三种(要深入到叶子节点)

    1. pre-order 前序遍历,对于每一棵子树,先访问该节点,然后是左子树,最后是右子树

    2. in-order 中序遍历,对于每一棵子树,先访问左子树,然后是该节点,最后是右子树

    3. post-order 后序遍历,对于每一棵子树,先访问左子树,然后是右子树,最后是该节点

2.1、广度优先

本轮开始时队列 本轮访问节点
[1] 1
[2, 3] 2
[3, 4] 3
[4, 5, 6] 4
[5, 6] 5
[6, 7, 8] 6
[7, 8] 7
[8] 8
[]
  1. 初始化,将根节点加入队列

  2. 循环处理队列中每个节点,直至队列为空

  3. 每次循环内处理节点后,将它的孩子节点(即下一层的节点)加入队列

注意

  • 以上用队列来层序遍历是针对 TreeNode 这种方式表示的二叉树

  • 对于数组表现的二叉树,则直接遍历数组即可,自然为层序遍历的顺序

2.2、深度优先遍历

深度优先遍历有前序遍历、中序遍历和后续遍历三种

  1. 前序遍历: 先输出父节点,再遍历左子树和右子树

  2. 中序遍历: 先遍历左子树,再输出父节点,再遍历右子树

  3. 后序遍历: 先遍历左子树,再遍历右子树,最后输出父节点

  • 小结: 看输出父节点的顺序,就确定是前序,中序还是后序

前,中,后序遍历详解

  1. 创建一颗二叉树

  2. 前序遍历 2.1 先输出当前节点(初始的时候是root节点) 2.2 如果左子节点不为空,则递归继续前序遍历 2.3 如果右子节点不为空,则递归继续前序遍历

  3. 中序遍历 3.1 如果当前节点的左子节点不为空,则递归中序遍历 3.2 输出当前节点 3.3 如果当前节点的右子节点不为空,则递归中序遍历

  4. 后序遍历 4.1 如果当前节点的左子节点不为空,则递归后序遍历 4.2 如果当前节点的右子节点不为空,则递归后序遍历 4.3 输出当前节点

2.3、递归实现深度优先遍历

java 复制代码
/**
 * 前序遍历,继续递归实现
 *
 * @param root
 */
static void preOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    System.out.print(root.val + "\t");
    preOrder(root.left);
    preOrder(root.right);
}
​
​
/**
 * 中序遍历
 *
 * @param root
 */
static void inOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    inOrder(root.left);
    System.out.print(root.val + "\t");
    inOrder(root.right);
}
​
​
/**
 * 后续遍历
 *
 * @param root
 */
static void postOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    postOrder(root.left);
    postOrder(root.right);
    System.out.print(root.val + "\t");
}

2.4、非递归实现深度优先遍历

java 复制代码
   /**
     * 前序遍历
     * 使用非递归的方式
     *
     * @param root
     */
    static String preOrder(TreeNode root) {
        LinkedList<TreeNode> stack = new LinkedList();
        StringBuilder res = new StringBuilder();
        TreeNode curr = root;
        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                res.append(curr.val).append(" ");
                stack.push(curr);
                curr = curr.left;
            } else {
                //弹栈
                TreeNode pop = stack.pop();
                curr = pop.right;
            }
        }
        return res.toString();
    }
​
​
    /**
     * 中序遍历
     * 使用非递归的方式
     *
     * @param root
     */
    static String inOrder(TreeNode root) {
        LinkedList<TreeNode> stack = new LinkedList();
        StringBuilder res = new StringBuilder();
        TreeNode curr = root;
        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                stack.push(curr);
                curr = curr.left;
            } else {
                //弹栈
                TreeNode pop = stack.pop();
                res.append(pop.val).append(" ");
                curr = pop.right;
            }
        }
        return res.toString();
    }
​
​
    /**
     * 后续遍历
     * 使用非递归的方式
     *
     * @param root
     */
    static String postOrder(TreeNode root) {
        LinkedList<TreeNode> stack = new LinkedList();
        StringBuilder res = new StringBuilder();
        TreeNode curr = root;
        TreeNode pop = null;
        while (curr != null || !stack.isEmpty()) {
            if (curr != null) {
                stack.push(curr);
                curr = curr.left;
            } else {
                TreeNode peek = stack.peek();
                if (peek.right == null || peek.right == pop) {
                    //弹栈
                    pop = stack.pop();
                    res.append(pop.val).append(" ");
                } else {
                    curr = peek.right;
                }
            }
        }
        return res.toString();
    }

2.5、在一个方法中实现二叉树的三种深度优先遍历(前序、中序和后续)

java 复制代码
/**
 * 使用非递归的方式求解,在一个方法中实现
 * 实现前序遍历,中序遍历和后续遍历
 *
 * @param root
 */
static List<String> order(TreeNode root) {
    TreeNode curr = root;
    StringBuilder pre = new StringBuilder("preOrder:");
    StringBuilder in = new StringBuilder("inOrder:");
    StringBuilder post = new StringBuilder("postOrder:");
    //定义一个栈,用于存储当前节点的父节点
    LinkedList<TreeNode> s = new LinkedList();
    TreeNode pop = null;
    while (curr != null || !s.isEmpty()) {
        if (curr != null) {
            s.push(curr);
            pre.append(curr.val + " ");
            //依次向左边遍历
            curr = curr.left;
        } else {
            TreeNode peek = s.peek();
            if (peek.right == null) {
                in.append(peek.val + " ");
                //当没有右节点时
                pop = s.pop();
                //这一行打印的是中序遍历的结果
                post.append(pop.val + " ");
            } else if (peek.right == pop) {
                //当右节点已经遍历结束时
                pop = s.pop();
                //这一行打印的是中序遍历的结果
                post.append(pop.val + " ");
            } else {
                //右节点不为空并且没有遍历
                in.append(peek.val + " ");
                curr = peek.right;
            }
        }
    }
    return List.of(pre.toString(), in.toString(), post.toString());
}
相关推荐
Sheep Shaun3 分钟前
C++11核心特性详解:从右值引用到现代C++编程
开发语言·数据结构·c++·算法
云深麋鹿18 分钟前
三.栈和队列
开发语言·数据结构·c++·算法
爆打维c24 分钟前
01BFS算法(例题:网格传送门旅游)
c语言·c++·python·算法·leetcode·广度优先
像素猎人31 分钟前
力扣:面试题16.01.交换数字
c++·算法·leetcode·面试
小O的算法实验室31 分钟前
2024年ASOC SCI2区TOP,异构 pbest 引导的综合学习粒子群算法,深度解析+性能实测
算法·论文复现·智能算法·智能算法改进
Python_Study202534 分钟前
工程材料企业如何通过智慧获客软件破解市场困局:方法论、架构与实践
大数据·网络·数据结构·人工智能·架构
AI科技星1 小时前
从质能关系到时空几何:光速飞行理论的框架对比与逻辑验证
服务器·人工智能·线性代数·算法·矩阵
CSDN_RTKLIB1 小时前
C++仿函数
c++·算法·stl
学嵌入式的小杨同学1 小时前
【嵌入式 C 语言高频考点】周测 + 期中真题解析:从基础语法到编程实战
c语言·数据结构·数据库·vscode·算法·面试
沉默-_-1 小时前
力扣hot100双指针专题解析2(C++)
java·c++·算法·蓝桥杯·双指针