看数据结构写代码(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;
}
相关文章推荐
- 看数据结构写代码(23) 二叉链表的实现
- 看数据结构写代码(28) 线索二叉链表的实现
- 【数据结构与算法】【排序】冒泡排序的代码实现
- 【数据结构与算法】【排序】简单选择排序的代码实现
- 【数据结构与算法】【排序】直接插入排序的代码实现
- 【数据结构与算法】【排序】希尔排序的代码实现
- 【数据结构与算法】【排序】堆排序的代码实现
- 【数据结构与算法】【排序】归并排序的代码实现
- 【数据结构与算法】【排序】快速排序的代码实现
- 【数据结构与算法】【查找】顺序查找(线性查找)的代码实现
- 【数据结构与算法】【查找】折半查找(二分查找)的代码实现
- 【数据结构与算法】【查找】插值查找的代码实现
- 【数据结构与算法】【查找】斐波那契查找的代码实现
- 数据结构之线性表――顺序存储结构(php代码实现)
- 数据结构之线性表――链式存储结构之单链表(php代码实现)
- 数据结构之队列――顺序存储结构(php代码实现――方法一)
- 数据结构之队列――顺序存储结构(php代码实现――方法二)
- 数据结构之队列――链式存储结构(php代码实现)
- 数据结构之完全二叉树――顺序存储结构(php代码实现)
- 数据结构之二叉树――链式存储结构(php代码实现)