树和二叉树

本文详细介绍了树和二叉树的概念,包括树的相关术语和二叉树的特性。讨论了满二叉树和完全二叉树,并列举了二叉树的性质。此外,还阐述了二叉树的链式存储实现、创建、遍历(前序、中序、后序及层次遍历)以及基本操作如获取节点数量、叶子节点数、高度等。最后提到了二叉树的相关练习,涉及树的比较、子树、翻转、平衡性、对称性等话题。
摘要由CSDN通过智能技术生成

树和二叉树

一、树型结构

1.1概念

树是一种非线性数据结构,是由n(n>=0)个有限节点组成的一个有层次关系的集合。把它叫做树是因为其形状长得像一棵倒挂的树,根在上,叶子在下。有以下特点:

  • 有一个特殊的节点,称为根节点,根没有前驱节点
  • 树是递归定义的

image-20221126195148743

注意:在树形结构中,子树之间是不能有交集的,否则不能构成树形结构.

1.2树相关概念

  • 结点的度:一个结点含有子树的个数(简单可以理解为,每个结点向下的分支数);
  • 树的度:一棵树中,所有结点度的最大值;
  • 叶子结点(终端结点):度为0的结点;
  • 双亲结点(父节点):若一个结点含有子结点,则成这个结点为子结点的父结点;
  • 孩子结点(子结点):一个结点含有子树的根结点称为该结点的子节点(与双亲结点概念相似);
  • 根结点:一棵树中,没有双亲结点的结点;
  • 结点的层次:从根节点开始定义,根为第一层,根的子结点为第二层,以此类推;
  • 树的高度或深度:树中结点的最大层次;

二、二叉树

2.1概念

一棵二叉树是节点的一个有限集合,该集合:

  1. 或者为空
  2. 或者是由一个根节点加两棵称为左子树和右子树的二叉树组成。

image-20221126191653029

  1. 二叉树不存在度大于2的结点
  2. 二叉树的子树左右次序不能调换,二叉树是有序树
  3. 二叉树可以只由左子树、右子树、根结点或者空树构成

2.2两种特殊的二叉树

  1. 满二叉树:一棵二叉树,如果每层的结点数达到最大值,则这棵二叉树称为满二叉树。一棵满二叉树的层数为k,总结点的个数为 2 k − 1 2^k-1 2k1.
  2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树引出来的。

image-20221126192852938

2.3二叉树的性质

  1. 若根结点所在的层数为1,则一棵二叉树第i层上最多有 2 i − 1 2^{i-1} 2i1(i>0)个结点
  2. 若只有根结点的二叉树的深度为1,则深度为k的二叉树最大结点数位 2 k − 1 2^k-1 2k1(k>=0)
  3. 任何一棵二叉树, 如果其叶结点个数为 n 0 n_0 n0, 度为2的非叶结点个数为 n 2 n_2 n2,则有 n 0 n_0 n0 n 2 n_2 n2+1
  4. 具有n个结点的完全二叉树的深度k为 l o g 2 ( n + 1 ) log_2{(n+1)} log2(n+1) 上取整
  5. 对于具有n个结点的完全二叉树,如果按照从上至下从左至右的顺序对所有节点从0开始编号,则对于序号为i
    的结点有:
    若i>0,双亲序号: ( i − 1 ) / 2 (i-1)/2 (i1)/2;i=0,i为根结点编号,无双亲结点
    若2i+1<n,左孩子序号:2i+1,否则无左孩子
    若2i+2<n,右孩子序号:2i+2,否则无右孩子

2.4二叉树的实现

二叉树的存储分为顺序存储类链表的链式存储

这里我们通过链式存储实现

 // 定义一个表示节点的类
     class TreeNode {
        int value; // 当前节点的值
        TreeNode left;  // 左子树的引用
        TreeNode right; // 右子树的引用
        // 构造方法

        public TreeNode(int value) {
            this.value = value;
        }
    }
// 根节点的引用
public TreeNode root;
	

2.5二叉树的操作

1.二叉树的创建

这里我们手动创建一个二叉树

    public void create() {
        // 先所有的节点创建出来
        TreeNode node1 = new TreeNode(1);
        TreeNode node2 = new TreeNode(2);
        TreeNode node3 = new TreeNode(3);
        TreeNode node4 = new TreeNode(4);
        TreeNode node5 = new TreeNode(5);
        TreeNode node6 = new TreeNode(6);
        TreeNode node7 = new TreeNode(7);
        TreeNode node8 = new TreeNode(8);
        // 处理引用关系
        node1.left = node2;
        node1.right = node3;
        node2.left = node4;
        node2.right = node5;
        node5.right = node8;
        node3.left = node6;
        node3.right = node7;
        // 指定根节点的引用
        root = node1;
    }

image-20221126123236452

2.二叉树的遍历

1.前中后序遍历

二叉树的遍历是指沿着某条搜索路线,对树中每个结点均做一次且只访问一次。

二叉树的遍历是最重要的基础操作之一,是二叉树的其他运算的基础

区别:

  1. 前序遍历:访问根结点–>根的左子树–>根的右子树(根–>左–>右)
  2. 中序遍历:左–>根–>右
  3. 后序遍历:左–>右–>根

根据二叉树的遍历还原二叉树

1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为()
A: ABDHECFG B: ABCDEFGH C: HDBEAFCG D: HDEBFGCA
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()
A: E B: F C: G D: H
3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为()
A: adbce B: decab C: debac D: abcde
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为()
A: FEDCBA B: CBAFED C: DEFCBA D: ABCDEF

还原思路

image-20221126192945295

注意:当给出二叉树的前序和后序遍历时将无法还原二叉树

// 前序遍历
public void preOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    System.out.print(root.value + " ");
    preOrder(root.left);
    preOrder(root.right);
}
// 中序遍历
public void inOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    inOrder(root.left);
    System.out.print(root.value + " ");
    inOrder(root.right);
}
// 后序遍历
public void postOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    inOrder(root.left);
    inOrder(root.right);
    System.out.print(root.value + " ");
}	  

遍历的过程:

image-20221126134158431

2.层序遍历

层序遍历:简单来说就是从根结点所在的层数开始,从上到下,从左到右依次访问每个结点

image-20221126134659431

实现代码

//层序遍历
public void levelOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    //用队列辅助存放节点
    Queue<TreeNode> queue = new LinkedList<>();
    //存放头节点
    queue.offer(root);
    while (!queue.isEmpty()) {
        //出队一个元素
        TreeNode node = queue.poll();
        System.out.print(node.value + " ");
        if (node.left != null) {
            queue.offer(node.left);
        }
        if (node.right != null) {
            queue.offer(node.right);
        }
    }
    System.out.println();
}
3.二叉树的基本操作

1.获取树中结点的个数

/**
 * 获取树中节点的个数 - 子问题思路
 *
 * @param root
 * @return
 */
public int size(TreeNode root) {
    //判断是否为空
    if (root == null) {
        return 0;
    }
    return size(root.left) + size(root.right) + 1;
}
public int nodeSize;
/**
 * 获取树中节点的个数 - 遍历思路
 *
 * @param root
 * @return
 */
static int count = 0;
public int size1(TreeNode root) {
    if (root == null) {
        return 0;
    }
    count++;
    size1(root.left);
    size1(root.right);
    return count;
}

2.获取叶子结点个数

/**
 * 获取叶子节点的个数 - 子问题
 *
 * @param root
 * @return
 */
public int getLeafNodeCount(TreeNode root) {
    if (root == null) {
        return 0;
    } else if (root.left == null && root.right == null) {
        return 1;
    }
    return getLeafNodeCount(root.left) + getLeafNodeCount(root.right);
}
public int leafCount;
/**
 * 获取叶子节点的个数 - 遍历
 *
 * @param root
 * @return
 */
public int getLeafNodeCount1(TreeNode root) {
    if (root == null) {
        return 0;
    } else if (root.left == null && root.right == null) {
        return leafCount++;
    }
    getLeafNodeCount1(root.left);
    getLeafNodeCount1(root.right);
    return leafCount;
}

3.获取第k层结点的个数

public int getKLevelNodeCount(TreeNode root, int k) {
    if (root == null || k <= 0) {
        return 0;
    }
    if (k == 1) {
        return 1;
    }
    return getKLevelNodeCount(root.left, k - 1) + getKLevelNodeCount(root.right, k - 1);
}

4.获取二叉树的高度

public int getHeight(TreeNode root) {
    if (root == null) {
        return 0;
    }
    int leftHeight = getHeight(root.left);
    int rightHeight = getHeight(root.right);
    return Math.max(leftHeight, rightHeight) + 1;
}

5.检测值为value的元素是否存在

public TreeNode find(TreeNode root, int val) {
    if (root == null) {
        return null;
    }
    if (root.value == val) {
        return root;
    }
    TreeNode left = find(root.left, val);
    TreeNode right = find(root.right, val);
    if (left != null) {
        return left;
    }
    if (right != null) {
        return right;
    }
    return null;
}

6.判断一棵树是不是完全二叉树

public boolean isCompleteTree(TreeNode root) {
    if (root == null) {
        return true;
    }
    //用队列辅助存放节点
    Queue<TreeNode> queue = new LinkedList<>();
    //存放头节点
    queue.offer(root);
    while (!queue.isEmpty()) {
        //出队一个元素
        TreeNode node = queue.poll();
        if (node != null) {
            //节点不为空,所有元素都入队
            queue.offer(node.left);
            queue.offer(node.right);
        } else {
            //节点为空全部出队,遇到非空元素,返回false
            while (!queue.isEmpty()) {
                TreeNode checkNode = queue.poll();
                if (checkNode != null) {
                    return false;
                }
            }
        }
    }
    return true;
}

2.6二叉树相关练习

  1. 检查两颗树是否相同
  2. 另一颗树的子树
  3. 翻转二叉树
  4. 判断一颗二叉树是否是平衡二叉树
  5. 对称二叉树
  6. 二叉树遍历
  7. 二叉树的层序遍历
  8. 二叉树的最近公共祖先
  9. 从前序与中序遍历序列构造二叉树
  10. 从中序与后序遍历序列构造二叉树
  11. 根据二叉树创建字符串
  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小 王

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

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

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

打赏作者

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

抵扣说明:

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

余额充值