ADT树——C语言实现


数据结构——树

PS:新人博客,如有错误欢迎在评论区指正

  • 前置知识:递归,结构体

基础概念

  1. 根节点只存在一个
  2. 节点拥有子树的个数为节点的度
  3. 除了根节点一外的,都是子树
    • 子树个数没有限制,子树之间互不相交
  4. 结点间的亲缘关系:如图示
  5. 从根节点开始向下数,树的层数即是树的深度
  6. 森林:互补相交的树

树的存储结构

  • 存储结构的设计是一个非常灵活的过程。一个存储结构设计得是否合理,取决于基于该存储结构的运算是否合适、方便,时间复杂度好不好等——《大话数据结构》

双亲表示法

采用数组来存储:顺序存储各个节点,给各节点附加记录其 parent 节点位置
每个节点中,包含该节点的数据和该节点的 parent 节点的位置
除了知道自己是谁,自己存放什么内容,还知道自己的 parent 在哪

#define MAX_TREE_SIZE 100 //树的最大尺寸
typedef int TElemType;    //树节点的数据类型
typedef struct PTNode{	  //节点结构
	TElemType data;		  //节点数据
	int parent;			  //parent 位置
}PTNode;
typedef struct{
	PTNode nodes[MAX_TREE_SIZE];//节点数组
	int r,n;			  //根的位置和节点数
}PTree;

易于找到 parent 节点,但在寻找节点的 child 时,需要遍历整个树

孩子表示法

每个节点指向一颗子树的根节点——多重链式表示法

实现方法一

  • 为给一个节点设置最大值

实现方法二

  • 对比于方法一无法确定节点的度,我们可以设置一个变量来记录节点的度,进而设计适当的个数,减少空指针浪费空间

克服空间浪费,但节点的链表结构不同,还要维护节点度的数值,会产生时间消耗(空间换时间,时间换空间)

二叉树

二叉树基础概念

在没有指定规则的情况下,其他形式树的创建比较困难,所以一般创建二叉树来学习树的数据类型
每个根节点最多只有两个子树
左子树和右子树是有区别的

  • 二叉树与非常经典的二分查找算法有一些关联,我们在将一组数据输入时,可以依照二分查找(大小关系)输入树中,并且在查找的时候也比较快捷(\(log_2N\))
    • 但很明显可以发现,如果所选取的根节点不好,就会导致树长得比较奇葩,甚至长成斜树
  • 斜树:左斜树 and 右斜树,每层都只有一个节点,所有节点都全是左子树(右子树)image
  • 满二叉树:全部节点都存在的二叉树
  • 完全二叉树:其序号按照满二叉树来标注,看图可知
    • 满二叉树一定是完全二叉树,但反过来不一定

二叉树存储结构

  • 可以类比前基本树的存储结构

二叉树的遍历

从根节点出发,按照某种次序依次访问二叉树中的所有节点,使得每个节点被访问且仅被访问一次

前序遍历:先访问根节点,然后遍历左节点,再遍历右节点:ABDGHCEIF


1. 先从根节点进入,访问A
2. 再进一步访问A的左子树B
3. 依次向左下访问:D->G,由于G没有子树,所以返回节点D,访问右子树H
PS:这里我们是使用递归来实现的二叉树,所以会有返回操作,看到完整代码即可理解
4. D节点的树已经访问完,返回到B节点,B节点没有右子树,返回A节点
5. 右边的访问类比左边即可,规则一样

  • 代码实现
void PreOrdeTraverse(BiTree T)
{
    if (T)		//树不空则继续访问
    {
        printf(T->data);
        PreOrdeTraverse(T->lchild);//使用递归形式实现遍历,所以需要下层左树访问完全,才能返回到节点,访问右树
        PreOrdeTraverse(T->rchild);
    }
}

中序遍历:从根节点开始(并不访问根节点),先遍历节点左树,再访问根节点,后访问右树:GDHBAEICF

  • 先看代码,更好的理解中序遍历
void InOrdeTraverse(BiTree T)
{
    if (T)
    {
        InOrdeTraverse(T->lchild);
        printf(T->data);
        InOrdeTraverse(T->rchild);
    }
}
1. 依旧是从A节点进入,但看程序,我们会优先进行`InOrdeTraverse(T->lchild)`,访问左子树
2. B->D->G
3. 当到达G节点时,没有左子树了,我们便输出G的内容printf(T->date);G节点无右子树,所以本节点递归结束,回到D节点printf(T->data),接着访问H节点,同样H节点无左右子树,便printf(T->data)
4. 其他同样如此理解

后序遍历:从根节点进入,先访问左子树,再访问右子树,最后放过根节点

  • 类比前两种遍历方式,不多赘述
void PosOrdeTraverse(BiTree T)
{
    if (T)
    {
        PosOrdeTraverse(T->lchild);
        PosOrdeTraverse(T->rchild);
        visit(T->data);
    }
}

层序遍历:按照层级关系对树进行遍历;使用队列实现:ABCDEFGHI

#define maxsize 30 //队列最大长度
//访问二叉树结点操作
void visit(char c)
{
    printf("%c", c);
}
//层序遍历队列结构
typedef struct
{
    BiTree data[maxsize];
    int front, rear; //前后
} SqQueue;
//初始化队列
void InitQueue(SqQueue *Q)
{
    Q->front = Q->rear = 0;
}
//入队
void EnQueue(SqQueue *Q, BiTree P)
{
    if ((Q->rear + 1) % maxsize == Q->front)
    {
        printf("this Queue is full");
    }
    else
    {
        Q->data[Q->rear] = P;
        Q->rear = (Q->rear + 1) % maxsize;
    }
}
//出队
void DeQueue(SqQueue *Q, BiTree *P)
{
    if (Q->front == Q->rear)
    {
        printf("this queue is full");
    }
    else
    {
        *P = Q->data[Q->front];
        Q->front = (Q->front + 1) % maxsize;
    }
}
int emptyQueue(SqQueue Q)
{
    if (Q.front == Q.rear)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
//层序遍历二叉树
void LevelOrder(BiTree T)
{
    SqQueue Q;
    InitQueue(&Q);
    BiTree P;
    EnQueue(&Q, T);
    while (!emptyQueue(Q))
    {
        DeQueue(&Q, &P);
        visit(P->data);
        if (P->lchild != NULL)
        {
            EnQueue(&Q, P->lchild);
        }
        if (P->rchild != NULL)
        {
            EnQueue(&Q, P->rchild);
        }
    }
}

总的代码汇集

#include 
#include 
//使用递归实现树
#define maxsize 30
typedef struct BiTNode
{
    char data;                       //结点的数据域
    struct BiTNode *lchild, *rchild; //指向左孩子和右孩子
} BiTNode, *BiTree;
//访问二叉树结点操作
void visit(char c)
{
    printf("%c", c);
}
//层序遍历队列结构
typedef struct
{
    BiTree data[maxsize];
    int front, rear; //前后
} SqQueue;
//初始化队列
void InitQueue(SqQueue *Q)
{
    Q->front = Q->rear = 0;
}
//入队
void EnQueue(SqQueue *Q, BiTree P)
{
    if ((Q->rear + 1) % maxsize == Q->front)
    {
        printf("this Queue is full");
    }
    else
    {
        Q->data[Q->rear] = P;
        Q->rear = (Q->rear + 1) % maxsize;
    }
}
//出队
void DeQueue(SqQueue *Q, BiTree *P)
{
    if (Q->front == Q->rear)
    {
        printf("this queue is full");
    }
    else
    {
        *P = Q->data[Q->front];
        Q->front = (Q->front + 1) % maxsize;
    }
}
int emptyQueue(SqQueue Q)
{
    if (Q.front == Q.rear)
    {
        return 1;
    }
    else
    {
        return 0;
    }
}
//层序遍历二叉树
void LevelOrder(BiTree T)
{
    SqQueue Q;
    InitQueue(&Q);
    BiTree P;
    EnQueue(&Q, T);
    while (!emptyQueue(Q))
    {
        DeQueue(&Q, &P);
        visit(P->data);
        if (P->lchild != NULL)
        {
            EnQueue(&Q, P->lchild);
        }
        if (P->rchild != NULL)
        {
            EnQueue(&Q, P->rchild);
        }
    }
}

//先序序列创建一颗二叉树
void CreatBiTree(BiTree *T)
{
    char c;
    scanf("%c", &c);
    if (c == '#')
        *T = NULL;
    else
    {
        *T = (BiTNode *)malloc(sizeof(BiTNode)); //给结点申请一个空间
        (*T)->data = c;
        CreatBiTree(&((*T)->lchild)); //创建左子树
        CreatBiTree(&((*T)->rchild)); //创建右子树
    }
}

//先序遍历二叉树
void PreOrdeTraverse(BiTree T)
{
    if (T)
    {
        visit(T->data);
        PreOrdeTraverse(T->lchild);
        PreOrdeTraverse(T->rchild);
    }
}
//中序遍历二叉树
void InOrdeTraverse(BiTree T)
{
    if (T)
    {
        InOrdeTraverse(T->lchild);
        visit(T->data);
        InOrdeTraverse(T->rchild);
    }
}
//后序遍历二叉树
void PosOrdeTraverse(BiTree T)
{
    if (T)
    {
        PosOrdeTraverse(T->lchild);
        PosOrdeTraverse(T->rchild);
        visit(T->data);
    }
}
//查找字母D在第几层
void Search(BiTree T, int leavel)
{
    if (T)
    {
        if (T->data == 'D')
        {
            printf("D在第%d层!\n", leavel);
        }
        Search(T->lchild, leavel + 1);
        Search(T->rchild, leavel + 1);
    }
}
//计算节点个数
int sumNode(BiTree T, int *count)
{
    if (T)
    {
        *count += 1;
        sumNode(T->lchild, count);
        sumNode(T->rchild, count);
    }
    return *count;
}
int main()
{
    BiTree T;
    int level = 1;
    int count = 0;
    printf("请输入先序创建的二叉树,以#结束:");
    CreatBiTree(&T);
    printf("正在先序打印二叉树:");
    PreOrdeTraverse(T);
    putchar('\n');
    printf("正在中序打印二叉树:");
    InOrdeTraverse(T);
    putchar('\n');
    printf("正在后序打印二叉树:");
    PosOrdeTraverse(T);
    printf("正在层序打印二叉树:");
    LevelOrder(T);
    putchar('\n');
    printf("Nodes = %d\n", sumNode(T, &count));
    Search(T, level);
    return 0;
}
  • 运行结果

谢谢观看

end;