数据结构——树的三种表示方法

news2024/9/20 19:24:51

目录

引言

1.树的定义

2.树的基本概念

3.树的表示方式

(1)双亲表示法

(2)孩子表示法

(3)左孩子右兄弟表示法

(4)树的应用

结束语


引言

在学习完栈和队列的之后后,我们接下来学习新的数据结构——树。

1.树的定义

树是一种非线性数据结构,它模拟了具有层次关系的数据的集合。树(数据结构)在形态上与自然界中的树木具有相似性,因此树才被称之为树,只不过它是倒挂着的树。

树中有一个特别的节点,称为根节点。它是整棵树的起点,没有前驱节点

除根节点外,其余结点被分成M(M > 0)个互不相交的集合T1、T2、……、Tm,其中每一个集合Ti(1 <= i<= m)又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有0个或多个后继因此,树是递归定义的。

如下图所示:

注意:树形结构中,子树之间不能有交集,否则就不是树形结构。

例如这样:

子树是不相交的。

除了根节点外,每个节点有且仅有一个父节点。

一棵N个节点的树有N-1条边。

2.树的基本概念

我们根据这棵树来介绍一下树的基本概念

如下表所示:

术语定义
节点的度一个节点含有的子树的个数称为该节点的度,比如说节点1的度为2
叶节点或终端节点度为0的节点,比如说4,5,6节点
非终端节点或分支节点度不为0的节点,比如说2,3节点
双亲结点或父节点若一个节点含有子节点,则这个节点称为其子节点的父节点。比如说2是4,5的父节点
孩子节点或子节点一个节点含有的子树的根节点称为该节点的子节点,比如说4,5是2的子节点
兄弟节点具有相同父节点的节点互称为兄弟节点,比如说4,5就是兄弟节点
树的度一棵树中,最大的节点的度称为树的度,比如说上面这棵树的度为2
节点的层次从根开始定义起,根为第1层,根的子节点为第2层,以此类推
树的高度或深度树中节点的最大层次,比如说上面这棵树的高度为3
堂兄弟节点双亲在同一层的节点互为堂兄弟,比如说5,6节点
节点的祖先从根到该节点所经分支上的所有节点,比如说1就是所有节点的祖先
子孙以某节点为根的子树中任一节点都称为该节点的子孙,比如说所有节点都是1的子孙
森林由m(m>0)棵互不相交的树的集合称为森林

3.树的表示方式

我们有三种方法表示下面这棵树:

(1)双亲表示法

双亲表示法是用顺序表,也就是数组对树进行表示的。即用顺序表存储各个节点的数据,并且同时存储其双亲节点的下标。

注意:根节点没有双亲节点,所以特别记为-1。

如下图所示:

存储方式如下:

说明:

1.因为根节点没有父节点,将其父节点数组下标设置为-1,根节点存储在顺序表的第1个位置。
2.数据元素2、3、4的父节点为0,父节点数组下标为0,分别存储在顺序表的2、3、4个位置。
3.数据元素5、6的父节点为1,父节点数组下标为1,分别存储在顺序表的第5、6个位置。
4.数据元素7的父节点为3,父节点数组下标为3,存储在顺序表的第7个位置。

优缺点:

优点:查找父节点方便

缺点:在查找子节点或兄弟节点时不够直接,通常需要遍历整棵树。

#include<stdio.h>
#include<stdlib.h>
#define MAXSIZE 10
typedef int DataType;
typedef struct TreeNode
{
	DataType data;
	int parent;
}Node;

typedef struct ParentTree
{
	// 数据结构的树节点
	Node Tnode[MAXSIZE];
	int n;		//当前节点个数
}Ptree;


// 初始化树
void TreeInit(Ptree* ptree, int x, int parentIndex) 
{
    if (ptree->n >= MAXSIZE) 
    {
        printf("Tree is full!\n");
        return;
    }

    ptree->Tnode[ptree->n].data = x;
    ptree->Tnode[ptree->n].parent = parentIndex;
    ptree->n++; // 增加节点计数  
}
(2)孩子表示法

树的孩子表示法就是采用顺序表与链表结合的形式,用顺序表存储树的值与链表的头节点,而链表的头节点存储其孩子节点在顺序表中的下标,若没有则记为空(NULL)。

相当于对树进行这样的处理:

存储方式如下:

说明:

添加一个数据(插入一个结点)

  1. 既要在数组中依次添加新的数据。
  2. 也要在其父节点后面用头插法插入。

头插法:数据结构——单链表 

优缺点:

优点:

1.可以快速访问子节点。由于每个节点的子节点都被直接存储在链表中,因此可以快速访问任意节点的所有子节点。

2.易于插入和删除节点。当需要向树中插入或删除节点时,只需在相应的链表中添加或删除节点即可,操作相对简单。

缺点:

查找双亲节点不便。与双亲表示法相反,孩子表示法在查找节点的父节点时不太方便。通常需要从树的根节点开始遍历整个树或至少遍历该节点所在子树的一部分才能找到父节点。

代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<stdlib.h>

#define MAXSIZE 10
typedef int DataType;
typedef struct ListNode
{
	int index;
	struct ListNode* next;
}ListNode;

typedef struct TreeNode
{
	DataType data;
	ListNode* child;
}TNode;

typedef struct Tree
{
	TNode nodes[MAXSIZE];
	int n;
}Tree;

// 创建一个新的ListNode  
ListNode* createListNode(int index) 
{
    ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
    if (newNode == NULL)
    {
        perror("malloc fail:");
        exit(0);
    }
    newNode->index = index;
    newNode->next = NULL;
    return newNode;
}

// 初始化树  
void TreeInit(Tree* t, DataType x) 
{
    if (t->n >= MAXSIZE) 
    {
        printf("Tree is full!\n");
        return;
    }
    t->nodes[t->n].data = x;
    t->nodes[t->n].child = NULL;  
    t->n++;
}

// 向节点的子链表中添加一个新节点  
void InsertChild(Tree* t, int parentIndex, int childIndex)
{
    if (parentIndex >= t->n || childIndex >= t->n) 
    {
        printf("error\n");
        return;
    }
    ListNode* newNode = createListNode(childIndex);
    if (newNode == NULL)
    {
        perror("malloc fail:");
        return; // 内存分配失败  
    }
    newNode->next = t->nodes[parentIndex].child;
    t->nodes[parentIndex].child = newNode;
}

(3)左孩子右兄弟表示法

最常用表示树的的方法就是左孩子右兄弟表示法,即定义两个指针,让左指针指向子节点,右指针指向兄弟节点。如果没有节点,则都指向空(NULL)。

如图所示:

说明:

  1. 先找到在谁的后面插入,即找到父节点
  2. 然后再看看父节点的孩子指针空不空
  3. 空的话就插入到父节点的孩子指针域里,如果这个位置有结点的话(孩子指针域不空),就要插入到这个孩子结点的兄弟指针域里,插入方式选择头插法。

代码如下:

#include <stdio.h>
#include <stdlib.h>
typedef int DataType;

typedef struct TreeNode
{
    DataType data;
    struct TreeNode* leftChild;  // 指向左孩子  
    struct TreeNode* rightBro; // 指向右兄弟  
} TNode;

// 创建一个新的树节点
TNode* createTreeNode(DataType x)
{
    TNode* newNode = (TNode*)malloc(sizeof(TNode));
    if (newNode == NULL)
    {
        perror("malloc fail:");
        exit(0);
    }
    newNode->data = x;
    newNode->leftChild = NULL;
    newNode->rightBro = NULL;
    return newNode;
}

void InsertChild(TNode* t, DataType x, int isLeftChild) 
{
    if (t == NULL) 
    {
        printf("Cannot insert into NULL node\n");
        return;
    }
    TNode* newNode = createTreeNode(x);
    if (newNode == NULL) 
    {
        return;
    }
    // 如果isLeftChild为1,表示新节点应该作为左孩子插入  
    if (isLeftChild) 
    {
        // 如果当前节点还没有右兄弟,则直接将新节点设置为右兄弟
        if (t->leftChild == NULL) 
        {
            t->leftChild = newNode;
        }
        // 如果当前节点已经有左孩子,则遍历左孩子的右兄弟链表
        // 找到最后一个右兄弟,并将新节点插入为它的下一个右兄弟
        else 
        {
            TNode* tmp = t->leftChild;
            while (tmp->rightBro != NULL) 
            {
                tmp = tmp->rightBro;
            }
            tmp->rightBro = newNode;
        }
    }
    // 如果isLeftChild为0,表示新节点应该作为右兄弟插入
    else
    {
        // 如果当前节点还没有右兄弟,则直接将新节点设置为右兄弟
        if (t->rightBro == NULL) 
        {
            t->rightBro = newNode;
        }
        // 如果当前节点已经有右兄弟,则遍历右兄弟链表
        // 找到最后一个右兄弟,并将新节点插入为它的下一个右兄弟
        else 
        {
            TNode* tmp = t->rightBro;
            while (tmp->rightBro != NULL) 
            {
                tmp = tmp->rightBro;
            }
            tmp->rightBro = newNode;
        }
    }
}
(4)树的应用

在linux环境下目录结构就是有一颗树构成,而在Windows环境下,目录许多内容并不交叉,所以是由森林构成。

结束语

下一篇我们将继续学习树的知识。

感谢各位大佬的支持!!!

求点赞收藏关注!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2070280.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript 数据结构 ==== 二叉树

目录 二叉树 结构 二叉树和二叉搜索树介绍 1.创建树 2.插入一个键 3.树的遍历 中序排序 先序遍历 后序遍历 4.搜索树中的值 5.删除节点 二叉树 在计算机科学中&#xff0c;二叉树是每个结点最多有两个子树的树结构。通常子树被称作“左子树”&#xff08;left subtre…

Docker Desktop镜像路径修改一直报错

一 点击Apply & Restart报错 [Window Title] Docker Desktop[Main Instruction] Error migrating WSL disk[Content] An error occurred while migrating the Docker Desktop WSL data disk to its new location:moving disk file: rename C:\Users\Lenovo\AppData\Local\D…

Linux 进程 | 进程地址空间

文章目录 进程地址空间程序地址空间进程地址空间 进程地址空间 程序地址空间 地址空间一共有如下的几个区域&#xff0c;从下到上地址逐渐增加&#xff0c;其中栈区的空间是从上往下使用&#xff0c;即从高地址往低地址增长&#xff1b;堆区的空间是从下往上使用&#xff0c;…

简单实现进度条效果(vue2)

如果用echarts或者其他图表来写个进度条有点大材小用&#xff0c;所以直接简单html、js写一下就可以&#xff1b; 以下代码基于vue2&#xff0c; 部分代码来自国内直连GPT/Claude镜像站 <template><div class"progress-container"><div class"p…

aosp源码导入android studio无法跳转-学员答疑

背景&#xff1a; 在学习framework入门课时候&#xff0c;一个很重要环节就是导入aosp的源码到android studio&#xff0c;这样有了IDE之后开发起来就很方便了&#xff0c;但是很多学员朋友对编译出来的ipr&#xff0c;iml文件改造不知道该怎么搞&#xff0c;导致android stud…

mysql 开启binlog并设置

打开my.cnf 文件&#xff0c;Linux系统文件默认位置为 /etc目录下&#xff0c;若不存在&#xff0c;可以使用下述命令查询 find / -name my.cnf修改my.cnf文件&#xff0c;开启binlog mysqld 模块下添加以下内容 server_id1 #给当前mysql机器设置一个id log-binmysql_bin …

分布式事务理论和解决方案

分布式事务理论 business&#xff08;下单&#xff09;远程调用库存&#xff08;storage&#xff09;,保存订单&#xff08;order&#xff09;&#xff0c;扣减积分&#xff08;account&#xff09;&#xff0c;只有这三个步骤全部成功&#xff0c;我们的下订单才算成功。 如果…

2024-01-开发技术积累

文章目录 递归删除文件执行任务超时时间读写锁获取异常栈信息通过NIO读取文件单例模式代码NIO管道写文件(来自nacos)NIO读取文件(来自Nacos)spring指定注解扫描 递归删除文件 xxl-job源码 public static boolean deleteRecursively(File root) {if (root ! null && ro…

JAVA中的Stream流的使用详解

1.Stream的介绍 Java 8 API添加了一个新的抽象称为流Stream&#xff0c;可以让你以一种声明的方式处理数据。Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。Stream API可以极大提高Java程序员的生产力&#xff0c;让程…

React+Vis.js(04):vis.js设置节点显示图片

文章目录 实现效果关键代码完整代码设置图片边框和背景颜色我们继续以 复仇者联盟为例,来介绍如何实现节点显示 图片。 实现效果 以图片进行节点的显示,使得显示效果更加直观,信息更为明了。 关键代码 在vis.js中,通过属性shape来控制节点显示为图像。 const nodes …

Shire 0.5 发布:构建数据安全 RAG,充分整合研发资产

最近&#xff0c;我们发布了新版本的 Shire&#xff0c;在这个新的发布&#xff08;Shire 0.5&#xff09;里&#xff0c;你可以更好地融合本地研发资产&#xff0c;同时构建数据安全 RAG。在这次版本中&#xff0c;我们增加了&#xff1a; 对 SonarQube 的 issue 支持。可以直…

1.反爬虫机制

一、IP 封锁 网站可以检测请求的IP地址&#xff0c;并封锁那些频繁请求的IP&#xff0c;使其无法访问网站。这是一种常见的反爬虫策略&#xff0c;用于防止单个IP地址对服务器造成过大的负载。 解决办法 &#xff1a; 使用代理IP池以避免IP封锁 // 待补充 二、请求头检测(Us…

2024百元学生党蓝牙耳机有哪些?甄选四款精品王炸机型推荐

近年来&#xff0c;随着手机的普及和音乐娱乐的盛行&#xff0c;蓝牙耳机成为越来越多人的选择。蓝牙耳机的优点在于无需使用线缆&#xff0c;方便携带&#xff0c;而且可以随时随地享受音乐或者通话。在市面上&#xff0c;有各种各样的蓝牙耳机可供选择&#xff0c;对于预算有…

Star-CCM+探针查看与创建

在实际应用工况中&#xff0c;数值计算结束后为了产看某个点的标量场或矢量场可以采用探针查看。而在软件中可以通过“&#xff0c;”、“。”快捷键进行创建与查看。两者的区别具体如下所示&#xff1a; 鼠标放在要查看部件的位置&#xff0c;同时点击键盘上的“。”键&#…

神经网络动画讲解 - 神经网络工作流程

神经网络工作流程 神经网络工作流程&#xff1a; 特征分解1&#xff08;手写数字->简单笔画组合&#xff09;、特征分解2&#xff08;简单笔画->最小笔画组合&#xff09;、特征识别1&#xff08;最小笔画组合->简单笔画&#xff09;、特征识别2&#xff08;简单笔画…

Java 入门指南:迭代器(Iterator)

迭代器 迭代器&#xff08;Iterator&#xff09; 是一种行为型设计模式&#xff0c;属于设计模式之一&#xff0c;迭代器模式提供了一种方法来顺序访问一个聚合对象&#xff08;如List、Set等&#xff09;中各个元素&#xff0c;而不需要暴露该对象的内部表示。 Iterator 对象…

攻防世界-web题型-6星难度汇总-个人wp

i-got-id-200 这一题很清楚的告诉了考点是什么&#xff0c;就是cgi相关的知识&#xff0c;不过我对这个不了解。。。也不会perl语言&#xff0c;先去网上看看这个东西吧&#xff0c;了解一下 看到一篇挺有意思的视频CGI&#xff08;通用网关接口&#xff09;_百度百科 看了这…

MediaTek 天玑9000 旗舰5G,都有哪些突破

天玑9000&#xff0c;是MTK 在2022发布的全新旗舰SOC , 采用台积电4nm制程和Armv9架构,八核CPU。 市场上采用天玑9000的手机有小米12Pro、小米Redmi K50 Pro、Vivo X80等。 今天我们看下这款soc更详细的一些特点介绍。 一、率先采用台积电 4nm 先进制程 业界率先采用台积电 4nm…

传统2D3D视觉分享

哈喽&#xff0c;大家好&#xff0c;本文主要是说明关于【视觉源码小铺】知识星球内的相关内容 视觉源码小铺都有什么内容 IVision拖拽平台&算法 星球内算法&#xff08;持续更新&#xff09;

滚雪球学Java(90):Java图形界面新篇章:Swing框架深度解析,真有点东西!

咦咦咦&#xff0c;各位小可爱&#xff0c;我是你们的好伙伴——bug菌&#xff0c;今天又来给大家普及Java SE啦&#xff0c;别躲起来啊&#xff0c;听我讲干货还不快点赞&#xff0c;赞多了我就有动力讲得更嗨啦&#xff01;所以呀&#xff0c;养成先点赞后阅读的好习惯&#…