二叉树是计算机科学中的一种重要的数据结构,它具有以下特点和原理:
总之,二叉树因其灵活性和效率,在计算机科学的多个领域都有广泛的应用。然而,为了克服其固有的不平衡问题,衍生出了多种平衡二叉树的结构,如AVL树、红黑树等,这些结构在实际应用中更为常见。
typedef char data_t;
typedef struct node_t {
data_t data;
struct node_t *lchild, *rchild;
} bitree;
/**
* 创建并返回一个二叉树的根节点。
*
* 该函数通过递归方式构建二叉树。从输入读取每个节点的值,如果值为'#',则表示当前节点为空节点。
* 否则,创建一个新节点,并将其左子树和右子树分别递归调用本函数来构建。
*
* @return 返回新建的二叉树的根节点,如果内存分配失败或输入结束标志,则返回NULL。
*/
bitree *tree_create(){
data_t val; // 用于存储当前节点的值
bitree *root; // 将要创建的节点指针
scanf("%d", &val); // 从输入读取节点的值
if(val == '#') // 如果值为'#',表示当前节点为空节点,直接返回NULL
return NULL;
if((root = (bitree *)malloc(sizeof(bitree))) == NULL) // 动态分配内存给新节点,检查是否分配成功
{
printf("malloc error\n"); // 如果内存分配失败,打印错误信息
return NULL;
}
root->data = val; // 将读取的值赋给新节点
root->lchild = tree_create(); // 递归调用本函数构建左子树
root->rchild = tree_create(); // 递归调用本函数构建右子树
return root; // 返回新创建的节点
}
该函数用于创建一个二叉树。它通过递归调用自身来构建树的左子树和右子树。函数首先从输入读取节点的值,如果值为'#',表示当前节点为空节点,直接返回NULL。然后,函数动态分配内存给新节点,并检查是否分配成功。如果内存分配失败,打印错误信息并返回NULL。否则,将读取的值赋给新节点,并递归调用本函数构建左子树和右子树。最后,返回新创建的节点。
/**
* 预序遍历二叉树
*
* 该函数对二叉树进行预序遍历,即先访问根节点,然后递归遍历左子树,最后递归遍历右子树。
* 这种遍历方式适用于需要先处理根节点,然后再处理子树的情况。
*
* @param root 二叉树的根节点指针。如果根节点为空,则函数直接返回,不进行任何操作。
*/
void preorder(bitree *root){
/* 如果当前节点为空,则返回,不进行任何操作 */
if(root == NULL)
return;
/* 访问当前节点,打印节点数据 */
printf("%d ", root->data);
/* 递归遍历左子树 */
preorder(root->lchild);
/* 递归遍历右子树 */
preorder(root->rchild);
}
/**
* 中序遍历二叉树
*
* @param root 二叉树的根节点指针
*
* 中序遍历的顺序为:先遍历左子树,然后访问根节点,最后遍历右子树。
* 该函数实现了这一遍历逻辑,对于每个节点,先递归遍历其左子树,然后打印该节点的数据,
* 最后递归遍历其右子树。如果根节点为空,则直接返回,结束遍历。
*/
void inorder(bitree *root){
/* 如果根节点为空,则返回,结束递归 */
if(root == NULL)
return;
/* 递归遍历根节点的左子树 */
inorder(root->lchild);
/* 打印当前节点的数据 */
printf("%d ", root->data);
/* 递归遍历根节点的右子树 */
inorder(root->rchild);
}
/**
* 以后序遍历方式遍历二叉树
*
* 后序遍历的顺序为:先遍历左子树,再遍历右子树,最后访问根节点。
* 这种遍历方式常用于计算表达式树、复制二叉树等场景。
*
* @param root 二叉树的根节点指针。如果根节点为空,则无需进行遍历。
*/
void postorder(bitree *root){
/* 如果当前节点为空,则返回,避免空指针解引用 */
if(root == NULL)
return;
/* 递归遍历左子树 */
postorder(root->lchild);
/* 递归遍历右子树 */
postorder(root->rchild);
/* 输出当前根节点的数据 */
printf("%d ",root->data);
}
该函数实现了二叉树的层次遍历(层序遍历)功能。使用一个链式队列来辅助遍历,首先将根节点入队,然后循环从队列中出队一个节点,打印该节点的值,并将其左右子节点依次入队,直到队列为空。最后销毁队列,释放内存。
#ifndef _LINKQUEUE_H_
#define _LINKQUEUE_H_
#include "tree.h"
typedef bitree* datatype;
typedef struct node {
datatype data;
struct node *next;
} listnode, *linklist;
typedef struct {
linklist front;
linklist rear;
} linkqueue;
linkqueue * queue_create(void);
int enqueue(linkqueue *q, datatype x);
datatype dequeue(linkqueue *q);
int queue_empty(linkqueue *q);
int queue_clear(linkqueue *q);
linkqueue *queue_free(linkqueue *q);
#endif
/**
* 层次遍历二叉树
* @param root 二叉树的根节点指针
* @note 本函数通过队列实现二叉树的层次遍历,逐层打印树的节点值
*/
void layerorder(bitree *root){
/* 定义一个链式队列指针 */
linkqueue *lq;
/* 创建队列,如果创建失败则直接返回 */
if((lq = queue_create()) == NULL)
return;
/* 如果根节点为空,则直接返回 */
if(root == NULL)
return;
/* 打印根节点的值 */
printf("%c ",root->data);
/* 将根节点入队 */
enqueue(lq,root);
/* 当队列不为空时,进行循环 */
while(!queue_empty(lq)){
/* 出队一个节点,并赋值给root */
root = dequeue(lq);
/* 如果当前节点有左子节点,则打印左子节点的值并将其入队 */
if(root->lchild != NULL){
printf("%c ",root->lchild->data);
enqueue(lq,root->lchild);
}
/* 如果当前节点有右子节点,则打印右子节点的值并将其入队 */
if(root->rchild != NULL){
printf("%c ",root->rchild->data);
enqueue(lq,root->rchild);
}
}
/* 销毁队列,释放内存 */
queue_free(lq);
}
#ifndef _TREE_H
#define _TREE_H
typedef char data_t;
typedef struct node_t {
data_t data;
struct node_t *lchild, *rchild;
} bitree;
bitree *tree_create();
void preorder(bitree *root);
void inorder(bitree *root);
void postorder(bitree *root);
void layerorder(bitree *root);
#endif
#include"tree.h"
#include"linkqueue.h"
#include
#include
/**
* 创建并返回一个二叉树的根节点。
*
* 该函数通过递归方式构建二叉树。从输入读取每个节点的值,如果值为'#',则表示当前节点为空节点。
* 否则,创建一个新节点,并将其左子树和右子树分别递归调用本函数来构建。
*
* @return 返回新建的二叉树的根节点,如果内存分配失败或输入结束标志,则返回NULL。
*/
bitree *tree_create(){
data_t val; // 用于存储当前节点的值
bitree *root; // 将要创建的节点指针
scanf("%d", &val); // 从输入读取节点的值
if(val == '#') // 如果值为'#',表示当前节点为空节点,直接返回NULL
return NULL;
if((root = (bitree *)malloc(sizeof(bitree))) == NULL) // 动态分配内存给新节点,检查是否分配成功
{
printf("malloc error\n"); // 如果内存分配失败,打印错误信息
return NULL;
}
root->data = val; // 将读取的值赋给新节点
root->lchild = tree_create(); // 递归调用本函数构建左子树
root->rchild = tree_create(); // 递归调用本函数构建右子树
return root; // 返回新创建的节点
}
/**
* 预序遍历二叉树
*
* 该函数对二叉树进行预序遍历,即先访问根节点,然后递归遍历左子树,最后递归遍历右子树。
* 这种遍历方式适用于需要先处理根节点,然后再处理子树的情况。
*
* @param root 二叉树的根节点指针。如果根节点为空,则函数直接返回,不进行任何操作。
*/
void preorder(bitree *root){
/* 如果当前节点为空,则返回,不进行任何操作 */
if(root == NULL)
return;
/* 访问当前节点,打印节点数据 */
printf("%d ", root->data);
/* 递归遍历左子树 */
preorder(root->lchild);
/* 递归遍历右子树 */
preorder(root->rchild);
}
/**
* 中序遍历二叉树
*
* @param root 二叉树的根节点指针
*
* 中序遍历的顺序为:先遍历左子树,然后访问根节点,最后遍历右子树。
* 该函数实现了这一遍历逻辑,对于每个节点,先递归遍历其左子树,然后打印该节点的数据,
* 最后递归遍历其右子树。如果根节点为空,则直接返回,结束遍历。
*/
void inorder(bitree *root){
/* 如果根节点为空,则返回,结束递归 */
if(root == NULL)
return;
/* 递归遍历根节点的左子树 */
inorder(root->lchild);
/* 打印当前节点的数据 */
printf("%d ", root->data);
/* 递归遍历根节点的右子树 */
inorder(root->rchild);
}
/**
* 以后序遍历方式遍历二叉树
*
* 后序遍历的顺序为:先遍历左子树,再遍历右子树,最后访问根节点。
* 这种遍历方式常用于计算表达式树、复制二叉树等场景。
*
* @param root 二叉树的根节点指针。如果根节点为空,则无需进行遍历。
*/
void postorder(bitree *root){
/* 如果当前节点为空,则返回,避免空指针解引用 */
if(root == NULL)
return;
/* 递归遍历左子树 */
postorder(root->lchild);
/* 递归遍历右子树 */
postorder(root->rchild);
/* 输出当前根节点的数据 */
printf("%d ",root->data);
}
/**
* 层次遍历二叉树
* @param root 二叉树的根节点指针
* @note 本函数通过队列实现二叉树的层次遍历,逐层打印树的节点值
*/
void layerorder(bitree *root){
/* 定义一个链式队列指针 */
linkqueue *lq;
/* 创建队列,如果创建失败则直接返回 */
if((lq = queue_create()) == NULL)
return;
/* 如果根节点为空,则直接返回 */
if(root == NULL)
return;
/* 打印根节点的值 */
printf("%c ",root->data);
/* 将根节点入队 */
enqueue(lq,root);
/* 当队列不为空时,进行循环 */
while(!queue_empty(lq)){
/* 出队一个节点,并赋值给root */
root = dequeue(lq);
/* 如果当前节点有左子节点,则打印左子节点的值并将其入队 */
if(root->lchild != NULL){
printf("%c ",root->lchild->data);
enqueue(lq,root->lchild);
}
/* 如果当前节点有右子节点,则打印右子节点的值并将其入队 */
if(root->rchild != NULL){
printf("%c ",root->rchild->data);
enqueue(lq,root->rchild);
}
}
/* 销毁队列,释放内存 */
queue_free(lq);
}