当前位置: 首页 > news >正文

树的存储结构

前面我们讲过二叉树的存储方式,包括顺序存储和链式存储,但如果用存储二叉树的方式来存储树可以吗,这显然是不行的,因为二叉树是规律的树,比如说我们可以利用顺序存储的方式来存储二叉树,但是树不同于二叉树是多种多样的,所以我们需要使用其他方法

1.双亲表示法

具体思想:

用数组顺序存储各个结点。每个结点中保存数据元素、指向双亲结点(父结点)的指针

非根结点的双亲指针 = 父结点在数组中的下标

根节点的双亲指针 = -1(根结点没有父结点)

代码实现:

        

#include<stdio.h>
#include<stdbool.h>
#define Max 100
//成员表示
typedef struct
{
int data;//数据域
int parent;//父结点在数组里的位置
}TreeMember;
//数组表示
typedef struct
{
TreeMember arr[Max];//双亲表示
int num;//结点总数
}TreeArray;
//声明根结点
void InitTree(TreeArray* tree)
{
tree->arr[0].data = 1;
tree->arr[0].parent = -1;//根结点无父结点
tree->num = 1;
return;
}
//插入树
bool EnTree(TreeArray* tree)
{
if (tree == NULL)//无根结点
return false;
if (tree->num >= Max)//数组无空间
return false;
//手动方式创建
tree->arr[1].data = 2;
tree->arr[1].parent = 0;
tree->num++;

    tree->arr[2].data = 3;
tree->arr[2].parent = 0;
tree->num++;

    tree->arr[3].data = 4;
tree->arr[3].parent = 0;
tree->num++;

    tree->arr[4].data = 5;
tree->arr[4].parent = 1;
tree->num++;

    tree->arr[5].data = 6;
tree->arr[5].parent = 1;
tree->num++;

    tree->arr[6].data = 7;
tree->arr[6].parent = 2;
tree->num++;

    tree->arr[7].data = 8;
tree->arr[7].parent = 3;
tree->num++;

    tree->arr[8].data = 9;
tree->arr[8].parent = 3;
tree->num++;

    tree->arr[9].data = 10;
tree->arr[9].parent = 3;
tree->num++;

    tree->arr[10].data = 11;
tree->arr[10].parent = 4;
tree->num++;

    return true;
}
//寻找父结点
bool FindParent(TreeArray* tree)
{
if (tree == NULL)
return false;//空树没有父结点

    int n;
printf("输入孩子结点(9999退出)\n");
scanf("%d", &n);
while (n != 9999)//假设输入9999退出
{
if (n == 1)
return false;//根节点无父节点
if (n < 0 && n >= tree->num)
return false;//输入不合法
int parent = tree->arr[n - 1].parent;
printf("父节点为%d\n",tree->arr[parent].data);

        printf("输入孩子结点(9999退出)\n");
scanf("%d", &n);
}

    return true;

}
//寻找孩子结点
bool FindSon(TreeArray* tree)
{
if (tree == NULL)//表示空树
return false;
printf("请输入要寻找的孩子结点:\n");
int n;
scanf("%d", &n);
if (n < 0 && n > tree->num)
return false;//输入错误
int i = 0;
int flag = 0;
for (i = 0; i < tree->num; i++)
{
if (tree->arr[i].parent == n - 1)
{
printf("%d的孩子结点为%d\n", n, tree->arr[i].data);
flag++;
}
}
if (flag == 0)
printf("没找到\n");
return true;
}
int main()
{
TreeArray tree;//声明一棵树
InitTree(&tree);//初始化根结点
EnTree(&tree);//插入结点
FindParent(&tree);//假设寻找5的父结点
FindSon(&tree);
return 0;
}

//寻找父结点的时间复杂度为O(1)
//寻找孩子结点的时间复杂度为O(n)

优缺点:

优点:找双亲(父结点)很方便

缺点:找孩子不方便,只能从头到尾遍历真个数组

2.孩子表示法

具体思想:用数组顺序存储各个结点。每个结点中保存数据元素、孩子链表头指针

代码实现:

        

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#define Max 100
//定义链表(孩子结点)
typedef struct LinkNode
{
int place;//数据域,存在孩子在数组里的位置
struct LinkNode* next;//指针域
}LinkNode;
//定义成员
typedef struct TreeMember
{
int data;//数据域
struct LinkNode* FirstChild;//指针域(第一个孩子的地址)
}TreeMember;
//定义数组
typedef struct TreeArray
{
TreeMember arr[Max];
int root;//根结点的位置
int num;//总数
}TreeArray;
//初始化数组
void InitTree(TreeArray* Tree)
{
Tree->arr[0].data = 1;//初始化根节点为1
Tree->arr[0].FirstChild = NULL;//孩子结点暂时为空
Tree->root = 0;
Tree->num = 1;
}
//增加树结点
bool EnTree(TreeArray* Tree)
{
//插入树结点
if (Tree->num < Max)
{
Tree->arr[1].data = 2;
Tree->arr[1].FirstChild = NULL;
Tree->num++;
Tree->arr[0].FirstChild = (LinkNode*)malloc(sizeof(LinkNode));//创建根结点的孩子指针
if (Tree->arr[0].FirstChild == NULL)
return false;//分配失败
Tree->arr[0].FirstChild->place = 1;
Tree->arr[0].FirstChild->next = NULL;

        LinkNode* q = Tree->arr[0].FirstChild;
int number = 3;
for (int i = 2; i <= 3; i++)
{
Tree->arr[i].data = number;
Tree->arr[i].FirstChild = NULL;//孩子结点指向空
Tree->num++;
LinkNode* p = (LinkNode*)malloc(sizeof(LinkNode));
if (p == NULL)
return false;//分配失败
q->next = p;
p->next = NULL;
p->place = i;
q = p;
number++;
}

        Tree->arr[4].data = 5;
Tree->arr[4].FirstChild = NULL;
Tree->num++;
Tree->arr[1].FirstChild = (LinkNode*)malloc(sizeof(LinkNode));
if (Tree->arr[1].FirstChild == NULL)
return false;//分配失败
Tree->arr[1].FirstChild->place = 4;
Tree->arr[1].FirstChild->next = NULL;

        Tree->arr[5].data = 6;
Tree->arr[5].FirstChild = NULL;
Tree->num++;
Tree->arr[1].FirstChild->next = (LinkNode*)malloc(sizeof(LinkNode));
if (Tree->arr[1].FirstChild->next == NULL)
return false;//分配失败
Tree->arr[1].FirstChild->next->place = 5;
Tree->arr[1].FirstChild->next->next = NULL;

        Tree->arr[6].data = 7;
Tree->arr[6].FirstChild = NULL;
Tree->num++;
Tree->arr[2].FirstChild = (LinkNode*)malloc(sizeof(LinkNode));
if (Tree->arr[2].FirstChild == NULL)
return false;//分配失败
Tree->arr[2].FirstChild->place = 6;
Tree->arr[2].FirstChild->next = NULL;

        Tree->arr[7].data = 8;
Tree->arr[7].FirstChild = NULL;
Tree->num++;
Tree->arr[3].FirstChild = (LinkNode*)malloc(sizeof(LinkNode));
if (Tree->arr[3].FirstChild == NULL)
return false;
Tree->arr[3].FirstChild->place = 7;
Tree->arr[3].FirstChild->next = NULL;

        LinkNode* s = Tree->arr[3].FirstChild;
int j;
for (j = 8; j <= 9; j++)
{
Tree->arr[j].data = j + 1;
Tree->arr[j].FirstChild = NULL;
Tree->num++;
LinkNode* x = (LinkNode*)malloc(sizeof(LinkNode));
if (x == NULL)
return false;//分配失败
s->next = x;
x->next = NULL;
x->place = j;
s = x;
}
}

    return true;
}
//寻找孩子结点
bool FindSon(TreeArray* Tree,int n)
{
if (Tree == NULL)
return false;
if (Tree->arr[n - 1].FirstChild == NULL)
{
printf("无孩子结点\n");
return false;
}
//n - 1是当前结点的位置
LinkNode* p = Tree->arr[n - 1].FirstChild;
while (p != NULL)
{
printf("%d的孩子结点为%d\n", n, Tree->arr[p->place].data);
p = p->next;
}

    return true;
}
//寻找父亲结点
bool FindPar(TreeArray* Tree,int n)
{
if (Tree == NULL)
return false;//空树
if (n == Tree->root)
return false;//根结点无父结点
int i;
for (i = 0; i < Tree->num; i++)
{
LinkNode* p = Tree->arr[i].FirstChild;
int number = p->place;
while (p != NULL)
{
if (Tree->arr[number].data == Tree->arr[n - 1].data)
{
printf("%d的父结点为%d\n", n, Tree->arr[i].data);
return true;//找到了
}
else
{
p = p->next;
number = p->place;
}
}
}
return true;
}
int main()
{
TreeArray Tree;//定义一棵树
InitTree(&Tree);//初始化根结点
EnTree(&Tree);//新增树结点
FindSon(&Tree,4);//找孩子结点,假设寻找结点元素为4的孩子结点
FindPar(&Tree,4);//寻找父结点,假设寻找结点元素为4的父亲结点

    return 0;
//找父结点的时间复杂度为O(n^2)
//找孩子结点的时间复杂度为O(1)
//空间复杂度O(n)
//用双亲方式存储还是用孩子方式存储各有利弊,要根据实际情况来使用更方便的一种方式

3.孩子兄弟表示法

具体思想:类似与二叉树的存储方式,构建两个指针,分别存储首个孩子指针和右兄弟指针

代码实现:

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct TreeNode
{
int data;//数据域
struct TreeNode* firstchild, *rightbrother;//指针域(第一个孩子和右兄弟)
}TreeNode,*Tree;
//初始化
void InitTree(Tree* T)
{
(*T) = (TreeNode*)malloc(sizeof(TreeNode));//创建根结点
if ((*T) == NULL)
return;//分配失败
(*T)->firstchild = NULL;
(*T)->rightbrother = NULL;
return;
}
//插入树结点
bool EnTree(Tree* T)
{
if ((*T) == NULL)
return false;//没有根结点

    TreeNode* p1 = (*T);
TreeNode*p2 = (TreeNode*)malloc(sizeof(TreeNode));
if (p2 == NULL)
return false;//创建失败
p2->data = 2;
p2->firstchild = NULL;
p2->rightbrother = NULL;
p1->firstchild = p2;

    TreeNode* p3 = (TreeNode*)malloc(sizeof(TreeNode));
if (p3 == NULL)
return false;
p3->data = 3;
p3->firstchild = NULL;
p3->rightbrother = NULL;
p2->rightbrother = p3;

    TreeNode* p4 = (TreeNode*)malloc(sizeof(TreeNode));
if (p4 == NULL)
return false;
p4->data = 4;
p4->firstchild = NULL;
p4->rightbrother = NULL;
p3->rightbrother = p4;

    TreeNode* p5 = (TreeNode*)malloc(sizeof(TreeNode));
if (p5 == NULL)
return false;
p5->data = 5;
p5->firstchild = NULL;
p5->rightbrother = NULL;
p2->firstchild = p5;

    TreeNode* p6 = (TreeNode*)malloc(sizeof(TreeNode));
if (p6 == NULL)
return false;
p6->data = 6;
p6->firstchild = NULL;
p6->rightbrother = NULL;
p5->rightbrother = p6;

    TreeNode* p7 = (TreeNode*)malloc(sizeof(TreeNode));
if (p7 == NULL)
return false;
p7->data = 7;
p7->firstchild = NULL;
p7->rightbrother = NULL;
p3->firstchild = p7;

    TreeNode* p8 = (TreeNode*)malloc(sizeof(TreeNode));
if (p8 == NULL)
return false;
p8->data = 8;
p8->firstchild = NULL;
p8->rightbrother = NULL;
p4->firstchild = p8;

    TreeNode* p9 = (TreeNode*)malloc(sizeof(TreeNode));
if (p9 == NULL)
return false;
p9->data = 9;
p9->firstchild = NULL;
p9->rightbrother = NULL;
p8->rightbrother = p9;


TreeNode* p10 = (TreeNode*)malloc(sizeof(TreeNode));
if (p10 == NULL)
return false;
p10->data = 10;
p10->firstchild = NULL;
p10->rightbrother = NULL;
p9->rightbrother = p10;

    return true;
}
//寻找孩子结点

//遍历树找到结点
TreeNode* PreTree(Tree T, int n)
{
if (T != NULL)
{
if (T->data == n)
return T;
//先找孩子结点
TreeNode* child = PreTree(T->firstchild,n);
if (child != NULL)
return child;
//再找兄弟结点
TreeNode*brother = PreTree(T->rightbrother, n);
if (brother != NULL)
return brother;
}
return NULL;
}

//寻找孩子结点
bool FindSon(Tree* T, int n)
{
if ((*T) == NULL)
return false;//空树
TreeNode * q = PreTree((*T), n);

    if (q == NULL)
{
printf("不存在该结点\n");
return false;
}
if (q->firstchild == NULL)
{
printf("没有孩子结点\n");
return false;
}
TreeNode* s = q->firstchild;
while (s != NULL)
{
printf("%d的孩子结点为%d\n", n, s->data);
s = s->rightbrother;
}
return true;
}
int main()
{
Tree T;//构建树
InitTree(&T);//初始化树
EnTree(&T);//插入树结点
FindSon(&T,4);//假设寻找结点为4的孩子结点
return 0;
}
//寻找孩子结点的时间复杂度O(n)
//用该方法寻找父结点必须要加上一个格外的parent成员,否则无法寻找父结点及其困难
//空间复杂度为O(n)

http://www.dtcms.com/a/426915.html

相关文章:

  • 2025年9月GESP(C++三级):数组清零
  • 怎样查看网站建设时间注册公司需要什么费用
  • Deepoc具身模型外拓板:重塑居家服务机器人的交互革命
  • cpuset v1
  • 2025年9月个人工作生活总结
  • Java SE “JDK1.8新特性”面试清单(含超通俗生活案例与深度理解)
  • 站台建筑资阳网站推广
  • 【论文阅读 | ECCV 2024 | DAMSDet:具有竞争性查询选择与自适应特征融合的动态自适应多光谱检测变换器】
  • 企业网站 三网系统好玩有趣的网站
  • 小程序的页面宽度 设置多少合适??
  • 基于libwebsockets与cJson的ASR Server实时语音识别实现指南
  • golang 写路由的时候要注意
  • EXCEL哪个版本开始支持VSTO-office插件?
  • 盲盒抽卡机小程序的技术挑战与解决方案
  • 全网网站建设推广国外设计网站都有哪些
  • 零基础学AI大模型之LangChain聊天模型多案例实战
  • GPU 网络基础,Part 2(MoE 训练中的网络挑战;什么是前、后端网络;什么是东西向、南北向流量)
  • 【菜狗学聚类】序列嵌入表示、UMAP降维——20250930
  • 网站外链建设的八大基本准则东大桥做网站的公司
  • MySQL进阶知识点(八)---- SQL优化
  • 【C++STL :vector类 (二) 】攻克 C++ Vector 的迭代器失效陷阱:从源码层面详解原理与解决方案
  • C++ string类常用操作
  • 修改网站模板详解如何开网站需要多少钱
  • 浅谈WebSocket
  • 做网站背景wordpress登录样式
  • 自动化通信谜团:耐达讯自动化Modbus RTU如何变身 Profibus连接触摸屏
  • 调节阀控制的“语言障碍“:耐达讯自动化一招破解,让Modbus RTU变身Profibus!
  • LE AUDIO之助听器Hearing Access Profile
  • 提升学习自主性:听写自动化对儿童习惯养成的技术支持
  • MySql的存储过程以及JDBC实战