前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【数据结构六】图文结合详解二叉树(五千字)

【数据结构六】图文结合详解二叉树(五千字)

作者头像
小皮侠
发布2024-04-08 20:43:50
990
发布2024-04-08 20:43:50
举报

二叉树

树是一种非线性的数据结构,它是由n个结点组成的具有层次关系的集合,把他叫做树是因为它的根朝上,叶子朝下,看起来像一颗倒挂的树。二叉树是一种最多只有两个节点的树型结构。这篇文章会用Java代码手撕二叉树的实现,从概念到实现,到oj题训练,你不仅能学会二叉树,还能加深对它的理解和运用。

1.树形结构的概念

在树形结构中,子树之间不能有交集,否则就不是树型结构,它具有以下的特点:

  • 子树是不相交的;
  • 除了根节点外,每个节点有且仅有一个父节点;
  • 一颗N个结点的树有N-1条边。

树中的相关概念:

结点的度:一个结点含有子树的个数称为该结点的度; 如上图:A的度为6

树的度:一棵树中,所有结点度的最大值称为树的度; 如上图:树的度为6

叶子结点或终端结点:度为0的结点称为叶结点; 如上图:B、C、H、I...等节点为叶结点

双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点; 如上图:A是B的父结点

孩子结点或子结点:一个结点含有的子树的根结点称为该结点的子结点; 如上图:B是A的孩子结点

根结点:一棵树中,没有双亲结点的结点;如上图:A

结点的层次:从根开始定义起,根为第1层,根的子结点为第2层,以此类推

树的高度或深度:树中结点的最大层次; 如上图:树的高度为4

非终端结点或分支结点:度不为0的结点; 如上图:D、E、F、G...等节点为分支结点

兄弟结点:具有相同父结点的结点互称为兄弟结点; 如上图:B、C是兄弟结点

堂兄弟结点:双亲在同一层的结点互为堂兄弟;如上图:H、I互为兄弟结点

结点的祖先:从根到该结点所经分支上的所有结点;如上图:A是所有结点的祖先

子孙:以某结点为根的子树中任一结点都称为该结点的子孙。如上图:所有结点都是A的子孙

森林:由m(m>=0)棵互不相交的树组成的集合称为森林

树的应用:

树的思想和应用在我们周围应用非常广泛也非常重要,比如树的遍历运用递归的思想,电脑中的文件管理系统(目录和文件)运用树形结构存储。

2.二叉树的性质

二叉树是一个节点的有限集合,要么为空,要么是由一个根节点加上两颗被称为左子树和右子树的二叉树组成。如下图所示:

两种特殊的二叉树:

1. 满二叉树: 一棵二叉树,如果每层的结点数都达到最大值,则这棵二叉树就是满二叉树。

2. 完全二叉树: 完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从0至n-1的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。

二叉树的性质:

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

3.如何构造一个二叉树

常见树的表示方式有很多种,如:双亲表示法,孩子表示法,孩子双亲表示法,孩子兄弟表示法等等。

二叉树用代码进行表示时有两种方法,一是通过数组形式的顺序存储,二是类似于链表的链式存储。

顺序存储:将元素存储到数组中,利用二叉树的性质5进行存储,设i为节点在数组中的下标,则:

  • 如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2
  • 如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子
  • 如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

完全二叉树

一般二叉树

tips: 对于非完全二叉树,则不适合使用顺序方式进行存储,,空间中必须存储空节点,导致空间利用率很低。

链式存储:二叉树的链式存储是通过一个一个的节点引用起来的,常见的表示方式有二叉和三叉表示方式,具体如下:

代码语言:javascript
复制
// 孩子表示法
class Node {
int val; // 数据域
Node left; // 左孩子的引用,常常代表左孩子为根的整棵左子树
Node right; // 右孩子的引用,常常代表右孩子为根的整棵右子树
} 

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

4.二叉树的基本操作

二叉树遍历分为四种:

  • 前序遍历——访问根结点--->根的左子树--->根的右子树。
  • 中序遍历——根的左子树--->根节点--->根的右子树。
  • 后序遍历——根的左子树--->根的右子树--->根节点。
  • 层序遍历——第一层--->第二层--->第三层--->...最后一层

下面是用代码分别实现这四种遍历方式的过程。

代码语言:javascript
复制
package Mytree;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User:莎土比亚
 * Date:2024-03-08
 * Time:18:06
 */
public class Mytree {
    class Node{
        public Node(int val) {
            this.val=val;
        }
        int val;
        Node left;
        Node right;
    }
    private int usedsize;
    Node root;
    public Mytree(){
        root=null;
        usedsize=0;
    }
    public void createBinaryTree(){
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);
        root = node1;
        node1.left = node2;
        node2.left = node3;
        node1.right = node4;
        node4.left = node5;
        node5.right = node6;
    }
    // 前序遍历
    public void preOrder(Node root) {
        if(root==null){
            return;
        }
        System.out.print(root.val+" ");
        preOrder(root.left);
        preOrder(root.right);
    }
    // 中序遍历
    public void inOrder(Node root) {
        if(root==null){
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }
    // 后序遍历
    public void postOrder(Node root){
     if(root==null){
         return;
     }
     postOrder(root.left);
     postOrder(root.right);
     System.out.print(root.val+" ");
    }
    //层序遍历
    public void LeOrder(Node root){
        Queue<Node> list=new LinkedList<>();
        list.add(root);
        while (!list.isEmpty()){
            Node cur=list.poll();
            if(cur!=null){
            System.out.print(cur.val+" ");
            list.add(cur.left);
            list.add(cur.right);
           }
        }
    }

    public static void main(String[] args) {
        Mytree tree=new Mytree();
        tree.createBinaryTree();
        System.out.println("前序遍历");
        tree.preOrder(tree.root);
        System.out.println("\n中序遍历");
        tree.inOrder(tree.root);
        System.out.println("\n后序遍历");
        tree.postOrder(tree.root);
        System.out.println("\n层序遍历");
        tree.LeOrder(tree.root);
    }
}

二叉树的常见方法:

代码语言:javascript
复制
// 获取树中节点的个数
int size(Node root);

// 获取叶子节点的个数
int getLeafNodeCount(Node root);

// 获取第K层节点的个数
int getKLevelNodeCount(Node root,int k);

// 获取二叉树的高度
int getHeight(Node root);

// 检测值为value的元素是否存在
Node find(Node root, int val);

//层序遍历
void levelOrder(Node root);

// 判断一棵树是不是完全二叉树
boolean isCompleteTree(Node root);

创建一个MyTree类实现一个自己的二叉树并包含上述方法:

代码语言:javascript
复制
    // 获取树中节点的个数
    int size(Node root) {
        int num=0;
        if(root==null){
            return 0;
        }
        num+=size(root.left);
        num+=size(root.right);
        num++;
        return num;
    }

    // 获取叶子节点的个数
    int getLeafNodeCount(Node root) {
        if(root==null){
            return 0;
        }
        if(root.left==null&&root.right==null) {
            return 1;
        }
        return getLeafNodeCount(root.left)+getLeafNodeCount(root.right);
    }

   //  获取第K层节点的个数
    int getKLevelNodeCount(Node root,int k) {
     if(k==1){
         return 1;
     }
     return getKLevelNodeCount(root.left,k-1)+getKLevelNodeCount(root.right,k-1);
    }

   // 获取二叉树的高度
   int getHeight(Node root) {
        if(root==null){
            return 0;
        }
        return Math.max(getHeight(root.left),getHeight(root.right))+1;
   }

    // 检测值为value的元素是否存在
    Node find(Node root, int val){
        if (root==null){
            return null;
        }
        if(root.val==val){
            return root;
        }
        Node left=find(root.left,val);
        Node right=find(root.right,val);
        if(left!=null){
            return left;
        }
        return right;
    }

    // 判断一棵树是不是完全二叉树
    boolean isCompleteTree(Node root) {
        if(root==null){
            return true;
        }
        if((root.left==null&&root.right!=null)||(root.left!=null&&root.right==null)){
            return false;
        }
        return isCompleteTree(root.left)&&isCompleteTree(root.right);
    }

总结:解决二叉树相关问题时要善用子问题思路,将原问题简化为从左子树和右子树中求原始结果,在套用方法递归即可得到问题答案。

5.二叉树相关oj题训练

1.翻转二叉树

2.检查两颗二叉树是否相同

3.判断一颗二叉树是否是平衡二叉树

4.根据一棵树的前序遍历和中序遍历构造二叉树

5.根据二叉树创建字符串

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-03-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二叉树
    • 1.树形结构的概念
      • 2.二叉树的性质
        • 3.如何构造一个二叉树
          • 4.二叉树的基本操作
            • 5.二叉树相关oj题训练
              • 4.根据一棵树的前序遍历和中序遍历构造二叉树
              相关产品与服务
              对象存储
              对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档