树的存储结构

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

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)

相关推荐
Sapphire~2 小时前
重学JS-009 --- JavaScript算法与数据结构(九)Javascript 方法
javascript·数据结构·算法
没有口袋啦2 小时前
《决策树、随机森林与模型调优》
人工智能·算法·决策树·随机森林·机器学习
小邓儿◑.◑2 小时前
贪心算法 | 每周8题(一)
算法·贪心算法
stolentime2 小时前
二维凸包——Andrew 算法学习笔记
c++·笔记·学习·算法·计算几何·凸包
Q741_1473 小时前
C++ 位运算 高频面试考点 力扣 371. 两整数之和 题解 每日一题
c++·算法·leetcode·面试·位运算
aramae3 小时前
链表理论基础
数据结构·c++·算法·leetcode·链表
2401_845417453 小时前
哈希表原理详解
数据结构·哈希算法·散列表
蒋星熠3 小时前
脑机接口(BCI):从信号到交互的工程实践
人工智能·python·神经网络·算法·机器学习·ai·交互
liuyao_xianhui3 小时前
四数之和_优选算法(C++)双指针法总结
java·开发语言·c++·算法·leetcode·职场和发展