二叉树

二叉树

1. 基本概念

1.1 名词定义

  • 根节点(Root):树的顶部节点,是整个树的起点

  • 父节点(Parent):相对于某个节点来说,其上一级的节点

  • 子节点(Child):相对于某个节点来说,其下一级的节点

  • 左子节点(Left Child):节点的左边子节点

  • 右子节点(Right Child):节点的右边子节点

  • 叶节点(Leaf):没有任何子节点的节点

  • 兄弟节点(Sibling):具有相同父节点的节点

如在下图,不同的颜色代表不同的层级:粉色表示根节点,蓝色表示内部节点(非叶节点),浅蓝色表示叶节点。箭头表示父子关系,展示了节点之间的层次结构
image-20250203124616295

1.2 常见类型

  1. 满二叉树

    • 除叶节点外,每个节点都有左右两个子节点
    • 所有叶节点都在最后一层
  2. 完全二叉树

    • 除最后一层外,每一层都是满的
    • 最后一层从左到右依次填充
  3. 空二叉树

    • 不包含任何节点的特殊情况

1.3 工作原理

  1. 存储方式
    • 数组存储法:适用于完全二叉树
    • 链式存储法:使用指针连接各个节点
  2. 遍历方法
    • 前序遍历(先访问根,再访问左子树,最后右子树):根→左→右
    • 中序遍历(先访问左子树,再访问根,最后右子树):左→根→右
    • 后序遍历(先访问左右子树,再访问根):左→右→根
  3. 应用场景
    • 文件系统目录结构
    • 数据库索引

2. 实现

二叉树例子:

image-20250203185314905

创建二叉树通过前序读入为: AB#CD###E#FGH##K###

实现效果:

image-20250203192612192

main.c

#include <stdio.h>
#include <stdlib.h>
#include "bitree.h"


int main() 
{
    bitree r = create_tree();
    
    printf("前序遍历:\n");
    preorder(r);
    puts("");
    printf("中序遍历:\n");
    inorder(r);
    puts("");
    printf("后序遍历:\n");
    postorder(r);
    puts("");
    printf("层次遍历:\n");
    layerorder(r);
    return 0;
}

bitree.h

#ifndef BITREE_H
#define BITREE_H

typedef char data_type;

typedef struct Node
{
    data_type data;
    struct Node *lchild, *rchild;
} treenode, *bitree;

bitree create_tree();
void preorder(bitree r);    
void inorder(bitree r);
void postorder(bitree r);
void layerorder(bitree r);

#endif  

bitree.c

#include <stdio.h>
#include <stdlib.h>
#include "bitree.h"
#include "linkqueue.h"
bitree create_tree()
{
    data_type ch;
    bitree r;

    scanf("%c", &ch);
    if (ch == '#')
       return NULL;
    if ((r = (bitree)malloc(sizeof(treenode))) == NULL)
    {
        printf("malloc failed\n ");
        return NULL;
    }

    r->data = ch;
    r->lchild = create_tree();
    r->rchild = create_tree();
    
    return r;
}

void preorder(bitree r)
{
    if (r == NULL)
    {
        return ;
    }
    printf("%c", r->data);
    preorder(r->lchild);
    preorder(r->rchild);
}

void inorder(bitree r)
{
    if (r == NULL)
    {
        return;
    }
    inorder(r->lchild);
    printf("%c", r->data);
    inorder(r->rchild);
}

void postorder(bitree r)
{
    if (r == NULL)
    {
        return;
    }
    postorder(r->lchild);
    postorder(r->rchild);
    printf("%c", r->data);
}
void layerorder(bitree r)
{
    linkqueue lq;
    if ((lq = linkqueue_create()) == NULL)
    {
        printf("malloc linkqueue failed\n");
        return;
    }

    if (r == NULL)
    {
        return;
    }
    
    printf("%c", r->data);
    linkqueue_enqueue(lq, r);

    while (!linkqueue_empty(lq))
    {
        r = linkqueue_dequeue(lq);
        if (r->lchild)
        {
            printf("%c", r->lchild->data);
            linkqueue_enqueue(lq, r->lchild);
        }
        if (r->rchild)
        {
            printf("%c", r->rchild->data);
            linkqueue_enqueue(lq, r->rchild);
        }
    }
    puts("");
}

linkqueue.h

#ifndef LINKQUEUE_H
#define LINKQUEUE_H

#include "bitree.h"

typedef bitree data_t;

typedef struct node_t
{
    data_t data;                 // 数据域
    struct node_t *next;        // 指针域
} queuenode_struct, *queuenode; // 队列结点结构体类型定义

typedef struct
{
    queuenode front, rear;
} linkqueue_struct,*linkqueue;  // 队列结构体类型定义

linkqueue linkqueue_create();                     // 创建队列
int linkqueue_empty(linkqueue q);                 // 判断队列是否为空
int linkqueue_enqueue(linkqueue q, data_t x);     // 入队
data_t linkqueue_dequeue(linkqueue q);            // 出队
int linkqueue_show(linkqueue q);                  // 遍历队列
int linkqueue_free(linkqueue *q);                 // 释放队列

#endif

linkqueue.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "linkqueue.h"

/**
  * @name 	linkqueue_create()
  * @brief	创建链式队列
  * @param  NULL
  * @retval q 队列地址
  */
linkqueue linkqueue_create()
{
    // 1. 申请队列头尾指针内存和队列节点内存
    linkqueue p = (linkqueue)malloc(sizeof(linkqueue_struct));
    if (p ==  NULL)
    {
        printf("linkqueue malloc error\n");
        return NULL;
    }
    queuenode newnode = (queuenode)malloc(sizeof(queuenode_struct));
    if (newnode == NULL)
    {
        printf("linknode malloc error\n");
        free(p);
        return NULL;
    }
    // 2. 初始化队列头尾指针和队列节点
    p->front = p->rear = newnode;
    newnode->next = NULL;
    newnode->data = NULL;

    return p;
}

/**
  * @name 	linkqueue_create()
  * @brief	p判断链式队列是否为空
  * @param  q 队列地址
  * @retval 0 队列不为空,1 队列为空,-1 队列不存在
  */
int linkqueue_empty(linkqueue q)
{
    // 1. 判断队列是否存在
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    // 2. 判断队列是否为空
    return q->front == q->rear ? 1 : 0;
}

/**
  * @name 	linkqueue_enqueue()
  * @brief	入队
  * @param  q 队列地址
  * @retval 0 入队成功,-1 入队失败
  */
int linkqueue_enqueue(linkqueue q, data_t x)
{
    // queuenode tem;
    // 1. 判断队列是否存在
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    // 2. 申请队列节点内存
    queuenode newnode = (queuenode)malloc(sizeof(queuenode_struct));
    if (newnode == NULL)
    {
        printf("queuenode malloc error\n");
        return -1;
    }
    newnode->data = x;
    newnode->next = NULL;
    // 3. 将新节点插入到队列尾结点之后,并尾指针指向新节点
    // tem = q->front;
    // while (tem->next != NULL)
    // {
    //     tem = tem->next;
    // }
    // tem->next = newnode;
    q->rear->next = newnode;
    q->rear = newnode;
    return 0;
}
/**
  * @name 	linkqueue_dequeue()
  * @brief	出队
  * @param  q 队列地址
  * @retval 出队元素
  */
data_t linkqueue_dequeue(linkqueue q)
{
    // 1. 判断队列是否存在
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return NULL;
    }

    // 2. 判断队列是否为空
    if (q->front == q->rear)
    {
        printf("linkqueue is empty\n");
        return NULL; 
    }
    // 3. 出队操作,释放队头结点,并头指针指向头结点下一个结点
    queuenode tem = q->front->next;
    data_t x = tem->data;

    q->front->next = tem->next;
    if (q->rear == tem)
    {
        q->rear = q->front;
    }
    free(tem);
    return x;
}
/**
  * @name 	linkqueue_show()
  * @brief	遍历队列,输出队列元素
  * @param  q 队列地址
  * @retval -1 遍历失败,0 遍历成功
  */
int linkqueue_show(linkqueue q)
{
    // 1. 判断队列是否存在以及队列是否为空
    if (q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    if (q->front == q->rear)
    {
        printf("linkqueue is empty\n");
        return -1; 
    }
    // 2. 遍历队列,输出队列元素
    queuenode tem = q->front->next;
    while (tem != NULL)
    {
        printf("%d \n", tem->data->data);
        tem = tem->next;
    }
    return 0;
}

/**
  * @name 	linkqueue_free()
  * @brief	遍历队列,输出队列元素
  * @param  q 队列地址
  * @retval -1 释放失败,0 释放成功
  */
int linkqueue_free(linkqueue *q)
{
    // 1. 判断队列是否存在
    if (*q == NULL)
    {
        printf("linkqueue is invalid\n");
        return -1;
    }
    // 2. 释放队列
    queuenode tem = (*q)->front;
    while (tem != NULL)
    {
        queuenode next = tem->next;
        free(tem);
        tem = next;
    }
    free(*q);
    *q = NULL;
    return 0;
}
posted @ 2025-02-03 19:31  Arsun  阅读(29)  评论(0)    收藏  举报