【十九】树以及树的存储结构

1、树的定义

这里写图片描述

这里写图片描述

2、相关概念

  • 树的结点包含一个数据及若干指向子树的分支
  • 结点拥有的子树数称为结点的度
    • 度为0的结点称为叶结点
    • 度不为0的结点称为分支结点
  • 树的度定义为所有结点中的度的最大值

这里写图片描述

  • 结点的直接后继称为该结点的孩子
    • 相应的,该结点称为孩子的双亲
  • 结点的孩子的孩子的……称为该结点的子孙
    • 相应的,该结点称为子孙的祖先
  • 同一个双亲的孩子之间互称兄弟

这里写图片描述

  • 结点的层次
    • 根为第1层
    • 根的孩子为第2层
    • ……
  • 树中结点的最大层次称为树的深度或高度

这里写图片描述

  • 如果树中结点的各子树从左向右是有次序的,子树间不能互换位置,则称该树为有序树,否则为无序树。

这里写图片描述

  • 森林是由n (n ≥ 0 ) 棵互不相交的树组成的集合

这里写图片描述

3、小结

这里写图片描述

4、树的存储结构

  • 无法直接用数组表示树的逻辑结构
  • 但可以设计结构体数组对结点间的关系进行表述

例如:

这里写图片描述

5、实现方案

  • 利用链表组织树中的各个结点
  • 链表中的前后关系不代表结点间的逻辑关系
  • 结点的逻辑关系由child数据域描述
  • child数据域保存其他结点的存储地址

数据结构的定义:

对外的数据定义:

//gtree.h
//对外的数据封装,增加程序的通用性
typedef void GTree;
typedef void GTreeData;

内部实现的数据定义:

//gtree.c
//树结点定义
typedef struct _struct_gtreenode GTreeNode;

/*
data: 指向树中保存的用户数据地址
parent:指向该结点的父结点
child:该结点的子结点组织链表
*/
struct _struct_gtreenode
{
  GTreeData *data;
  GTreeNode *parent;
  Linklist *child;
};

//组织链表结点定义
typedef struct _struct_tlnode TLNode;
/*
listNode: 链表结点,包含指向下一个结点的指针
node:保存树结点地址
*/
struct _struct_tlnode
{
  LinklistNode listNode;
  GTreeNode *node;
};

这里写图片描述

这里写图片描述

这里写图片描述

6、部分代码

/*
  将结点node插入到GTree中的pos位置处
  插入成功,返回1,失败,返回0
*/
int GTree_Insert(GTree* tree, GTreeData* data, int parentpos)
{
  int iret = 1;
  iret = iret && (tree != NULL) && (data != NULL) && (parentpos <= List_Length(tree));
  if(iret)
  {
    GTreeNode *treeNode = (GTreeNode*)malloc(sizeof(GTreeNode));
    //创建主组织结点,并插入主组织链表中
    TLNode *tlNode = (TLNode*)malloc(sizeof(TLNode));

    iret =(treeNode != NULL) && (tlNode != NULL);
    if(iret)
    {
      treeNode->data = data;
      treeNode->parent = NULL;//假设当前结点没有父结点也没有子结点
      treeNode->child = NULL;

      tlNode->node = treeNode;
      iret = List_Insert(tree,(LinklistNode*)tlNode,List_Length(tree));

      //如果未插入成功,则可释放内存
      if(!iret)
      {
        free(tlNode);
      }

      //尝试获取当前结点的父结点
      TLNode *parentNode = (TLNode*)List_Get(tree,parentpos);

      if(parentNode != NULL)
      {
        //如果有父结点存在,将当前结点,插入父结点的子结点的组织结构链表中
        treeNode->parent = parentNode->node;
        //如果父结点的组织结构链表为空,则先创建它
        if(parentNode->node->child == NULL)
        {
          parentNode->node->child = List_Create();
        }
        TLNode *pchildNode = (TLNode*)malloc(sizeof(TLNode));
        //如果这里条件判断失败,也有可能是上面的插入操作失败造成的
        iret = iret && (pchildNode != NULL) && (parentNode->node->child != NULL);
        if(iret)
        {
          pchildNode->node = treeNode;
          iret = List_Insert(parentNode->node->child,(LinklistNode*)pchildNode,
          List_Length(parentNode->node->child));
        }
        //如果未插入成功,则可释放内存
        if(!iret)
        {
          free(pchildNode);
        }
      }
    }
    else
    {
      free(tlNode);
      free(treeNode);
    }
  }
  return iret;
}
/*
  递归函数
  核心思想是:要删除一个结点,递归删除它和它的所有子结点
*/
static void recursivr_delete(GTree *tree,GTreeNode *node)
{
  if((tree != NULL) && (node != NULL))
  {
    //把它从主组织链表中删除
    for(int i = 0; i < List_Length(tree); i++)
    {
      TLNode *tlNode = (TLNode*)List_Get(tree,i);
      if ((tlNode != NULL) && (tlNode->node == node))
      {
        List_Delete(tree,i);
        free(tlNode);
        break;
      }
    }
    //如果它有父结点,把它从它的父结点中的子结点组织链表中删除
    GTreeNode *parentNode = node->parent;
    if(parentNode != NULL)
    {
      for(int i = 0; i<List_Length(parentNode->child); i++)
      {
        TLNode *childNode = (TLNode*)List_Get(parentNode->child,i);
        if(childNode->node == node)
        {
          List_Delete(parentNode->child,i);
          free(childNode);
          break;
        }
      }
    }
    //如果它有子结点,递归删除它所有的子结点
    if(node->child != NULL)
    {
      for(int i = 0; i < List_Length(node->child); i++)
      {
        TLNode *childNode = (TLNode*)List_Get(node->child,i);
        if(childNode != NULL)
        {
          recursivr_delete(node->child,childNode->node);
        }
      }
    }
    free(node);
  }
}

/*
  将GTree中pos位置的结点删除并返回其中的数据
  删除成功,返回结点中的数据指针,失败,返回NULL
*/
GTreeData* GTree_Delete(GTree* tree, int pos)
{
  GTreeData *ret = NULL;
  TLNode *treeNode = (TLNode*)List_Get(tree,pos);
  if(treeNode != NULL)
  {
    ret = treeNode->node->data;
    recursivr_delete(tree,treeNode->node);
  }
  return ret;
}
/*
  递归函数
  求树的高度,将问题转化为求一个结点的子结点高度,如此递归
*/
static int recursivr_height(GTree *tree,GTreeNode *node)
{
  int iret = 0;
  if((tree != NULL) && (node != NULL))
  {
    int subHeight = 0;
    //获取当前结点的所有子节点
    for(int i = 0; i < List_Length(node->child); i++)
    {
      TLNode *childNode = (TLNode*)List_Get(node->child,i);
      if(childNode != NULL)
      {
        //递归,获取子节点的高度
        subHeight = recursivr_height(tree,childNode->node);
      }
      if(iret < subHeight)
      {
        iret = subHeight;
      }
    }
  //加上自己的高度1
  iret = iret + 1;
  }
  return iret;
}

/*
  返回GTree的高度
  如果成功,返回非负值,否则返回-1
*/
int GTree_Height(GTree* tree)
{
  int iret = -1;
  TLNode *root = (TLNode*)List_Get(tree,0);
  if(root != NULL)
  {
    iret = recursivr_height(tree,root->node);
  }
  return iret;
}
/*
  递归调用
  求树的度,问题转化为求每个子节点的度,取最大值
*/
static int recursivr_degree(GTreeNode *node)
{
  int iret = 0;
  if(node != NULL)
  {
    int subDegree = 0;
    //获取当前结点子结点数目
    iret = List_Length(node->child);
    //获取当前结点,所有子结点的度
    for(int i = 0; i < List_Length(node->child); i++)
    {
      TLNode *childNode = (TLNode*)List_Get(node->child,i);
      if(childNode != NULL)
      {
        subDegree = recursivr_degree(childNode->node);
      }
      //取最大值,最为当前结点的度
      if(iret < subDegree)
      {
        iret = subDegree;
      }
    }
  }
  return iret;
}

/*
  返回树的度数
  如果成功,返回非负值,失败,返回-1
*/
int GTree_Degree(GTree* tree)
{
  int iret = -1;
  TLNode *root = (TLNode*)List_Get(tree,0);
  if(root != NULL)
  {
    iret = recursivr_degree(root->node);
  }
  return iret;
}
/*
  递归调用,显示当前结点的值,及其所有子结点的值
*/
static void recursivr_display(GTreeNode *node,GTree_Printf pfunc, int subindex, char div,int gap)
{
  if(node != NULL)
  {
    for(int i = 0; i < subindex; i++)
    {
      printf("%c",div);
    }
    //回调函数
    pfunc(node->data);
    if(node->child != NULL)
    {
      for(int i = 0; i < List_Length(node->child); i++)
      {
        TLNode *childNode = (TLNode*)List_Get(node->child,i);
        if(childNode != NULL)
        {
          recursivr_display(childNode->node,pfunc,subindex + gap,div,gap);
        }
      }
    }
  }
}

/*
  显示树的结构和内容
  tree:树的指针
  pfunc:毁掉函数指针,由用户自行编写
  div:自定义分割符
  gap:自定义间隔
*/
void GTree_Display(GTree* tree, GTree_Printf pfunc, char div,int gap)
{
  TLNode *root = (TLNode*)List_Get(tree,0);
  if(root != NULL)
  {
    recursivr_display(root->node,pfunc,0,div,gap);
  }
}

7、完整源码下载

文件名:gtree-1.0.tar.gz
链接: http://pan.baidu.com/s/1o6OLhYq 密码: tew8

编译步骤:

0.1 解压缩:tar -zxvf gtree-1.0.tar.gz
0.2 进入目录:./configure
0.3 生成Seqlist:make
0.4 运行程序:./GTree

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值