树的存储结构

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

1.双亲表示法

具体思想:

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

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

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

代码实现:

#include<stdio.h>

#include<stdbool.h>

#define Max 100

//成员表示

typedef struct

{

int data;//数据域

int parent;//父结点在数组里的位置

}TreeMember;

//数组表示

typedef struct

{

TreeMember arrMax;//双亲表示

int num;//结点总数

}TreeArray;

//声明根结点

void InitTree(TreeArray* tree)

{

tree->arr0.data = 1;

tree->arr0.parent = -1;//根结点无父结点

tree->num = 1;

return;

}

//插入树

bool EnTree(TreeArray* tree)

{

if (tree == NULL)//无根结点

return false;

if (tree->num >= Max)//数组无空间

return false;

//手动方式创建

tree->arr1.data = 2;

tree->arr1.parent = 0;

tree->num++;

tree->arr2.data = 3;

tree->arr2.parent = 0;

tree->num++;

tree->arr3.data = 4;

tree->arr3.parent = 0;

tree->num++;

tree->arr4.data = 5;

tree->arr4.parent = 1;

tree->num++;

tree->arr5.data = 6;

tree->arr5.parent = 1;

tree->num++;

tree->arr6.data = 7;

tree->arr6.parent = 2;

tree->num++;

tree->arr7.data = 8;

tree->arr7.parent = 3;

tree->num++;

tree->arr8.data = 9;

tree->arr8.parent = 3;

tree->num++;

tree->arr9.data = 10;

tree->arr9.parent = 3;

tree->num++;

tree->arr10.data = 11;

tree->arr10.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->arrn - 1.parent;

printf("父节点为%d\n",tree->arrparent.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->arri.parent == n - 1)

{

printf("%d的孩子结点为%d\n", n, tree->arri.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 arrMax;

int root;//根结点的位置

int num;//总数

}TreeArray;

//初始化数组

void InitTree(TreeArray* Tree)

{

Tree->arr0.data = 1;//初始化根节点为1

Tree->arr0.FirstChild = NULL;//孩子结点暂时为空

Tree->root = 0;

Tree->num = 1;

}

//增加树结点

bool EnTree(TreeArray* Tree)

{

//插入树结点

if (Tree->num < Max)

{

Tree->arr1.data = 2;

Tree->arr1.FirstChild = NULL;

Tree->num++;

Tree->arr0.FirstChild = (LinkNode*)malloc(sizeof(LinkNode));//创建根结点的孩子指针

if (Tree->arr0.FirstChild == NULL)

return false;//分配失败

Tree->arr0.FirstChild->place = 1;

Tree->arr0.FirstChild->next = NULL;

LinkNode* q = Tree->arr0.FirstChild;

int number = 3;

for (int i = 2; i <= 3; i++)

{

Tree->arri.data = number;

Tree->arri.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->arr4.data = 5;

Tree->arr4.FirstChild = NULL;

Tree->num++;

Tree->arr1.FirstChild = (LinkNode*)malloc(sizeof(LinkNode));

if (Tree->arr1.FirstChild == NULL)

return false;//分配失败

Tree->arr1.FirstChild->place = 4;

Tree->arr1.FirstChild->next = NULL;

Tree->arr5.data = 6;

Tree->arr5.FirstChild = NULL;

Tree->num++;

Tree->arr1.FirstChild->next = (LinkNode*)malloc(sizeof(LinkNode));

if (Tree->arr1.FirstChild->next == NULL)

return false;//分配失败

Tree->arr1.FirstChild->next->place = 5;

Tree->arr1.FirstChild->next->next = NULL;

Tree->arr6.data = 7;

Tree->arr6.FirstChild = NULL;

Tree->num++;

Tree->arr2.FirstChild = (LinkNode*)malloc(sizeof(LinkNode));

if (Tree->arr2.FirstChild == NULL)

return false;//分配失败

Tree->arr2.FirstChild->place = 6;

Tree->arr2.FirstChild->next = NULL;

Tree->arr7.data = 8;

Tree->arr7.FirstChild = NULL;

Tree->num++;

Tree->arr3.FirstChild = (LinkNode*)malloc(sizeof(LinkNode));

if (Tree->arr3.FirstChild == NULL)

return false;

Tree->arr3.FirstChild->place = 7;

Tree->arr3.FirstChild->next = NULL;

LinkNode* s = Tree->arr3.FirstChild;

int j;

for (j = 8; j <= 9; j++)

{

Tree->arrj.data = j + 1;

Tree->arrj.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->arrn - 1.FirstChild == NULL)

{

printf("无孩子结点\n");

return false;

}

//n - 1是当前结点的位置

LinkNode* p = Tree->arrn - 1.FirstChild;

while (p != NULL)

{

printf("%d的孩子结点为%d\n", n, Tree->arrp-\>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->arri.FirstChild;

int number = p->place;

while (p != NULL)

{

if (Tree->arrnumber.data == Tree->arrn - 1.data)

{

printf("%d的父结点为%d\n", n, Tree->arri.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)

相关推荐
JieE21216 小时前
LeetCode 56. 合并区间|超清晰 JS 图解思路,面试高频区间题
javascript·算法·面试
Jack201 天前
HarmonyOS开发中错误处理策略:网络异常统一处理
算法
小小杨树1 天前
读懂色彩:拍照调色不再难
算法·计算机视觉·配色
JieE2122 天前
LeetCode 226. 翻转二叉树|JS 递归超详细拆解,二叉树入门经典题
javascript·算法
JieE2122 天前
LeetCode 104. 二叉树的最大深度|递归思路超详细拆解
javascript·算法
vivo互联网技术2 天前
CVPR 2026 | 全新强化学习框架 BeautyGRPO:重塑真实人像
算法·大模型·cvpr·影像
Darling噜啦啦2 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
用户497863050732 天前
(一)小红的数组操作
算法·编程语言
怕浪猫2 天前
Electron 系列文章封面图
算法·架构·前端框架