目录
树:
数据之间存在一对多的关系
树和子树之间的关系,可以类比于集合和子集,但是子树的根节点下的所有结点都包括的才叫做子树,下面图片圈出来的都是,若是ABCD组成的并不叫作子树。
树的定义是具有递归性的,即树中有树
相关概念:
1.结点:
把每一个数据都存放在树中的一个结点中
根结点:没有前驱的结点,如上图的A
叶子结点:没有后继的结点,如上图的KLMFGIJ
结点和结点之间的关系
父子关系 -->父亲结点、孩子结点
兄弟关系 -->兄弟结点(有广义(堂兄弟)和狭义(亲兄弟)上的)
注意:结点间的关系是相对的,如果在没有前提的条件下,说谁是谁的xx(关系)的说法是错误的
2.度
结点的度:看该结点有几个度(只算一层)如A的度是3(BCD三个孩子)
度为0的结点是叶子结点√
树的度:树中所有的结点的最大值 ,如上图的树的度是3
3.n叉树
度为n的树,在n叉树中,每个结点最多有n个孩子
4.高度/深度
5.有序树和无序树
如果是有序树,左子树和右子树的顺序不同,看作不同树;
如果是无序树,左子树和右子树的顺序不同,仍然看作同一个树;
后面的代码将树按照无序树来处理
6.空树:
没有任何结点
树的存储结构/表示方法:
树中都需要存储什么?
树中的数据-->数组
数据之间的关系-->父亲(1.双亲表示法) 孩子(2.孩子表示法)孩子和兄弟(3.孩子兄弟表示法)
1.双亲表示法
指名某个结点的父亲
在存储数据的时候,我们可以采用结构体数组
struct node{
char data;//数据
int fi;//data父亲的下标
}t[105];
#include<stdio.h>
#include<stdlib.h>
struct node{
char data;//数据
int fi;//data父亲的下标
}t[105];
int size;//树中的节点个数
void initTree(char x)
{
t[0].data=x;
t[0].fi=-1;
size++;
}
int find(char fx)
{
for(int i=0;i<size;i++)
{
if(t[i].data==fx)
{
return i;
}
}
}
void insert(char x,char fx)
{
t[size].data=x;
int fx_i=find(fx);
t[size].fi=fx_i;
size++;
}
int main()
{
int n;//输入数据的个数
char root;
scanf("%d",&n);
getchar();
scanf("%c",&root);
initTree(root);
char x,fx;//x的父亲是fx
for(int i=1;i<=n-1;i++)
{
getchar();
scanf("%c %c",&x,&fx);
insert(x,fx);
}
getchar();
scanf("%c",&x);
//找x的父亲和孩子
//找父亲
int x_i=find(x);
int fx_i=t[x_i].fi;
if(fx_i==-1)
{
printf("根节点,没有父亲节点\n");
}
else{
printf("父亲节点是%c\n",t[fx_i].data);
}
int sum=0;//孩子的个数
//找孩子:
printf("孩子节点是:\n");
for(int i=0;i<size;i++)
{
if(x_i==t[i].fi)
{
sum++;
printf("%c ",t[i].data);
}
}
if(sum==0)
{
printf("叶子节点,没有孩子节点\n");
}
return 0;
}
2.孩子表示法
孩子表示法
根据上图,我们可以申请一个t数组,t数组中存放有数据和指向孩子的链表,在链表中存放孩子结点的下标,所以代码如下(这里需要注意,链表只是结构体数组的一个成员变量,t的本质仍是数组)
//存放孩子的单链表结点结构
typedef struct childNode {
int chi;//存放孩子结点的下标
struct childNode* next;//写一个孩子的结点
}chNode;
struct node {
char data;//存放数据
chNode* first;//指向孩子链表
}t[105];
数据的初始化
int size;//记录存储数据的个数,同时可以当作指向下一个将要插入的数据位置的指针
//树的初始化
void initTree(char root) {
t[0].data = root;
t[0].first = NULL;
size++;
}
数据的插入(难点)
//寻找对应数据的下标
int find(char x) {
for (int i = 0; i < size; i++) {
if (t[i].data == x) {
return i;
}
}
}
//插入树的结点
//如果想要插入,首先要知道插入的元素和它的父亲
void insert(char x, char fx) {
//先将x放入到t数组中,并将x元素的孩子命名为空
t[size].data = x;
t[size].first = NULL;
//然后再将x的下标存放再fx的的链表中
int fxi = find(fx);//寻找fx的下标
//申请空间
chNode* s = (chNode*)malloc(sizeof(chNode));
//为了方便使用,这里我们采用头插法,(无序树)
s->chi = size;
s->next = t[fxi].first;
t[fxi].first = s;
size++;
}
理解插入数据的过程:
加入我们想要插入的数据是B,如下图所示
在插入过程,我们需要将让t[2]=B,也就是t[size]=x(x是要插入的数据),所以size即表示数组中元素的个数,又表示将插入数据的位置的下标
因为现在是在插入元素B,而暂时不考虑B的孩子下标的问题,所以让B的孩子下标为空,即t[size].first = NULL;
因为B是A的孩子,为了体现这一关系,需要在A的孩子下标处插入B的下标2,为了方便代码的编写,这里我们采用头插法。
首先我们要找到B的父亲R的下标,这里我们通过find函数来实现,并记录父亲下标为fxi
因为孩子下标是以链表的方式存储,所以我们需要申请一块孩子下标空间,也就是chNode* s = (chNode*)malloc(sizeof(chNode));
然后头插法插入数据
最后size++;
寻找父亲结点
如果我们知道一个元素,我们很容易知道这个元素的孩子都有哪些,但如果想要知道这个元素的父亲,则需要遍历整个数组,代码如下:
//寻找元素的父亲
void find_fa(int xi) {
chNode* p = NULL;
int flag = 0;
for (int i = 0; i < size; i++) {
p = t[i].first;
while (p!=NULL&&p->chi!=xi)
{
p = p->next;
}
if (p !=NULL && p->chi == xi) {
flag = 1;
printf("%c",t[i].data);
break;
}
}
if (flag == 0) {
printf("根节点,没有父亲结点");
}
}
寻找孩子结点
//寻找孩子结点
void find_ch(int xi) {
chNode* p = t[xi].first;
int chi;
while (p != NULL) {
chi = p->chi;
printf("%c", t[chi].data);
p = p->next;
}
}
整体代码
#include <stdio.h>
#include <stdlib.h>
//存放孩子的单链表结点结构
typedef struct childNode {
int chi;//存放孩子结点的下标
struct childNode* next;//写一个孩子的结点
}chNode;
struct node {
char data;//存放数据
chNode* first;//指向孩子链表
}t[105];
int size;//记录存储数据的个数,同时可以当作指向下一个将要插入的数据位置的指针
//树的初始化
void initTree(char root) {
t[0].data = root;
t[0].first = NULL;
size++;
}
//寻找对应数据的下标
int find(char x) {
for (int i = 0; i < size; i++) {
if (t[i].data == x) {
return i;
}
}
}
//插入树的结点
//如果想要插入,首先要知道插入的元素和它的父亲
void insert(char x, char fx) {
//先将x放入到t数组中,并将x元素的孩子命名为空
t[size].data = x;
t[size].first = NULL;
//然后再将x的下标存放再fx的的链表中
int fxi = find(fx);//寻找fx的下标
//申请空间
chNode* s = (chNode*)malloc(sizeof(chNode));
//为了方便使用,这里我们采用头插法,(无序树)
s->chi = size;
s->next = t[fxi].first;
t[fxi].first = s;
size++;
}
//寻找父亲结点
void find_fa(int xi) {
chNode* p = NULL;
int flag = 0;
for (int i = 0; i < size; i++) {
p = t[i].first;
while (p!=NULL&&p->chi!=xi)
{
p = p->next;
}
if (p !=NULL && p->chi == xi) {
flag = 1;
printf("%c",t[i].data);
break;
}
}
if (flag == 0) {
printf("根节点,没有父亲结点");
}
}
//寻找孩子结点
void find_ch(int xi) {
chNode* p = t[xi].first;
int chi;
while (p != NULL) {
chi = p->chi;
printf("%c", t[chi].data);
p = p->next;
}
}
int main() {
return 0;
}