热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

通用树的双亲表示法(代码演示))

通用树双亲表示法头文件#ifndef__TREE_H__#define__TREE_H__#include<stdio.h>#include<stdlib
//通用树双亲表示法头文件
#ifndef __TREE_H__
#define __TREE_H__

#include
#include
#include

#define FALSE 0
#define TRUE 1

struct _treenode;

typedef struct _childnode //孩子链表内孩子结点
{
struct _childnode *Next;
struct _treenode *ChildNode; //指向孩子结点的指针
}CHILD_NODE;

typedef char TREE_DATA;
typedef struct _treenode //树结点
{
TREE_DATA data;
struct _treenode *Parent; //双亲结点
struct _childnode *ChildList; //孩子结点链表
struct _treenode *Next;
int degree; //树结点的度
}TREE_NODE;

typedef struct _tree
{
struct _treenode *head; //带头结点,方便遍历
int length; //树的总长度(不是高度)
}TREE;

//创建一个空树
extern TREE* CreateTree();
//在指定位置pos插入一个树结点
extern int InsertTreeNode(TREE* tree, TREE_DATA data, int pos);
//打印树
extern void Display(TREE* tree);
//删除指定位置pos的树结点
extern DeleteTreeeNode(TREE *tree, int pos, TREE_DATA* e);
//获得指定位置的树结点数据
extern int GetTreeNode(TREE* tree, int pos, TREE_DATA *e);
//清空一棵树
extern int ClearTree(TREE* tree);
//销毁一棵树
extern int DestoryTree(TREE* tree);
//获得根结点
extern TREE_NODE* GetTreeRoot(TREE* tree);
//获得树的总长度
extern int TreeLength(TREE* tree);
//获得树的高度
extern int TreeHeight(TREE* tree);
//获得树的度
extern int TreeDegree(TREE *tree);

#endif
//tree.c 包含了通用树的常用操作
#include "tree.h"

TREE* CreateTree()
{
TREE* tree = (TREE*)malloc(sizeof(TREE)); //创建一颗空树(包含头结点)
if(tree == NULL)
{
printf("create tree:malloc error!\n");
return NULL;
}
tree->length = 0; //初始长度为0
tree->head = (TREE_NODE*)malloc(sizeof(TREE_NODE)); //带头结点的树
if(tree->head == NULL)
{
printf("create tree head:malloc error!\n");
free(tree);
return NULL;
}
tree->head->degree = 0; //初始化头结点
tree->head->data = 0;
tree->head->Next = NULL;
tree->head->Parent = NULL;
tree->head->ChildList = NULL;

return tree;
}

int InsertTreeNode(TREE* tree, TREE_DATA data, int pos) //在指定位置pos后面插入一个树结点
{
if(tree == NULL || pos < 0 || pos > tree->length)
{
printf("insert tree node[1]:invalid input\n");
return FALSE;
}
if(pos != 0 && tree->length == pos)//不能等于树的长度
{
printf("insert tree node[2]:invalid input\n");
return FALSE;
}

TREE_NODE* node = (TREE_NODE*)malloc(sizeof(TREE_NODE)); //创建一个树结点
if(node == NULL)
{
printf("insert tree node: node malloc error\n");
return FALSE;
}
node->data = data; //初始化数据内容
node->Next = NULL;//指针域指向空

node->ChildList = (CHILD_NODE*)malloc(sizeof(CHILD_NODE)); //创建该树结点的孩子链表头结点
if(node->ChildList == NULL)
{
printf("insert tree node:childlist malloc error\n");
free(node);
return FALSE;
}
node->ChildList->Next = NULL; //初始化孩子链表头结点
node->ChildList->ChildNode = NULL;

int i = 0;
TREE_NODE* _parent = tree->head->Next; //开始寻找双亲结点
for (i = 0; i < pos; i++)
{
_parent = _parent->Next;//如果是根结点,则双亲为空
}
node->Parent = _parent;

if(_parent != NULL) //如果有双亲,则在其孩子链表中新增一个结点指向插入的结点
{
CHILD_NODE* child_node = (CHILD_NODE*)malloc(sizeof(CHILD_NODE));//新增的孩子链表结点
if(child_node == NULL)
{
printf("insert tree node:child node malloc error!\n");
free(node->ChildList);
free(node);
return FALSE;
}
child_node->Next = NULL;
child_node->ChildNode = node;//让孩子链表结点指向需要插入的树结点

CHILD_NODE* tmp = _parent->ChildList; //并且将其插入到双亲的孩子链表后面
while (tmp->Next != NULL)
{
tmp = tmp->Next;
}

tmp->Next = child_node;
_parent->degree++; //别忘了双亲的度加一
}
TREE_NODE* temp = tree->head; //最后将该结点插入到树中
while(temp->Next != NULL)
{
temp = temp->Next;
}
temp->Next = node;
tree->length++; //别忘了树的长度加一

return TRUE;
}

void RDisplay(TREE_NODE* node, int gap)
{
if(node == NULL)
{
printf("R display:no node\n");
return ;
}
int i = 0;
for(i = 0; i < gap; i++)
{
printf("%c", '-');//用来显示树的层次
}
printf("%c\n", node->data);

CHILD_NODE* child = node->ChildList->Next; //当前结点的子结点
while(child != NULL)
{
RDisplay(child->ChildNode, gap + 4); //打印孩子结点的孩子结点......如果是孩子结点,那么就得多打印4个'-'
child = child->Next;
}
}

void Display(TREE* tree)
{
if(tree == NULL)
{
printf("display:invalid input\n");
return ;
}
RDisplay(tree->head->Next, 0); //递归打印结点 这个参数0是用来初始化'-'的。
}

void RDelete(TREE *tree, TREE_NODE* node)
{
if(tree == NULL || node == NULL)
{
printf("R delete:unexpected error\n");
return ;
}
TREE_NODE* tmp = tree->head; //1.将需要删除的树结点移出来,树长度减一
while(tmp->Next != NULL)
{
if(tmp->Next == node)
{
tmp->Next = node->Next;//也就是这一步
tree->length--;
break;
}
tmp = tmp->Next;
}

TREE_NODE* parent = node->Parent; //2.删除双亲结点孩子链表中指向该结点的结点
if(parent != NULL)
{
CHILD_NODE* temp = parent->ChildList;
while(temp->Next != NULL)
{
if(temp->Next->ChildNode == node)
{
CHILD_NODE* p = temp->Next;
temp->Next = p->Next;
free(p);
parent->degree--; //别忘了双亲的度减一
break;
}
temp = temp->Next;
}
}

CHILD_NODE* child = node->ChildList->Next; //3.删除该结点的孩子结点
while (child != NULL)
{
CHILD_NODE* pchild = child->Next;//这边保存child的next域是为了防止程序长时间运行后,该域被意外修改导致程序崩溃
RDelete(tree, child->ChildNode); //4.每个孩子结点的孩子链表处理并且删除其孩子结点
child = pchild;
}

free (node->ChildList); //别忘了释放两个空间
free (node);
}

int DeleteTreeNode(TREE* tree, int pos, TREE_DATA *e) //删除指定位置pos的树结点
{
if(tree == NULL || pos < 0 || pos > tree->length)
{
printf("delete tree node:input invalid\n");
return FALSE;
}
if(pos != 0 && pos == tree->length)
{
printf("delete tree node:input invalid\n");
return FALSE;
}
int i = 0;
TREE_NODE* node = tree->head->Next; //树中第一个结点
for (i = 0; i < pos; i++)
{
node = node->Next; //遍历到需要删除的结点位置
}
*e = node->data; //保存所需删除的结点的数据
RDelete(tree, node); //递归删除

return TRUE;
}

int GetTreeNode(TREE* tree, int pos, TREE_DATA *e)
{
if(tree == NULL || pos < 0 || pos > tree->length)
{
printf("get tree node:input invalid\n");
return FALSE;
}
if(pos != 0 && pos == tree->length)
{
printf("get tree node:input invalid\n");
return FALSE;
}
int i = 0;
//遍历到该结点直接返回即可
TREE_NODE* tmp = tree->head->Next;
for(i = 0; i < pos; i++)
{
tmp = tmp->Next;
}
*e = tmp->data;

return TRUE;
}

int ClearTree(TREE* tree)
{
if(tree == NULL)
{
printf("clear tree:no tree\n");
return FALSE;
}
TREE_DATA e;

return DeleteTreeNode(tree, 0, &e);//其实就是删除根结点
}

int DestoryTree(TREE *tree)
{
if(tree == NULL)
{
printf("destory tree:no tree\n");
return FALSE;
}

ClearTree(tree);

free(tree->head); //清空之后再把刚开始分配的树和头结点释放掉
free(tree);

return TRUE;
}

TREE_NODE* GetTreeRoot(TREE* tree)
{
if(tree == NULL)
{
printf("get tree root:no tree\n");
return FALSE;
}

return tree->head->Next;
}

int TreeLength(TREE* tree)
{
if(tree == NULL)
{
printf("get tree length:no tree\n");
return FALSE;
}

return tree->length;
}

int RHeight(TREE_NODE* node)
{
if(node == NULL)
{
printf("r height:no tree head\n");
return FALSE;
}
int SubHeight = 0;
int max = 0;
CHILD_NODE* child = node->ChildList->Next;//第一个孩子结点
while(child != NULL) //遍历孩子链表
{
SubHeight = RHeight(child->ChildNode); //向下递归找最深的叶子结点,最后一级一级返回求出max
if(SubHeight > max)
{
max = SubHeight;
}
child = child->Next;
}

return max + 1; //由于根结点没算上所以返回max+1
}

int TreeHeight(TREE* tree)
{
if(tree == NULL)
{
printf("get tree height:no tree\n");
return FALSE;
}
int height = RHeight(tree->head->Next);//从第一个结点开始向下递归深入求深度

return height;
}

int RDegree(TREE_NODE* node)
{
if(node == NULL)
{
printf("get tree degree:no tree head\n");
return FALSE;
}
int SubDegree = 0;
int max = node->degree; //假设当前结点度最大
CHILD_NODE* child = node->ChildList->Next;
while(child != NULL)
{
SubDegree = RDegree(child->ChildNode);向下递归找最大度
if(SubDegree > max)
{
max = SubDegree;
}
child = child->Next;
}

return max;
}

int TreeDegree(TREE* tree)
{
if(tree == NULL)
{
printf("get tree degree:no tree\n");
return FALSE;
}
int degree = RDegree(tree->head->Next);

return degree;
}
//main.c 基本测试
#include "tree.h"

int main()
{
TREE* tree = CreateTree();
if(tree == NULL)
{
return -1;
}
InsertTreeNode(tree, 'A', 0);
InsertTreeNode(tree, 'B', 0);
InsertTreeNode(tree, 'C', 0);
InsertTreeNode(tree, 'D', 0);
InsertTreeNode(tree, 'E', 1);
InsertTreeNode(tree, 'F', 1);
InsertTreeNode(tree, 'G', 3);
InsertTreeNode(tree, 'H', 3);
InsertTreeNode(tree, 'I', 3);
InsertTreeNode(tree, 'J', 8);

Display(tree);

printf("删除结点D\n");
TREE_DATA e;
DeleteTreeNode(tree, 3, &e);
Display(tree);

printf("获取结点F的值\n");
TREE_DATA e1;
GetTreeNode(tree, 4, &e1);
printf("F data is %c\n", e1);

printf("获取树的高度\n");
int height = TreeHeight(tree);
printf("树的高度为 %d\n", height);

printf("获取树的度\n");
int degree = TreeDegree(tree);
printf("树的度为 %d\n", degree);

return 0;
}

这是程序的运行结果


推荐阅读
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • 本文介绍了解决二叉树层序创建问题的方法。通过使用队列结构体和二叉树结构体,实现了入队和出队操作,并提供了判断队列是否为空的函数。详细介绍了解决该问题的步骤和流程。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 李逍遥寻找仙药的迷阵之旅
    本文讲述了少年李逍遥为了救治婶婶的病情,前往仙灵岛寻找仙药的故事。他需要穿越一个由M×N个方格组成的迷阵,有些方格内有怪物,有些方格是安全的。李逍遥需要避开有怪物的方格,并经过最少的方格,找到仙药。在寻找的过程中,他还会遇到神秘人物。本文提供了一个迷阵样例及李逍遥找到仙药的路线。 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文介绍了为什么要使用多进程处理TCP服务端,多进程的好处包括可靠性高和处理大量数据时速度快。然而,多进程不能共享进程空间,因此有一些变量不能共享。文章还提供了使用多进程实现TCP服务端的代码,并对代码进行了详细注释。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
author-avatar
一个人灬过世界amp丶_420
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有