您的位置:首页 > 理论基础 > 数据结构算法

看数据结构写代码(31)树的二叉链表的实现

2015-03-27 16:03 423 查看
首先向大家推荐一个 很棒的 介绍 树,二叉树,森林之间转换的博客:点击打开链接

源码网盘地址:点击打开链接

树的二叉链表 和 二叉树的 二叉链表 在存储结构上 是一致的,但是 具体 含义 不同; 树节点的 两个指针 指向 第一个孩子节点 和 右兄弟 节点,而 二叉链表指向 左,右子树。这就表示了 他们的操作 既有相同的地方,又有不同的地方。

例如 树的 先序遍历,销毁子树,求树的长度 和 二叉树的 操作 是一样的。

void treeClear(Tree * tree){
if (*tree != NULL)
{
treeClear(&((*tree)->firstChild));
treeClear(&((*tree)->nextSibling));
free(*tree);
*tree = NULL;
}
}

void treeDestory(Tree * tree){
treeClear(tree);
}

//先跟节点 遍历...在孩子
void preOrderTraverse(Tree tree){
if (tree != NULL)
{
printf("%c\t",tree->data);
preOrderTraverse(tree->firstChild);
preOrderTraverse(tree->nextSibling);
}
}


//树求 长度 和 二叉树 求长度 一样.
int treeLen(Tree tree){
if (tree != NULL)
{
return treeLen(tree->firstChild) + treeLen(tree->nextSibling) + 1;
}
return 0;
}


树的后序遍历(没有右子树,所以没有中序遍历)和 二叉树的 中序遍历时一样的。

void postOrderTraverse(Tree tree){
if (tree != NULL)
{
/*postOrderTraverse(tree->firstChild);
postOrderTraverse(tree->nextSibling);
printf("%c\t",tree->data);*/
postOrderTraverse(tree->firstChild);
printf("%c\t",tree->data);
postOrderTraverse(tree->nextSibling);
}
}


树的层序遍历,和二叉树 在形式上 是类似的,树是 将所有孩子节点 入队列,而二叉树 是将 左右子树入队列

//层序遍历 (根二叉树的遍历 略有不同)
void levelOrderTraverse(Tree tree){
if (tree != NULL)
{
LinkQueue queue;
queueInit(&queue);
enqueue(&queue,tree);
while (!queueEmpty(queue))
{
Tree father;
dequeue(&queue,&father);
printf("%c\t",father->data);
Tree child = father->firstChild;//等于第一个孩子//
//将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队)
while (child != NULL)
{
enqueue(&queue,child);
child = child->nextSibling;
}
}
queueDestory(&queue);
}
}
在求树的深度 得注意:二叉树 是求 左右子树 取 最大值 + 1,而 树的 左子树代表 孩子节点, 右子树 代表的是 兄弟,所以 右子树 不 + 1.

//求树的深度
int treeDepth(Tree tree){
if (tree != NULL)
{
int leftDepth = treeDepth(tree->firstChild) + 1;
int rightDepth = treeDepth(tree->nextSibling);//注意 从右子树 返回的 没有 加1,因为右子树返回的是 兄弟节点的 深度
return leftDepth > rightDepth ? leftDepth : rightDepth;
}
return 0;
}
求树的叶子节点 得注意,二叉树 是 遇到 左右子树为空 就 返回1,若是 树在 遇到 firstChild为NULL 就返回,那样 就没有 判断兄弟节点 上的 叶子 节点。

int treeLeafCount(Tree tree){
if (tree != NULL)
{
if (tree->firstChild == NULL)//第一个孩子都不存在,肯定是叶子节点..
{
printf("%c\t",tree->data);
//return 1;//还没有访问 树的 兄弟节点呢。。。
return 1 + treeLeafCount(tree->nextSibling);
}
return treeLeafCount(tree->firstChild) + treeLeafCount(tree->nextSibling);
}
return 0;
}


求树的宽度,求节点的层数,求双亲,求所有孩子节点,求所有兄弟节点 其实 都是 层序遍历,根二叉树的 不同点,上面已经说过。

//求树的宽度(每一层节点数比较之后的最大值)
//层序法
int treeWidth(Tree tree){
int maxWidth = 0;//层的最大节点数
if (tree != NULL)
{
maxWidth = 0;//第一层只有根节点..
int curWidth = 1;//当前层的节点数
int nextWidth = 0;//下一层的节点数..
LinkQueue queue;
queueInit(&queue);
enqueue(&queue,tree);
while (!queueEmpty(queue))
{
for (; curWidth > 0; curWidth--)
{
Tree father;
dequeue(&queue,&father);
Tree child = father->firstChild;//等于第一个孩子//
//将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队)
while (child != NULL)
{
enqueue(&queue,child);
child = child->nextSibling;
nextWidth++;//计算出 每一层的宽度
}
}
curWidth = nextWidth;
maxWidth = maxWidth < nextWidth ? nextWidth : maxWidth;
nextWidth = 0;
}
queueDestory(&queue);
}
return maxWidth;
}

//求节点的层数
//跟 求 树的 宽度 基本上一致
int treeLevel(Tree tree,TDataType data){
int level = 0;
if (tree != NULL)
{
int curWidth =1;
int nextWidth = 0;
LinkQueue queue;
queueInit(&queue);
enqueue(&queue,tree);
while (!queueEmpty(queue))
{
//每循环一次 加 一层
level++;
for (; curWidth > 0; curWidth--)
{
Tree father;
dequeue(&queue,&father);
if (father->data == data)
{
queueDestory(&queue);
return level;
}
Tree child = father->firstChild;//等于第一个孩子//
//将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队)
while (child != NULL)
{
enqueue(&queue,child);
child = child->nextSibling;
nextWidth++;//计算出 每一层的宽度
}
}
curWidth = nextWidth;
nextWidth = 0;
}
queueDestory(&queue);
}
return level;
}

//打印父节点的值
//跟二叉树 不一致,有误区(不能用 tree->firstChild->data == childData 作为判断条件)
//思路 : 层序遍历.
Tree treeGetParent(Tree tree,TDataType childData){
if (tree != NULL)
{
LinkQueue queue;
queueInit(&queue);
enqueue(&queue,tree);
while (!queueEmpty(queue))
{
Tree father;
dequeue(&queue,&father);
Tree child = father->firstChild;//等于第一个孩子//
//将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队)
while (child != NULL)
{
if (child->data == childData)
{
queueDestory(&queue);
return father;
}
enqueue(&queue,child);
child = child->nextSibling;
}
}
queueDestory(&queue);
}
return NULL;
}

//打印所有的孩子节点.
void treeGetAllChild(Tree fatherTree){
if (fatherTree != NULL)
{
printf("%c节点的孩子有:",fatherTree->data);
Tree child = fatherTree->firstChild;
for (; child != NULL; child = child->nextSibling)
{
printf("%c\t",child->data);
}
printf("\n");
}
}

//打印出所有的兄弟节点.
void treeGetAllBrother(Tree tree,TDataType data){
Tree fatherTree = treeGetParent(tree,data);//找到父节点
printf("%c节点的兄弟有:",data);
if (fatherTree != NULL)
{
Tree child = fatherTree->firstChild;
for (; child != NULL; child = child->nextSibling)
{
if (child->data != data)
{
printf("%c\t",child->data);
}
}
printf("\n");
}
}
在求 节点的祖先 和 最近 祖先问题上,原本准备操作 一把,可是 上网百度的时候 看到 一篇 《淘宝面试题,在一亿个节点的树中,查找两个节点的最近祖先》的面试题,网上 有人 给出了 答案,在 树的 二叉 链表 和 三叉链表 数据结构中 的算法 完全不同。可知 三叉链表 多了 一个 父亲 节点 带来 很大的方便 和 效率。

看来 相同问题,不同数据结构,算法步骤不同,算法的效率 和存储空间也不同。选择 合适的 数据结构很重要。

所以 就不准备 写 这两个函数的 具体实现了。

下面 上完整代码:

// BinaryLinkTree.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "queue.h"
#include "stack.h"
#include <cstring>

typedef char TDataType;//树的数据域的类型

//树的二叉链表表示法(孩子兄弟)
typedef struct TreeNode
{
TDataType data;
TreeNode * firstChild;//指向第一个孩子
TreeNode * nextSibling;//指向 下一个兄弟..
} * Tree;

Tree makeNode(TDataType data){
Tree tree = (Tree)malloc(sizeof(TreeNode));
tree->data = data;
tree->firstChild = NULL;
tree->nextSibling = NULL;
return tree;
}

//初始化成空表
void treeInit(Tree * tree){
*tree = NULL;
}

E_State treeCreate(Tree * tree){
TDataType data = ' ';
printf("------------层序创建树(输入根节点数据,#代表空树)-------------\n");
scanf("%c",&data);
if (data != '#')
{
*tree = makeNode(data);
if (*tree == NULL)
{
return E_State_Error;
}
LinkQueue queue;
queueInit(&queue);
enqueue(&queue,*tree);
while (!queueEmpty(queue))
{
qElementType father;
dequeue(&queue,&father);
printf("--------请输入%c节点的 所有 孩子节点(输入exit 表示创建完毕)-------\n",father->data);
char childArray[100];
scanf("%s",childArray);
if (strcmp(childArray,"exit") == 0)
{
break;
}
char * p = childArray;
Tree preTree = NULL;
while (*p != '\0')
{
Tree childTree = makeNode(*p);
if (childTree == NULL)
{
queueDestory(&queue);//记得 要 销毁 队列..
return E_State_Error;
}
if (p == childArray)//第一个孩子节点
{
father->firstChild = childTree;
}
else//设置上一个节点的 兄弟..
{
preTree->nextSibling = childTree;
}
//将节点 入队列..
preTree = childTree;
enqueue(&queue,childTree);
p ++;
}

}
queueDestory(&queue);
}
else//空树
{
treeInit(tree);
}
return E_State_Ok;
}

//释放空间,并设置成空树...
//树的二叉链表形式,存储结构 等同于 二叉树的 二叉链表..
//所以 先释放 第一个孩子节点(左子树),再释放兄弟节点(右子树) 最后 释放 根节点..
void treeClear(Tree * tree){ if (*tree != NULL) { treeClear(&((*tree)->firstChild)); treeClear(&((*tree)->nextSibling)); free(*tree); *tree = NULL; } } void treeDestory(Tree * tree){ treeClear(tree); } //先跟节点 遍历...在孩子 void preOrderTraverse(Tree tree){ if (tree != NULL) { printf("%c\t",tree->data); preOrderTraverse(tree->firstChild); preOrderTraverse(tree->nextSibling); } }

//由于 树的根节点 无 兄弟节点,所以 树根的 右子树 必为空
//所以树 无 后序,只有中序
//用二叉树的 中序 表示.
void postOrderTraverse(Tree tree){ if (tree != NULL) { /*postOrderTraverse(tree->firstChild); postOrderTraverse(tree->nextSibling); printf("%c\t",tree->data);*/ postOrderTraverse(tree->firstChild); printf("%c\t",tree->data); postOrderTraverse(tree->nextSibling); } }

//层序遍历 (根二叉树的遍历 略有不同) void levelOrderTraverse(Tree tree){ if (tree != NULL) { LinkQueue queue; queueInit(&queue); enqueue(&queue,tree); while (!queueEmpty(queue)) { Tree father; dequeue(&queue,&father); printf("%c\t",father->data); Tree child = father->firstChild;//等于第一个孩子// //将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队) while (child != NULL) { enqueue(&queue,child); child = child->nextSibling; } } queueDestory(&queue); } }

bool treeIsEmpty(Tree tree){
return tree == NULL ? true : false;
}

//树求 长度 和 二叉树 求长度 一样.
int treeLen(Tree tree){
if (tree != NULL)
{
return treeLen(tree->firstChild) + treeLen(tree->nextSibling) + 1;
}
return 0;
}

//求树的深度 int treeDepth(Tree tree){ if (tree != NULL) { int leftDepth = treeDepth(tree->firstChild) + 1; int rightDepth = treeDepth(tree->nextSibling);//注意 从右子树 返回的 没有 加1,因为右子树返回的是 兄弟节点的 深度 return leftDepth > rightDepth ? leftDepth : rightDepth; } return 0; }

//求树的叶子树,并打印这些叶子节点.
//只要 firstChild为NULl 就是 叶子节点 (二叉树 是 左右 子树 都为NULL。)
//先序遍历
int treeLeafCount(Tree tree){ if (tree != NULL) { if (tree->firstChild == NULL)//第一个孩子都不存在,肯定是叶子节点.. { printf("%c\t",tree->data); //return 1;//还没有访问 树的 兄弟节点呢。。。 return 1 + treeLeafCount(tree->nextSibling); } return treeLeafCount(tree->firstChild) + treeLeafCount(tree->nextSibling); } return 0; }

//求树的宽度(每一层节点数比较之后的最大值) //层序法 int treeWidth(Tree tree){ int maxWidth = 0;//层的最大节点数 if (tree != NULL) { maxWidth = 0;//第一层只有根节点.. int curWidth = 1;//当前层的节点数 int nextWidth = 0;//下一层的节点数.. LinkQueue queue; queueInit(&queue); enqueue(&queue,tree); while (!queueEmpty(queue)) { for (; curWidth > 0; curWidth--) { Tree father; dequeue(&queue,&father); Tree child = father->firstChild;//等于第一个孩子// //将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队) while (child != NULL) { enqueue(&queue,child); child = child->nextSibling; nextWidth++;//计算出 每一层的宽度 } } curWidth = nextWidth; maxWidth = maxWidth < nextWidth ? nextWidth : maxWidth; nextWidth = 0; } queueDestory(&queue); } return maxWidth; } //求节点的层数 //跟 求 树的 宽度 基本上一致 int treeLevel(Tree tree,TDataType data){ int level = 0; if (tree != NULL) { int curWidth =1; int nextWidth = 0; LinkQueue queue; queueInit(&queue); enqueue(&queue,tree); while (!queueEmpty(queue)) { //每循环一次 加 一层 level++; for (; curWidth > 0; curWidth--) { Tree father; dequeue(&queue,&father); if (father->data == data) { queueDestory(&queue); return level; } Tree child = father->firstChild;//等于第一个孩子// //将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队) while (child != NULL) { enqueue(&queue,child); child = child->nextSibling; nextWidth++;//计算出 每一层的宽度 } } curWidth = nextWidth; nextWidth = 0; } queueDestory(&queue); } return level; } //打印父节点的值 //跟二叉树 不一致,有误区(不能用 tree->firstChild->data == childData 作为判断条件) //思路 : 层序遍历. Tree treeGetParent(Tree tree,TDataType childData){ if (tree != NULL) { LinkQueue queue; queueInit(&queue); enqueue(&queue,tree); while (!queueEmpty(queue)) { Tree father; dequeue(&queue,&father); Tree child = father->firstChild;//等于第一个孩子// //将所有孩子节点 入队(相当于 二叉树的 将 左右 子树 入队) while (child != NULL) { if (child->data == childData) { queueDestory(&queue); return father; } enqueue(&queue,child); child = child->nextSibling; } } queueDestory(&queue); } return NULL; } //打印所有的孩子节点. void treeGetAllChild(Tree fatherTree){ if (fatherTree != NULL) { printf("%c节点的孩子有:",fatherTree->data); Tree child = fatherTree->firstChild; for (; child != NULL; child = child->nextSibling) { printf("%c\t",child->data); } printf("\n"); } } //打印出所有的兄弟节点. void treeGetAllBrother(Tree tree,TDataType data){ Tree fatherTree = treeGetParent(tree,data);//找到父节点 printf("%c节点的兄弟有:",data); if (fatherTree != NULL) { Tree child = fatherTree->firstChild; for (; child != NULL; child = child->nextSibling) { if (child->data != data) { printf("%c\t",child->data); } } printf("\n"); } }
//求节点的祖先
void treeGetAllParent(Tree tree,TDataType data){
}

//求节点的最近公共祖先
TDataType treeFindCommonAncestor(Tree tree,TDataType data1,TDataType data2){
return ' ';
}

int _tmain(int argc, _TCHAR* argv[])
{
Tree tree;
treeCreate(&tree);
printf("------------先序遍历------------\n");
preOrderTraverse(tree);
printf("------------后(中)序遍历------------\n");
postOrderTraverse(tree);
printf("------------层序遍历------------\n");
levelOrderTraverse(tree);
printf("\n树的叶子节点是:");
int leafCount = treeLeafCount(tree);
printf(",叶子节点数为:%d\n",leafCount);
int len = treeLen(tree);
char * isEmpty = treeIsEmpty(tree) ? "是" : "不是";
int depath = treeDepth(tree);
int width = treeWidth(tree);
printf("树是否为空:%s,长度是:%d,深度是:%d,宽度是:%d\n",isEmpty,len,depath,width);
int level = treeLevel(tree,'e');
printf("节点e的所在层数是:%d\n",level);
Tree father = treeGetParent(tree,'h');
printf("h节点的父节点是:%c\n",father->data);
treeGetAllChild(father);
treeGetAllBrother(tree,father->data);
treeDestory(&tree);
return 0;
}
运行截图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: