一、树与二叉树
-
什么是树?
树是n (n > 0)个节点的有限集。当n = 0时,称为空树。在任意一棵非空树中应满足:
①有且仅有一个特定的称为根的结点。
②当n>1时,其余节点可分为m (m>0) 个互不相交的有限集T1,T2…, Tm,其中每个集合本身又是一棵树,并且称为根的子树。
-
树作为一种逻辑结构,同时也是一种分层结构,具有以下两个特点:
①树的根结点没有前驱,除根结点外的所有结点有且只有一个前驱。
②树中所有结点可以有零个或多个后继。
二叉树:
①是另一种树形结构
②特点是每个结点至多只有两棵子树(即二叉树中不存在度大于2的结点)
③二叉树的子树有左右之分,其次序不能任意颠倒
④二叉树是n(n≥0)个结点的有限集合:
1)或者为空二叉树,即n=0
2)或者由一个根结点和两个互不相交的被称为根的左子树和右子树组成。左子树和右子树又分别是一颗二叉树。
二、二叉树层次建树
代码:
function头文件
//
// Created by 克里斯 on 2023/8/17.
//
#ifndef INC_1_TREE_FUNCTION_H
#define INC_1_TREE_FUNCTION_H
#include <stdio.h>
#include <stdlib.h>
typedef char BiElemType;
typedef struct BiTNode{
BiElemType c;//c就是书籍上的data
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode,*BiTree;
//tag结构体是辅助队列使用的
typedef struct tag{
BiTree p;//树的某一个结点的地址值
struct tag *pnext;
}tag_t,*ptag_t;
#endif //INC_1_TREE_FUNCTION_H
main函数
#include "function.h"
int main() {
BiTree pnew;//用来申请一个树的新结点
BiTree tree=NULL;//tree是指向树根的,代表树
char c;
ptag_t phead=NULL,ptail=NULL,listpnew=NULL,pcur;
//abcdefghij
while(scanf("%c",&c))
{
if(c=='\\n')
{
break;//读到换行就结束
}
//calloc申请空间大小是两个参数直接相乘,并对空间进行初始化,赋值为0
pnew= (BiTree)calloc(1,sizeof (BiTNode));
pnew->c=c;
listpnew= (ptag_t)calloc(1,sizeof (tag_t));//给队列结点申请空间
listpnew->p=pnew;
//如果是树的第一个结点
if(tree==NULL)
{
tree=pnew;//指向树的根结点
phead=listpnew;//第一个队列既是队列头,也是队列尾
ptail=listpnew;
pcur=listpnew;//pcur要指向要进入树的父亲元素
}else{
//让元素先入队列
ptail->pnext=listpnew;
ptail=listpnew;
//接下来把b结点放入树中
if(pcur->p->lchild==NULL)
{
pcur->p->lchild=pnew;//pcur->p左孩子为空,就放入左孩子
}else if(pcur->p->rchild==NULL)
{
pcur->p->rchild=pnew;//pcur->p右孩子为空,就放入右孩子
pcur=pcur->pnext;//当前结点左孩子右孩子都满了,就指向下一个
}
}
}
return 0;
}
三、二叉树前中后序遍历实战
- 前序遍历 : 也叫先序遍历,先打印当前结点,打印左孩子,打印右孩子
- 中序遍历 : 先打印左孩子,打印父亲,打印右孩子
- 后序遍历 : 打印左孩子,打印右孩子,打印父亲
代码实战:
#include "function.h"
//使用递归
//前序遍历,也叫先序遍历,深度优先遍历
void pre_order(BiTree p)
{
if(p!=NULL)
{
printf("%c",p->c);
pre_order(p->lchild);//打印左结点
pre_order(p->rchild);//打印右结点
}
}
//中序遍历
void in_order(BiTree p)
{
if(p!=NULL)
{
in_order(p->lchild);//打印左结点
printf("%c",p->c);
in_order(p->rchild);//打印右结点
}
}
//后序遍历
void post_order(BiTree p)
{
if(p!=NULL)
{
post_order(p->lchild);//打印左结点
post_order(p->rchild);//打印右结点
printf("%c",p->c);
}
}
int main() {
BiTree pnew;//用来申请一个树的新结点
BiTree tree=NULL;//tree是指向树根的,代表树
char c;
ptag_t phead=NULL,ptail=NULL,listpnew=NULL,pcur;
//abcdefghij
while(scanf("%c",&c))
{
if(c=='\n')
{
break;//读到换行就结束
}
//calloc申请空间大小是两个参数直接相乘,并对空间进行初始化,赋值为0
pnew= (BiTree)calloc(1,sizeof (BiTNode));
pnew->c=c;
listpnew= (ptag_t)calloc(1,sizeof (tag_t));//给队列结点申请空间
listpnew->p=pnew;
//如果是树的第一个结点
if(tree==NULL)
{
tree=pnew;//指向树的根结点
phead=listpnew;//第一个队列既是队列头,也是队列尾
ptail=listpnew;
pcur=listpnew;//pcur要指向要进入树的父亲元素
}else{
//让元素先入队列
ptail->pnext=listpnew;
ptail=listpnew;
//接下来把b结点放入树中
if(pcur->p->lchild==NULL)
{
pcur->p->lchild=pnew;//pcur->p左孩子为空,就放入左孩子
}else if(pcur->p->rchild==NULL)
{
pcur->p->rchild=pnew;//pcur->p右孩子为空,就放入右孩子
pcur=pcur->pnext;//当前结点左孩子右孩子都满了,就指向下一个
}
}
}
printf("-----pre_order-----\n");//也叫先序遍历,先打印当前结点,打印左孩子,打印右孩子
pre_order(tree);
printf("\n");
printf("-----in_order-----\n");//先打印左孩子,打印父亲,打印右孩子
in_order(tree);
printf("\n");
printf("-----post_order-----\n");//打印左孩子,打印右孩子,打印父亲
post_order(tree);
printf("\n");
return 0;
}
测试结果:
abcdefghij
-----pre_order-----
abdhiejcfg
-----in_order-----
hdibjeafcg
-----post_order-----
hidjebfgca
Process finished with exit code 0
四、二叉树的层序遍历
function.h文件
//
// Created by 克里斯 on 2023/8/17.
//
#ifndef INC_1_TREE_FUNCTION_H
#define INC_1_TREE_FUNCTION_H
#include <stdio.h>
#include <stdlib.h>
typedef char BiElemType;
typedef struct BiTNode{
BiElemType c;//c就是书籍上的data
struct BiTNode *lchild;
struct BiTNode *rchild;
}BiTNode,*BiTree;
//tag结构体是辅助队列使用的
typedef struct tag{
BiTree p;//树的某一个结点的地址值
struct tag *pnext;
}tag_t,*ptag_t;
//队列的结构体
typedef BiTree ElemType;
typedef struct LinkNode{
ElemType data;
struct LinkNode *next;
}LinkNode;
typedef struct {
LinkNode *front,*rear;//链表头 链表尾
}LinkQueue;//先进先出
void init_queue(LinkQueue &Q);
bool is_empty(LinkQueue Q);
void en_queue(LinkQueue &Q,ElemType x);
bool de_queue(LinkQueue &Q,ElemType &x);
#endif //INC_1_TREE_FUNCTION_H
queue.cpp文件
//
// Created by 克里斯 on 2023/8/24.
//
#include "function.h"
//初始化队列
void init_queue(LinkQueue &Q)
{
Q.front=Q.rear= (LinkNode*)malloc(sizeof(LinkNode));
Q.front->next=NULL;
}
//判断队列是否为空
bool is_empty(LinkQueue Q)
{
return Q.rear==Q.front;
}
//入队
void en_queue(LinkQueue &Q,ElemType x)
{
LinkNode *pnew=(LinkNode*) malloc(sizeof(LinkNode));
pnew->data=x;
pnew->next=NULL;//让next为空,否则遍历时结束不了
Q.rear->next=pnew;//尾指针的next指向pnew,从尾部入队
Q.rear=pnew;//rear指向新的尾部
}
//出队
bool de_queue(LinkQueue &Q,ElemType &x)
{
if(Q.rear==Q.front)//队列为空
{
return false;
}
LinkNode *q=Q.front->next;//拿到第一个结点,存入q
x=q->data;//获取要删除的元素值
Q.front->next=q->next;//让一个结点断链
if(Q.rear==q)//链表只剩余一个结点时,被删后,要改变rear
{
Q.rear=Q.front;
}
free(q);
return true;
}
main.cpp文件:
#include "function.h"
//使用递归
//前序遍历,也叫先序遍历,深度优先遍历
void pre_order(BiTree p)
{
if(p!=NULL)
{
printf("%c",p->c);
pre_order(p->lchild);//打印左结点
pre_order(p->rchild);//打印右结点
}
}
//中序遍历
void in_order(BiTree p)
{
if(p!=NULL)
{
in_order(p->lchild);//打印左结点
printf("%c",p->c);
in_order(p->rchild);//打印右结点
}
}
//后序遍历
void post_order(BiTree p)
{
if(p!=NULL)
{
post_order(p->lchild);//打印左结点
post_order(p->rchild);//打印右结点
printf("%c",p->c);
}
}
//层序遍历,层次遍历,广度优先遍历
void level_order(BiTree T)
{
LinkQueue Q;//辅助队列
init_queue(Q);//初始化队列
BiTree p;//存储出队的结点
en_queue(Q,T);//根入队
while (!is_empty(Q))
{
de_queue(Q,p);
putchar(p->c);//等价与printf("%c",c);
if(p->lchild)
{
en_queue(Q,p->lchild);//入队左孩子
}
if(p->rchild)
{
en_queue(Q,p->rchild);//入队右孩子
}
}
}
int main() {
BiTree pnew;//用来申请一个树的新结点
BiTree tree=NULL;//tree是指向树根的,代表树
char c;
ptag_t phead=NULL,ptail=NULL,listpnew=NULL,pcur;
//abcdefghij
while(scanf("%c",&c))
{
if(c=='\n')
{
break;//读到换行就结束
}
//calloc申请空间大小是两个参数直接相乘,并对空间进行初始化,赋值为0
pnew= (BiTree)calloc(1,sizeof (BiTNode));
pnew->c=c;
listpnew= (ptag_t)calloc(1,sizeof (tag_t));//给队列结点申请空间
listpnew->p=pnew;
//如果是树的第一个结点
if(tree==NULL)
{
tree=pnew;//指向树的根结点
phead=listpnew;//第一个队列既是队列头,也是队列尾
ptail=listpnew;
pcur=listpnew;//pcur要指向要进入树的父亲元素
}else{
//让元素先入队列
ptail->pnext=listpnew;
ptail=listpnew;
//接下来把b结点放入树中
if(pcur->p->lchild==NULL)
{
pcur->p->lchild=pnew;//pcur->p左孩子为空,就放入左孩子
}else if(pcur->p->rchild==NULL)
{
pcur->p->rchild=pnew;//pcur->p右孩子为空,就放入右孩子
pcur=pcur->pnext;//当前结点左孩子右孩子都满了,就指向下一个
}
}
}
printf("-----pre_order-----\n");//也叫先序遍历,先打印当前结点,打印左孩子,打印右孩子
pre_order(tree);
printf("\n");
printf("-----in_order-----\n");//先打印左孩子,打印父亲,打印右孩子
in_order(tree);
printf("\n");
printf("-----post_order-----\n");//打印左孩子,打印右孩子,打印父亲
post_order(tree);
printf("\n-----level_order-----\n");
level_order(tree);
return 0;
}
测试结果:
abcdefghij
-----pre_order-----
abdhiejcfg
-----in_order-----
hdibjeafcg
-----post_order-----
hidjebfgca
-----level_order-----
abcdefghij
Process finished with exit code 0