• 数据结构:二叉树(1)


    目录

    树的概念

    树的表示形式

    二叉树

    二叉树的性质

    题目

    二叉树的存储

    链式存储

    初始化二叉树

    二叉树的遍历

    前序遍历:根👉左子树👉右子树

     中序遍历:左子树👉根👉右子树

    后序遍历:左子树👉右子树👉根

    选择题

    代码代码!

    前序遍历的存储问题 


    树的概念

    树是一种非线性的数据结构,是由nn>=0)个有限结点组成一个具有层次关系的集合

    它像是一颗倒挂的树,即根朝上,叶(结点)朝下

    注意:

    除了根结点外,每个节点有且只有一个父结点 

    一棵n个结点的树由n-1条边


    结点的度:一个结点含有子树的个数,上面A的度就是6

    树的度:所有结点度的最大值(max(node degree)),上面树的度就是6

    叶结点/终端结点:度为0的结点,上面B,C,H,I,K,L,M,N,P,Q就是叶结点

    父结点:一个结点如果有条线连着上面一个结点,那上面这个结点就是这个结点的父结点

    比如:A是B的父结点

    子节点:结点含有的子树的根结点,B是A的子结点

    根结点:没有父结点的结点

    结点层次:根是第一层,其子结点是第2层。。。。

    树的高度:树结点最大层次,树高度为4

    分支结点:度不为0的结点,比如:D,E,J....

    兄弟结点:有相同父结点的结点

    堂兄弟结点:双亲在同一层的结点互为堂兄弟,如H,I就是堂兄弟结点


    树的表示形式

    孩子兄弟表达法

    文字形式:一般题目里面用二叉树的根结点来表示整个二叉树

    为啥要这么设定?运用了什么修辞手法?😊

    因为一个root就可以去到二叉树的任何一个节点。

    借代:以局部表示整体


    二叉树

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

    特点:

    二叉树不存在度大于2的结点,所以他的每颗子树都是二叉树

    满二叉树:每层结点树都达到最大值。如果一棵二叉树的层数为k,且结点总数是2^k-1,那它就是一棵满二叉树

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

    满二叉树是一种特殊的完全二叉树

    二叉树的性质

    1.若规定的根结点层数是1,那一棵非空二叉树的第i层上最多有2^(i-1)个结点

    2.规定只有根结点的二叉树深度为1,则深度为k的二叉树最大结点数是2^k -1

    3.对于任意一棵二叉树,如果叶结点个数为n0,度为2的非叶结点个数为n2,则有n0 = n2+1

    换句话说,叶结点(度为0)的个数总是要比度为2的结点个数多1个

    如上图,叶结点的个数有6个,度为2的结点个数为5个 5+1 = 6

    推导公式

    等式1:假设一棵树有n个结点,度为0的有n0个,度为1的有n1个,度为2的有n2个,那么

    n = n0 + n1 + n2

    等式2:一棵n个结点的树有n-1条边

    一般的,度为0的结点不会产生边,度为1的结点(n1个)产生n1 * 1条边,度为2的结点(n2个)产生

    n2 * 2 条边

    那么 n-1 = n1+2*n2

    把等式1和2联立,n0+n1+n2-1 = n1 + 2 * n2  -- >   n0 = n2 + 1

    4.具有n个结点的完全二叉树的深度k为上取整

    5. 对于具有 n 个结点的完全二叉树 ,如果按照 从上至下从左至右的顺序对所有节点从 0 开始编号 ,则对于 序号为 i 的结点有
    i>0 双亲序号: (i-1)/2 i=0 i 为根结点编号 ,无双亲结点
    2i+1 ,左孩子序号: 2i+1 ,否则无左孩子
    2i+2 ,右孩子序号: 2i+2 ,否则无右孩子

    假设父结点下标是i,则左孩子下标为 2*i+1,右孩子下标是2*i+2

    反推,如果孩子下标为i,则父结点下标是(i-1) / 2


    题目

    偶数个结点的完全二叉树,度为1的结点一定只有1个

    而奇数个结点的,度为1的结点为0个

    所以可以列个等式

    2n = n0 + 1 + n2 = n0 + 1 + n0 - 1

    所以 n0 = n      选A


    二叉树的存储

    分为顺序存储链式存储

    顺序存储:堆(可以看看我后面的博客),这篇博客讲链式存储

    链式存储


    初始化二叉树

    目前的思路:

    先创建结点,以穷举的方式造一棵二叉树,根据下面这张图来创建

    1. public class BinaryTree {
    2. static class TreeNode{
    3. public char val;
    4. public TreeNode left;
    5. public TreeNode right;
    6. public TreeNode(char val) {
    7. this.val = val;
    8. }
    9. }
    10. public TreeNode root;
    11. //创建一棵二叉树,创建成功后返回根结点
    12. public TreeNode createTree(){
    13. TreeNode A = new TreeNode('A');
    14. TreeNode B = new TreeNode('B');
    15. TreeNode C = new TreeNode('C');
    16. TreeNode D = new TreeNode('D');
    17. TreeNode E = new TreeNode('E');
    18. TreeNode F = new TreeNode('F');
    19. TreeNode G = new TreeNode('G');
    20. TreeNode H = new TreeNode('H');
    21. A.left = B;
    22. A.right = C;
    23. B.left = D;
    24. B.right = E;
    25. C.left = F;
    26. C.right = G;
    27. E.right = H;
    28. return A;
    29. }
    30. }

    测试一下

    煤油问题😊

    二叉树的遍历

    遍历指的是沿着某条路线遍历

    前序遍历:根👉左子树👉右子树

    遇到根就打印

     中序遍历:左子树👉根👉右子树

    后序遍历:左子树👉右子树👉根

    留一道题,请写出下图的前序,中序和后序遍历(答案在文章末尾)

    选择题

    首先把这个树画出来

    画出来后就很简单了,答案就是A

    怎么画出这棵树?

     根结点就是E

    注意:后序遍历是可以确定树的根的,就是最后一个字母a

    那放到中序遍历中,a的左边b就是左子树,a的右边dce构成右子树

    后序遍历里面,a后面的c是一个子树根,根据中序,那d和e就很自然排在c的左和右了

     

    画出来就是这样,前序遍历就是abcde

    后序遍历确定根是F,那根据中序遍历F前面的ABCDE构成左子树,没有右子树

    F后面每一个元素都是前一个元素的根结点,画出来就是这样

    层次输出FEDCBA

    问题:如果给你前序遍历和后序遍历,可以画出一棵二叉树吗?

    不可以。因为前序和后序确定的都是根

    代码代码!

    1. // 前序遍历
    2. void preOrder(TreeNode root){
    3. if(root == null){
    4. return;
    5. }
    6. System.out.println(root.val + " ");
    7. preOrder(root.left);
    8. preOrder(root.right);
    9. }

    有点难理解?其实这段代码的分析过程跟我们在造树的过程差不多 

    (图太大了只能截一部分了...)

    剩下的俩就很简单了

    1. // 中序遍历
    2. void inOrder(TreeNode root){
    3. if(root == null){
    4. return;
    5. }
    6. inOrder(root.left);
    7. System.out.println(root.val + " ");
    8. inOrder(root.right);
    9. }
    10. // 后序遍历
    11. void postOrder(TreeNode root){
    12. if(root == null){
    13. return;
    14. }
    15. postOrder(root.left);
    16. postOrder(root.right);
    17. System.out.println(root.val + " ");
    18. }
    前序遍历的存储问题 

    144. 二叉树的前序遍历 - 力扣(LeetCode)

    给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

    遍历思路:遍历到是我就存储进去

    1. class Solution {
    2. List list = new ArrayList<>();
    3. public List preorderTraversal(TreeNode root) {
    4. if(root == null){
    5. return list;
    6. }
    7. list.add(root.val);
    8. preorderTraversal(root.left);
    9. preorderTraversal(root.right);
    10. return list;
    11. }
    12. }

    套娃存列表思想

    1. public List preorderTraversal(TreeNode root) {
    2. List list = new ArrayList<>();
    3. if(root == null){
    4. return list;
    5. }
    6. list.add(root.val);
    7. List leftTree = preorderTraversal(root.left);
    8. list.addAll(leftTree);
    9. List rightTree = preorderTraversal(root.right);
    10. list.addAll(rightTree);
    11. return list;
    12. }

    画个图解释一下(只画了左子树) 

    每次返回就把拼接好的列表归到父结点的列表中


    图片遍历答案

    前序:ABDEHCFG

    中序:DBEHAFCG

    后序:DHEBFGCA

  • 相关阅读:
    新手如何快速参与开源项目
    如何使用前端表格控件实现数据更新?
    企业软文怎么写?教你搞定各种类型软文
    深度学习实战52-基于医疗大模型与医疗智能诊断问答的运用研究
    嵌入式系统-开关机测试笔记
    springboot使用Freemark生成动态页面工具
    使用 Spring Cloud Loadbalancer 实现客户端负载均衡
    常见的编码及哈希算法
    Android14 WMS启动流程
    搜索与图论 ---- 匈牙利算法
  • 原文地址:https://blog.csdn.net/hellg/article/details/133855737