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

来源:互联网 发布:如何用阿里云os系统 编辑:程序博客网 时间:2024/05/29 19:08

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

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

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

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

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;}
运行截图:







1 0
原创粉丝点击