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

数据结构-树(1)

一、树的基本概念

二,树的抽象数据结构

三,树的存储结构

1.双亲表示法

  • 数组存储结点,含数据域和双亲下标(根结点双亲为 - 1)

代码示例

  • include <stdio.h>
    #include <stdlib.h>#define MAX_TREE_SIZE 100
    typedef int TElemType;// 双亲表示法结点结构
    typedef struct PTNode {TElemType data;        // 结点数据int parent;           // 双亲位置下标(-1表示无双亲)
    } PTNode;// 树结构
    typedef struct {PTNode nodes[MAX_TREE_SIZE];  // 结点数组int r, n;                   // 根的位置和结点数
    } PTree;// 初始化树(示例:构建文档中的树结构)
    void InitTree(PTree* T) {TElemType data[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' };T->n = 10;T->r = 0;  // 根结点下标为0// 设定各结点的双亲T->nodes[0].data = data[0];T->nodes[0].parent = -1;T->nodes[1].data = data[1];T->nodes[1].parent = 0;T->nodes[2].data = data[2];T->nodes[2].parent = 0;T->nodes[3].data = data[3];T->nodes[3].parent = 1;T->nodes[4].data = data[4];T->nodes[4].parent = 2;T->nodes[5].data = data[5];T->nodes[5].parent = 2;T->nodes[6].data = data[6];T->nodes[6].parent = 3;T->nodes[7].data = data[7];T->nodes[7].parent = 3;T->nodes[8].data = data[8];T->nodes[8].parent = 3;T->nodes[9].data = data[9];T->nodes[9].parent = 4;
    }// 输出每个结点的双亲
    void PrintParents(PTree T) {printf("结点\tdata\tparent\n");for (int i = 0; i < T.n; i++) {printf("%d\t%c\t%d\n", i, T.nodes[i].data, T.nodes[i].parent);}
    }int main() {PTree T;InitTree(&T);printf("双亲表示法存储结构:\n");PrintParents(T);return 0;
    }

2.孩子表示法

每个结点有多个指针域,其中每个指针指向一颗子树的根结点,我们把这种方法叫做多重链表表示法

有两种方案解决

方案一:

一种是指针域的个数就等于树的度,树的度是树各个结点度的最大值

这种方法对于树中的各个结点的度相差很大时,显然是很浪费空间的,因为有很多的结点,它的指针域是空的。

不过如果树的各结点度相差很小时,那就意味着开辟的空间被充分利用了,这时存储结构的缺点反而变成了优点

方案二

第二种方案每个结点指针域的个数等于该结点的度,我们专门取一个位置来存储结点指针域的个数

这种方法克服了浪费空间的缺点,对空间利用率是很高的,但是由于各个结点的链表是不相同的结构,加上要维护结点的度的 数值,在运算上就会带来时间上的损耗

这时,我们想到了更好的办法,既可以减少空指针的浪费又能使结点结构相同

这就是孩子表示法,具体办法就是,把每个结点的孩子结点排列起来,以单链表作存储结构,则n个结点有n个孩子链表,如果是叶子结点则此单链表为空;然后n个指针又组成一个线性表,采用顺序存储结构,存放进一个一维数组中

/*树的孩子表示法的结构定义代码*/
#define MAX_TREE_SIZE 100
typedef struct CTNode //孩子结点
{int child;struct CTNode *next;
}*ChildPtr;typedef struct //表头结构
{TElemType data;ChildPtr firstchild;
}*CTBox;typedef struct
{CTBox nodes[MAX_TREE_SIZ];int r,n;
}CTree;

代码示例:

#include <stdio.h>
#include <stdlib.h>#define MAX_TREE_SIZE 100
typedef char TElemType; // 修改为char类型以匹配数据初始化// 孩子结点
typedef struct CTNode {int child;          // 孩子结点下标struct CTNode* next; // 指向下一个孩子的指针
} *ChildPtr;// 表头结构
typedef struct CTBox {TElemType data;      // 结点数据ChildPtr firstchild; // 第一个孩子指针
} CTBox;// 树结构
typedef struct {CTBox nodes[MAX_TREE_SIZE];  // 表头数组int r, n;                   // 根的位置和结点数
} CTree;// 创建新的孩子结点
ChildPtr createChildNode(int childIndex) 
{ChildPtr newNode = (ChildPtr)malloc(sizeof(struct CTNode));if (newNode == NULL) {printf("内存分配失败\n");exit(EXIT_FAILURE);}newNode->child = childIndex;newNode->next = NULL;return newNode;
}// 添加孩子结点到指定父结点
void addChild(CTree* T, int parentIndex, int childIndex)
{ChildPtr newNode = createChildNode(childIndex);newNode->next = T->nodes[parentIndex].firstchild;T->nodes[parentIndex].firstchild = newNode;
}// 创建完整的树结构(示例:构建书中图6-4-1的树)
void CreateChildTree(CTree* T) 
{TElemType data[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' };T->n = 10;T->r = 0;  // 根结点下标为0// 初始化表头数据for (int i = 0; i < T->n; i++) {T->nodes[i].data = data[i];T->nodes[i].firstchild = NULL;}// 添加所有孩子结点关系addChild(T, 0, 1); // A的孩子: BaddChild(T, 0, 2); // A的孩子: CaddChild(T, 1, 3); // B的孩子: DaddChild(T, 2, 4); // C的孩子: EaddChild(T, 2, 5); // C的孩子: FaddChild(T, 3, 6); // D的孩子: GaddChild(T, 3, 7); // D的孩子: HaddChild(T, 3, 8); // D的孩子: IaddChild(T, 4, 9); // E的孩子: J
}// 输出每个结点的孩子列表
void PrintChildren(CTree T) 
{printf("结点\tdata\t孩子列表\n");for (int i = 0; i < T.n; i++) {printf("%d\t%c\t", i, T.nodes[i].data);ChildPtr p = T.nodes[i].firstchild;while (p) {printf("%d(%c) ", p->child, T.nodes[p->child].data);p = p->next;}printf("\n");}
}// 释放树的内存
void destroyTree(CTree* T) 
{for (int i = 0; i < T->n; i++) {ChildPtr current = T->nodes[i].firstchild;while (current != NULL) {ChildPtr next = current->next;free(current);current = next;}}
}int main() {CTree T;CreateChildTree(&T);printf("孩子表示法存储结构:\n");PrintChildren(T);destroyTree(&T); // 释放内存return 0;
}

这时,也存在着问题,我如何知道某个结点的双亲是谁呢?比较麻烦,需要整棵树遍历才行。

我们可以把双亲表示法和孩子表示法综合一下。

 

代码示例:

#include <stdio.h>
#include <stdlib.h>#define MAX_TREE_SIZE 100
typedef char TElemType; // 结点数据类型(示例为字符型)// 孩子结点结构(用于孩子链表)
typedef struct ChildNode 
{int child_idx;       // 孩子结点在数组中的下标struct ChildNode* next; // 指向下一个兄弟结点
} ChildNode;// 双亲孩子表示法的结点结构
typedef struct PTNode 
{TElemType data;           // 结点数据int parent_idx;          // 双亲结点下标(-1表示根结点)ChildNode* first_child;  // 第一个孩子结点指针
} PTNode;// 树结构
typedef struct 
{PTNode nodes[MAX_TREE_SIZE]; // 结点数组int root_idx;                // 根结点下标int node_count;              // 结点总数
} ParentChildTree;// 创建孩子结点
ChildNode* create_child_node(int child_idx) 
{ChildNode* node = (ChildNode*)malloc(sizeof(ChildNode));node->child_idx = child_idx;node->next = NULL;return node;
}// 向双亲孩子结点中添加孩子
void add_child(ParentChildTree* tree, int parent_idx, int child_idx) 
{ChildNode* new_node = create_child_node(child_idx);// 将新孩子插入到孩子链表头部(或尾部,此处示例为头部)new_node->next = tree->nodes[parent_idx].first_child;tree->nodes[parent_idx].first_child = new_node;// 设置孩子的双亲tree->nodes[child_idx].parent_idx = parent_idx;
}// 初始化示例树(构建书中图6-4-1的树结构)
void init_example_tree(ParentChildTree* tree) 
{// 初始化结点数据TElemType data[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J' };tree->node_count = 10;tree->root_idx = 0; // 根结点下标为0// 初始化每个结点的基本信息(无孩子和双亲)for (int i = 0; i < tree->node_count; i++) {tree->nodes[i].data = data[i];tree->nodes[i].parent_idx = -1;tree->nodes[i].first_child = NULL;}// 添加孩子关系(按书中示例树结构)// A的孩子:B(下标1)、C(下标2)add_child(tree, 0, 1);add_child(tree, 0, 2);// B的孩子:D(下标3)add_child(tree, 1, 3);// C的孩子:E(下标4)、F(下标5)add_child(tree, 2, 4);add_child(tree, 2, 5);// D的孩子:G(下标6)、H(下标7)、I(下标8)add_child(tree, 3, 6);add_child(tree, 3, 7);add_child(tree, 3, 8);// E的孩子:J(下标9)add_child(tree, 4, 9);
}// 打印树的存储结构
void print_tree(ParentChildTree tree) 
{printf("结点\t数据\t双亲\t孩子列表\n");for (int i = 0; i < tree.node_count; i++) {PTNode node = tree.nodes[i];printf("%d\t%c\t", i, node.data);printf("%d\t", node.parent_idx);// 打印孩子列表ChildNode* child = node.first_child;printf("孩子: ");while (child) {printf("%d ", child->child_idx);child = child->next;}printf("\n");}
}// 释放内存(避免内存泄漏)
void free_tree(ParentChildTree* tree) 
{for (int i = 0; i < tree->node_count; i++) {ChildNode* child = tree->nodes[i].first_child;while (child) {ChildNode* temp = child;child = child->next;free(temp);}}
}int main() {ParentChildTree tree;init_example_tree(&tree);printf("双亲孩子表示法存储结构:\n");print_tree(tree);free_tree(&tree); // 释放内存return 0;
}

3.孩子兄弟表示法

任一棵树,它的结点的第一个孩子如果存在就是唯一的,它的右兄弟如果存在也是唯一的。因此,我们设置两个指针,分别指向该结点的第一个孩子和此结点的右兄弟

                                

其中 data 是数据域,frstchikd 为指针域,存储该结点的第一个孩子结点的存储地址,rightsib是指针域,存储该结点的右兄弟结点的存储地址。

/*树的孩子兄弟表示法结构定义*/
typedef struct CSNode
TElemType datar
struct CsNode *firstchild,*rightsib;CSNode,*CsTreei

代码示例:

#include <stdio.h>
#include <stdlib.h>typedef int TElemType;// 孩子兄弟表示法结点结构
typedef struct CSNode {TElemType data;struct CSNode* firstchild, * rightsib;
} CSNode, * CSTree;// 创建示例树(简单二叉树结构)
CSTree CreateCSTree() {// 手动构建示例树(根结点A,左孩子B,右孩子C)CSNode* A = (CSNode*)malloc(sizeof(CSNode));CSNode* B = (CSNode*)malloc(sizeof(CSNode));CSNode* C = (CSNode*)malloc(sizeof(CSNode));A->data = 'A';A->firstchild = B;A->rightsib = NULL;B->data = 'B';B->firstchild = NULL;B->rightsib = C;C->data = 'C';C->firstchild = NULL;C->rightsib = NULL;return A;
}// 前序遍历孩子兄弟表示法的树
void PreOrder(CSTree T) {if (T) {printf("%c ", T->data);PreOrder(T->firstchild);  // 遍历长子PreOrder(T->rightsib);    // 遍历右兄弟}
}int main() {CSTree T = CreateCSTree();printf("孩子兄弟表示法遍历结果:\n");PreOrder(T);  // 输出: A B Creturn 0;
}

如果有必要,可以再增加一个parent指针域来解决快速查找双亲的问题

代码如下:

#include <stdio.h>
#include <stdlib.h>typedef int TElemType;// 孩子兄弟表示法结点结构,增加parent指针域
typedef struct CSNode {TElemType data;struct CSNode* firstchild, * rightsib;struct CSNode* parent; // 新增parent指针,指向父结点
} CSNode, * CSTree;// 创建示例树(简单二叉树结构)
CSTree CreateCSTree() {// 手动构建示例树(根结点A,左孩子B,右孩子C)CSNode* A = (CSNode*)malloc(sizeof(CSNode));CSNode* B = (CSNode*)malloc(sizeof(CSNode));CSNode* C = (CSNode*)malloc(sizeof(CSNode));A->data = 'A';A->firstchild = B;A->rightsib = NULL;A->parent = NULL; // 根结点的parent为NULLB->data = 'B';B->firstchild = NULL;B->rightsib = C;B->parent = A; // B的父结点是AC->data = 'C';C->firstchild = NULL;C->rightsib = NULL;C->parent = A; // C的父结点是Areturn A;
}// 前序遍历孩子兄弟表示法的树
void PreOrder(CSTree T) {if (T) {printf("%c ", T->data);PreOrder(T->firstchild);  // 遍历长子PreOrder(T->rightsib);    // 遍历右兄弟}
}// 通过parent指针查找指定结点的双亲
CSNode* FindParent(CSTree T, TElemType target) {if (T == NULL) {return NULL;}if (T->data == target) {return T->parent;}CSNode* temp = T->firstchild;while (temp) {CSNode* parent = FindParent(temp, target);if (parent) {return parent;}temp = temp->rightsib;}return NULL;
}int main() {CSTree T = CreateCSTree();printf("孩子兄弟表示法遍历结果:\n");PreOrder(T);  // 输出: A B Cprintf("\n");TElemType target = 'B';CSNode* parent = FindParent(T, target);if (parent) {printf("结点%c的双亲是%c\n", target, parent->data);}else {printf("未找到结点%c的双亲\n", target);}return 0;
}

http://www.xdnf.cn/news/407197.html

相关文章:

  • 硬件设备基础
  • 豆瓣电影Top250数据工程实践:从爬虫到智能存储的技术演进(含完整代码)
  • Mysql使用PXC实现高可用
  • js 字符串中的特殊字符全部替换成定义对象里面key对应的value值(进阶篇)
  • Python60日基础学习打卡D12【虫豸版】
  • 如何使用 React Hooks 替代类组件的生命周期方法?
  • Linux服务器连接SSH工具FinalShell安装使用支持Linux文件上传下载
  • (自用)Java学习-5.8(总结,springboot)
  • 【合新通信】无人机天线拉远RFOF(射频光纤传输)解决方案
  • upload-labs通关笔记-第01关 文件上传之前端绕过(3种渗透方法)
  • 浙江大学 deepseek 公开课 第三季 第3期 - 陈喜群 教授 (附PPT下载) by 突破信息差
  • Linux笔记---信号(上)
  • SWMM在城市排水防涝规划中的实战应用:模型校准、情景模拟与工程决策
  • Linux进程10-有名管道概述、创建、读写操作、两个管道进程间通信、读写规律(只读、只写、读写区别)、设置阻塞/非阻塞
  • WordPress 网站上的 jpg、png 和 WebP 图片插件
  • 请解释 React Native 的新架构(Fabric 和 TurboModules)与旧架构的主要区别
  • 「光域」系列激光测距传感器:以光为尺,重构空间认知边界
  • 【华为HCIP | 华为数通工程师】821—多选解析—第二十二页
  • 详解 IRC协议 及客户端工具 WeeChat 的使用
  • OpenCV进阶操作:光流估计
  • Linux基础命令之目录管理——了解各种操作文件目录的命令,万字教学,超详细!!!(1)
  • OCCT知识笔记之分解BOX
  • 计算频谱的方法
  • 《基于 Kubernetes 的 WordPress 高可用部署实践:从 MariaDB 到 Nginx 反向代理》
  • 《AI大模型应知应会100篇》第59篇:Flowise:无代码搭建大模型应用
  • 免费批处理软件一键修改文件名称
  • 了解docker-compose.yml
  • mac一键安装gpt-sovit教程中,homebrew卡住不动的问题
  • latex控制表格宽度,不要超出页面
  • windows系统使用phpstudy安装ssl证书