树的定义及基本操作

来源:互联网 发布:smtp pop 端口号 编辑:程序博客网 时间:2024/05/18 20:07

首先,什么是树呢?
数的定义是递归的:
定义树是满足以下条件的,包含至少一个结点的有限集合:
(1)树中有一个特别指定的结点,称为根,或树根。
(2)其它结点划分成n>=0个不相交的集合T1…Tn ,每个集合又还是一棵树,但称为根的子树。
树的主要操作包括:求树的深度、求给定节点的子节点、兄弟节点、遍历树、插入子树、删除子树等等。因为树的定义本身就是递归的,所以在实际的程序中,很多操作也是通过递归来完成的。
树是如何表示的呢?有一种常用的表示方法,成为孩子、兄弟表示法,对于每个节点,通过孩子指针指向自己的子节点,通过兄弟指针指向自己右边的兄弟。

typedef char ElemType;//通过第一个孩子和下一个兄弟来确定整个树typedef struct TNode{ElemType data;TNode *firstChild,*nextSibling;}TNode,*Tree;//初始化一个节点Tree initTree(ElemType e){Tree pT;pT = (TNode*)malloc(sizeof(TNode));pT->firstChild = NULL;pT->nextSibling = NULL;pT->data = e;return pT;}//删除树void deleteTree(Tree *T){if(NULL == *T)return ;deleteTree(&(*T)->nextSibling);deleteTree(&(*T)->firstChild);free(*T);*T = NULL;}//遍历树并打印void printTree(Tree T){if(NULL == T)return;else{printf("%c ",T->data);/*//如果不习惯递归,可以这样写Tree p = T->firstChild;while(p != NULL){printTree(p);p = p->nextSibling;}*///注释掉的部分等价于如下两行:printTree(T->firstChild);printTree(T->nextSibling);}}//求树的高度int treeDepth(Tree T){int hmax = 0;int subTreeDepth = 0;if(NULL == T)return 0;Tree p = T->firstChild;while(p != NULL){subTreeDepth = treeDepth(p);p = p->nextSibling;if(subTreeDepth > hmax)hmax=subTreeDepth;}return hmax+1;}//全局变量记录找到的元素的地址Tree result;void locateElem(Tree T,ElemType e){if(NULL == T)return;if(T->data == e) result = T;/*Tree p = T->firstChild;while(p != NULL){locateElem(p,e);p = p->nextSibling;}*/locateElem(T->firstChild,e);locateElem(T->nextSibling,e);}//返回子节点的指针Tree findChild(Tree T,ElemType e){result = NULL;locateElem(T,e);//如果没有找到或者该节点是叶子节点,返回空if(NULL == result && NULL == result->firstChild)return NULL;elsereturn result->firstChild;}//返回兄弟节点的指针Tree findSibling(Tree T,ElemType e){result = NULL;locateElem(T,e);//如果没有找到或者该节点没有右兄弟,返回空if(NULL == result && NULL == result->nextSibling)return NULL;elsereturn result->nextSibling;}//插入子树bool insertTree(Tree T,Tree Tadd,ElemType e){result = NULL;locateElem(T,e);if(result != NULL){Tadd->nextSibling = result->firstChild;result->firstChild = Tadd;return true;}elsereturn false;}//删除子树bool deleteSubTree(Tree T,ElemType e){result = NULL;locateElem(T,e);//先判断result有什么节点if(result->firstChild != NULL){deleteTree(&(result->firstChild));return true;}elsereturn false;}

初始化节点时要注意,这里通过函数的返回值将动态分配的节点带出去了,但是并没有说明节点之间的关系,所以需要在主函数中手动的为每个节点连接它的孩子和兄弟指针。对于树的核心操作,毫无疑问是如何递归的遍历一棵树。我们的作法是这样的:对于一个节点,先访问它的数据域,然后通过孩子指针来访问它的孩子,一直到沿着这条分支的叶子节点,此时,访问这个节点的兄弟,直到所有的兄弟都访问过了,然后回退到上一节点,访问它的兄弟,依次类推。
在程序中,有两种写法,以printTree为例,注释掉的部分更贴近于我们描述,而实际使用的代码则更加抽象、简洁。按理来说,二者是可以相互转化的,但是对于在求树的高度这一块,我并没有好的办法解决。
对于通过递归操作操作的产找,有一个很烦人的问题,就是找到以后如何把指向这个节点的指针带出来。我试了很多方法,都不管用,无奈之下使用了全局变量。所以每次进行跟查找有关的操作时(查找、指定位置插入、指定位置删除),都必须先初始化result指针为NULL。如果读者有更好的办法,欢迎指教。
这里并没有给出通过一个节点来查找到它的父节点的操作,虽然这个工作“可能”可以完成,但最简单明了的办法就是在结构体内部增加一个指向parent的指针。

原创粉丝点击