1.树
树形结构:一对多,第一个节点没有前驱,最后一个节点没有后继,中间节点有唯一前驱,可以有多个后继,也可以没有
2.树的概念
由m(>=0)个互不相交的有限集合组成的集合
一个节点的子树个数称为该节点的度数。树的度数由树中最大的度数来决定。
度数为0的节点称为树叶,叶节点或终端节点。度数不为0的称为分支节点,除了根节点以外的分支节点称为内部节点。
路径:路径上的节点ki是ki+1的父节点(叶节点的个数也就是路径的个数)路径的长度称为边数。
层数:同一父节点的节点为同一层。根节点为第一层,树中节点的最大层数称为树的高度或者深度
树的左右节点有序,从左到右不可交换,则称为有序树
m(m>=0)棵互不相交的树的集合称为森林。树去掉根节点就称为森林
兄弟分为:亲兄弟,堂兄弟
3.二叉树
二叉树的定义:二叉树是n(n>=0)个节点的有限集合,它或者是空集(n=0),或者是由一个根节点以及两棵不互相交的、分别称为左子树与柚子树的二叉树组成,二叉树与普通有序树不同,二叉树严格区分左孩子和右孩子,即使只有一个节点也要区分左右,度数最大为2.
性质:
第i层上的节点最多为2^(i-1)
知道最大层数,总的节点最多为2^i-1
树叶的数目比度数为2的节点的数目多1
(n0表示没有子节点的节点总数,n1表示有一个子节点的节点总数,n2表示有两个子节点的节点总数)
n=n0+n1+n2①
n=n1+2*n2+1②
n0=n2+1
满二叉树,每一层的孩子都存满(可以顺序存储,空间不浪费)
完全二叉树:只有最下面两层有度数小于2的节点,只有一个孩子时,孩子为左孩子
4.二叉树的顺序存储
先将二叉树补成完全二叉树,再从上到下,从左到右将所有节点编号,依次存放在缓冲区中。
完全二叉树节点编号,从上到下,从左到右,根节点为一号,设节点数为n,某编号为i。
性质:
当i>1时,有父节点,其编号为i/2;
当2* i<=n时,有左孩子,其编号为2*i,否则没有左孩子,本身是叶节点;
当2* i+1<=n时,有右孩子,其编号为2*i+1,否则没右孩子,本身是叶节点;
当i为奇数(右节点)且不为1时,有左兄弟,其编号为i-1,否则没有左兄弟;
当i为偶数(左节点)且小于n时,有右兄弟,其编号为i+1,否则没有右兄弟
/*===============================================
* 文件名称:seqtree.c
* 创 建 者:
* 创建日期:2022年08月02日
* 描 述:
================================================*/
#include <stdio.h>
int main(int argc, char *argv[])
{
char buf[16]={0},ch,i=1,n=1;
printf("请输入完全二叉树的元素(#结束)\n");
while((ch=getchar())!='#')//一个一个输入
{
buf[i]=ch;
i++;
}
for(i=0;i<16;i++)//显示
{
if(i>n-1)
{
puts("");
n=2*n;
}
if(buf[i]=='@')
{
printf(" ");
continue;
}
printf("%c",buf[i]);
}
puts("");
return 0;
}
5.树的链式存储
树中每个节点相关的结构体:
typedef int data_t;
typedef struct tree_node
{
data_t data;//数据域
struct tree_node *l_child;//左节点
struct tree_node *r_child;//右节点
}T_node;
6.二叉树的遍历
先序遍历:根左右
中序遍历:左根右
后序遍历:左右根
二叉树的层次遍历:
根先入队,然后循环到队列为空。
入队:判断一次根的左右节点是否存在,存在就入队(先左后右)
出队:打印根的数据域,根出队
6.1头文件
头文件定义了树的存储类型,结构体的设计,以及将要实现的功能函数。
/*===============================================
* 文件名称:linktree.h
* 创 建 者:
* 创建日期:2022年08月02日
* 描 述:
================================================*/
#ifndef __LINKTREE__
#define __LINKTREE__
typedef char data_t;
typedef struct tree_node
{
data_t data;
struct tree_node *l_child;
struct tree_node *r_child;
}T_node;
T_node *Create_Linktree();//创建一个二叉树
void Linktree_Show0(T_node *root);//先序遍历
void Linktree_Show1(T_node *root);//中序遍历
void Linktree_Show2(T_node *root);//后序遍历
void Nooread(T_node *root);//层次遍历
#endif
6.2功能函数
二叉树的难点在于创建时的递归调用。从终端输入一串数据以 '#' 结束,数据存储在缓冲区中,然后每次执行创建程序,读一个字符。判断是否是'#',如不是,则向堆区申请空间,把数据存入数据域,指针域则调用创建函数,形成递归,先创建左子树,再创建右子树,当读取到'#'时,标明没有子树。返回NULL。
三种常见的遍历方式先判断是否是空树,不是的话就按照对应的顺序来调用遍历函数,形成递归条件,都是在根节点时打印。
最后的层次遍历则利用到了队列,每一个根节点都判断是否有左右子树,有的话就入队,然后把队列的第一个节点也就是这一次循环的根节点打印,然后出队,因为只显示一遍,不需要循环队列。
/*===============================================
* 文件名称:linktree.c
* 创 建 者:
* 创建日期:2022年08月02日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include "linktree.h"
T_node *Create_Linktree()//创建一个二叉树
{
char x;
//printf("请输入节点的值,#表示空\n");
T_node *root;
scanf("%c",&x);
//printf("x=%c\n",x);
//getchar();
if(x!='#')
{
root=(T_node *)malloc(sizeof(T_node));
if(NULL==root)
{
printf("nalloc error");
return NULL;
}
root->data=x;
root->l_child=Create_Linktree();//先左后右
root->r_child=Create_Linktree();
return root;
}
else
{
return NULL;
}
}
void Linktree_Show0(T_node *root)//先序遍历
{
if(root!=NULL) //根
{
printf("%4c",root->data);
Linktree_Show0(root->l_child);//左
Linktree_Show0(root->r_child);//右
}
return;
}
void Linktree_Show1(T_node *root)//中序遍历
{
if(root!=NULL) //根
{
Linktree_Show0(root->l_child);//左
printf("%4c",root->data);
Linktree_Show0(root->r_child);//右
}
return;
}
void Linktree_Show2(T_node *root)//后序遍历
{
if(root!=NULL) //根
{
Linktree_Show0(root->l_child);//左
Linktree_Show0(root->r_child);//右
printf("%4c",root->data);
}
return;
}
void Nooread(T_node *root)//层次遍历
{
int front,rear;//头尾指针
T_node *q[16]; //指针数组
if(root==NULL) //判断树是否为空
{
printf("空树无法遍历\n");
return;
}
for(rear=1;rear<16;rear++)//给指针数组赋初值NULL
{
q[rear]=NULL;
}
front=rear=1;//下标为0的数组元素不用
q[rear]=root;
rear++;
while(q[front]!=NULL)//队列为空则跳出循环
{
if(q[front]->l_child!=NULL)//左孩子存在则入队
{
q[rear]=q[front]->l_child;
rear++; //rear后移
}
if(q[front]->r_child!=NULL)//右孩子存在则入队
{
q[rear]=q[front]->r_child;
rear++; //rear后移
}
printf("%c",q[front]->data);//打印出队节点
front++; //队头节点出队
}
}
6.3主函数
主函数就调用了功能函数,先是创建了二叉树,然后四种遍历方式输出
/*===============================================
* 文件名称:main.c
* 创 建 者:
* 创建日期:2022年08月02日
* 描 述:
================================================*/
#include <stdio.h>
#include "linktree.h"
int main(int argc, char *argv[])
{
T_node *root=Create_Linktree();
printf("-------------------------------------------\n");
Linktree_Show0(root);
printf("\n-------------------------------------------\n");
Linktree_Show1(root);
printf("\n-------------------------------------------\n");
Linktree_Show2(root);
printf("\n-------------------------------------------\n");
Nooread(root);
puts("");
return 0;
}