门户网站流量,黄永玉的艺术人生,中细软做的网站,聚名网怎么赚钱树#xff08;Tree#xff09;的定义及基本概念
树的定义
树(Tree)是个结点的有限集合T#xff0c;它满足两个条件#xff1a;
有且仅有一个特定的称为根#xff08;Root#xff09;的节点#xff1b;其余的节点分为个互不相交的有限合集#xff0c;其中每一个集合又…树Tree的定义及基本概念
树的定义
树(Tree)是个结点的有限集合T它满足两个条件
有且仅有一个特定的称为根Root的节点其余的节点分为个互不相交的有限合集其中每一个集合又是一棵树并称为其根的子树。
表示方法树形表示法目录表示法。 树的基本概念
一个节点的子树的个数称为该节点的度数。
一颗树的度数是指该树中节点的最大度数。
度数为零的节点称为树叶或终端节点。
度数不为零的节点称为分支节点。
一个节点系列并满足是的父节点就称为一条从到的路径路径的长度为即路径中的边数。
路径中前面的节点是后面节点的祖先后面节点是前面节点的子孙。
节点的层数等于父节点的层数加一根节点的层数定义为一。树中节点层数的最大值称为该树的高度或深度。
若树中每个节点的各个子树的排列为从左到右不能交换即兄弟之间是有序的则该树称为有序树。
棵互不相交的树的集合称为森林。
树去掉根节点就成为森林森林加上一个新的根节点就成为树。
树的逻辑结构
树中任何节点都可以有零个或多个直接后继节点子节点但至多只有一个直接前趋节点父节点根节点没有前趋节点叶节点没有后继节点。
二叉树
二叉树的逻辑结构 二叉树是个节点的有限集合或者是空集 或者是由一个根节点以及两棵互不相交的、分别称为左子树和右子树的二叉树组成 严格区分左孩子和右孩子即使只有一个子节点也要区分左右。
二叉树的性质
二叉树第层上的节点最多为个。
深度为的二叉树最多有个节点。
满二叉树
深度为时有个节点的二叉树。
完全二叉树
只有最下面两层有度数小于2的节点且最下面一层的叶节点集中在最左边的若干位置上。
具有n个节点的完全二叉树的深度为
或
二叉树的存储结构
二叉树的顺序存储
完全二叉树节点的编号方法是从上到下从左到右根节点为1号节点设完全二叉树的节点数为,某节点编号为
当不是根节点时有父节点其编号为当时有左孩子其编号为,否则没有左孩子本身是叶节点当时有右孩子其编号为 ,否则没有右孩子当为奇数且不为1时有左兄弟其编号为,否则没有左兄弟当为偶数且小于时有右兄弟其编号为,否则没有右兄弟。
有个节点的完全二叉树可以用有个元素的数组进行顺序存储节点号和数组下标一一对应下标为零的元素不用。
利用以上特性可以从下标获得节点的逻辑关系。不完全二叉树通过添加虚节点构成完全二叉树然后用数组存储这要浪费一些存储空间。
二叉树的链式存储
定义一个二叉树节点类型结构体bitree每个结点包括三个域
数据域data存放每个节点的数据左孩子指针域left存放指向左孩子的指针如果没有左孩子则为NULL右孩子指针域right存放指向右孩子的指针如果没有右孩子则为NULL。 在头文件tree.h中定义二叉树结构体
typedef char data_t;typedef struct node_t {data_t data;//二叉树节点数据域struct node_t *left;//二叉树节点左孩子指针域struct node_t *right;//二叉树节点右孩子指针域
}bitree;//二叉树节点类型别名
二叉树的四种基本遍历算法
遍历的含义
遍历是指沿某条搜索路径周游二叉树对树中的每一个节点访问一次且仅访问一次。
二叉树是非线性结构每个结点有两个后继则存在如何遍历即按什么样的搜索路径进行遍历的问题。
由于二叉树的递归性质遍历算法也是递归的。
对于下面这棵树的遍历可以通过什么算法实现呢 遍历算法先序遍历
先序遍历是指先访问树根再访问左子树最后访问右子树。
先序遍历算法
若二叉树为空树则空操作否则
访问根节点先序遍历左子树先序遍历右子树。
这是一个递归算法不断遍历左子树和右子树直到左子树和右子树均为NULL结束遍历依次所访问的节点即为遍历结果。
为了实现算法在定义了上述bitree结构体后声明一个数据类型为bitree的指针函数tree_create()和先序遍历函数preorder()
在tree.h文件中声明树的创建函数和先序遍历函数
bitree *tree_create();//创建二叉树函数
void preorder(bitree *r);//先序遍历函数
在tree.c中实现树的创建函数tree_create()
#include stdio.h
#include stdlib.h
#include linkqueue.h//队列头文件在层次遍历过程中需要用到队列bitree *tree_create() {data_t ch;bitree *r;scanf(%c, ch);//获取用户输入的字符if (ch #) {return NULL;//如果获取到的字符是‘#’则返回NULL}//给二叉树的节点分配内存空间如果分配失败则返回NULLif ((r (bitree *)malloc(sizeof(bitree))) NULL) {printf(malloc failed.\n);return NULL;}//将获取到的字符存入二叉树节点的data域中并递归创建左子树和右子树r-data ch;r-left tree_create();r-right tree_create();return r;
}
在tree.c中实现先序遍历函数preorder()
void preorder(bitree *r) {//传入参数验证同时也是退出递归的条件if (r NULL) {return;}//打印访问到的节点存储的值printf(%c, r-data);//递归遍历被访问的节点的左子树和右子树preorder(r-left);preorder(r-right);
}
先序遍历代码看似简单但其中蕴含一个非常重要的思想就是递归
递归是指原问题的解总是依赖于子问题的解决在二叉树的先序遍历过程中要想遍历完整个树在访问了节点数据data之后总是依赖于左子树和右子树的是否遍历完成而左子树和右子树是否遍历完成同样依赖于它们的左子树和右子树是否遍历完成以此类推直到传入preorder()函数的参数为NULL再返回完成遍历。
使用递归算法一定要明确退出递归的条件否则构成死循环在这个算法中退出递归的条件就是r为NULL。
遍历算法中序遍历
中序遍历是指先访问左子树再访问树根最后访问右子树。
若二叉树为空树则空操作否则
中序遍历左子树访问根节点中序遍历右子树。
在tree.h文件中声明中序遍历函数inorder()
void inorder(bitree *r);//中序遍历函数
在tree.c文件中实现中序遍历函数inorder()
void inorder(bitree *r) {//传入参数验证如果为NULL返回也是递归终止条件if (r NULL) {return;}inorder(r-left);//递归遍历左子树printf(%c, r-data);//访问并打印节点data值inorder(r-right);//递归遍历右子树
}
在这里同样用到递归退出条件依然是r为NULL只是是先进行左子树的递归再打印节点的值再遍历右子树。
遍历算法后序遍历
后序遍历是指先访问左子树再访问右子树最后访问树根。
若二叉树为空树则空操作否则
后序遍历左子树后序遍历右子树。访问根节点。
在tree.h文件中声明后序遍历函数postorder()
void postorder(bitree *r);//声明后序遍历函数 在tree.c文件中实现后序遍历函数postorder()
void postorder(bitree *r) {//传入参数验证如r为NULL返回递归终止条件if (r NULL) {return;}postorder(r-left);//递归遍历左子树postorder(r-right);//递归遍历右子树printf(%c, r-data);//访问节点data值并打印
}
遍历算法层次遍历
二叉树的层次遍历是指是从左往右依次访问每层结点的过程。对于顺序表存储的二叉树层次遍历较容易实现只需逐层访问存储的结点。对于链表存储的二叉树可利用队列实现层次遍历。
链表存储的二叉树层次遍历算法
根结点入队出队并访问将其左右孩子入队出队并访问重复此过程直至队列为空。 图1 在tree.h文件中声明后序遍历函数layerorder()
void layerorder(bitree *r);//层次遍历函数声明 在linkqueue.h文件中定义节点和队列结构体并声明相关函数
#include tree.h
typedef bitree * datatype;//给bitree起别名datatype//定义node结构体并起别名listnode和linklist
typedef struct node {datatype data;struct node *next;
}listnode, *linklist;//定义队列结构体并起别名linkqueue
typedef struct {linklist front;//队头linklist rear;//队尾
}linkqueue;linkqueue *queue_create();//声明创建队列函数
int enqueue(linkqueue *lq, datatype x);//声明入队函数
datatype dequeue(linkqueue *lq);//声明出队函数
int queue_empty(linkqueue *lq);//声明队列是否为空判断函数
int queue_clear(linkqueue *lq);//声明队列清空函数
linkqueue *queue_free(linkqueue *lq);//声明释放队列函数 在linkqueue.c文件中实现队列的相关函数 实现队列的创建函数queue_create()
#include stdio.h
#include stdlib.h
//#include tree.h
#include linkqueue.hlinkqueue *queue_create() {linkqueue *lq;if ((lq (linkqueue *)malloc(sizeof(linkqueue))) NULL) {printf(malloc linkqueue failed.\n);return NULL;}lq-front lq-rear (linklist)malloc(sizeof(listnode));if (lq-front NULL) {printf(malloc front failed.\n);return NULL;}lq-front-data 0;lq-front-next NULL;return lq;
} 实现入队函数enqueue()
int enqueue(linkqueue *lq, datatype x) {linklist p;if (lq NULL) {printf(lq is NULL.\n);return -1;}p (linklist)malloc(sizeof(listnode));if (p NULL) {printf(malloc front failed.\n);return -1;}p-data x;p-next NULL;lq-rear-next p;lq-rear p;return 0;
} 实现出队函数dequeue()
datatype dequeue(linkqueue *lq) {linklist p;if (lq NULL) {printf(lq is NULL.\n);return NULL;}p lq-front;lq-front p-next;free(p);p NULL;return (lq-front-data);
} 实现队列判断函数queue_empty()
int queue_empty(linkqueue *lq) {if (lq NULL) {printf(lq is NULL.\n);return -1;}return (lq-front lq-rear ? 1: 0);
} 实现队列清空函数queue_clear()
int queue_clear(linkqueue *lq) {linklist p;if (lq NULL) {printf(lq is NULL.\n);return -1;}while (lq-front-next) {p lq-front;lq-front p-next;//printf(clear free: %d\n, p-data);free(p);p NULL;}return 0;} 实现队列释放函数queue_free()
linkqueue *queue_free(linkqueue *lq) {linklist p;if (lq NULL) {printf(lq is NULL.\n);return NULL;}while (lq-front) {p lq-front;lq-front p-next;//printf(free: %d\n, p-data);free(p);}free(lq);lq NULL;return 0;
} 在tree.c文件中实现层次遍历函数layerorder()
void layerorder(bitree *r) {//声明一个队列lqlinkqueue *lq;//创建队列lq入创建失败直接返回if ((lq queue_create()) NULL)return;//验证传入参数rif (r NULL) return;//打印访问到的节点数值并让该节点入队printf(%c, r-data);enqueue(lq, r);//当队列不为空不断执行左孩子、右孩子的访问入队和出队while (!queue_empty(lq)) {r dequeue(lq);if (r-left ! NULL) {printf(%c, r-left-data);enqueue(lq, r-left);}if (r-right ! NULL) {printf(%c, r-right-data);enqueue(lq, r-right);}}//确认清空队列并释放内存queue_clear(lq);queue_free(lq);
}
遍历算法测试test.c文件
#include stdio.h
#include stdlib.h
#include tree.hint main(int argc, const char *argv[])
{bitree *r;if((r tree_create()) NULL) return -1;//调用先序遍历函数preorder(r);puts();//调用中序遍历函数inorder(r);puts();//调用后序遍历函数postorder(r);puts();//调用层次遍历函数layerorder(r);puts();return 0;
}运行结果
将树图1左孩子、有孩子缺失的部分用‘#’填充运行程序后输入A#BCEH###FI##J##D#GK###得到以下结果