数据结构--二叉树

发布于:2025-08-09 ⋅ 阅读:(19) ⋅ 点赞:(0)

一、树形结构相关概念

  1. 树形结构:是一种常见的数据组织形式,具有层级和分支的特点
  2. 前驱:由前驱节点可以访问到当前节点
  3. 后续:当前节点可以访问到的节点
  4. 节点:组成树形结构的一个小单元
    1. 分支节点:既有前驱,又有后续的节点
    2. 根节点:只有后续,没有前驱
    3. 叶子节点:只有前驱,没有后续
  5. 层:根节点层数尾 0 ,在此基础上延伸一个节点,层数 + 1,其中层数最高节点对应的层数为树的层数
  6. 高度:当前节点到最远结点的距离
  7. 深度:当前节点到根节点的距离
  8. 度:后继结点的个数

二、二叉树

1、相关概念

  • 二叉树:若树形结构中的所有节点度数最大为 2 则称为二叉树
  • 左孩子:二叉树节点两个后继节点中的左节点
  • 右孩子:二叉树节点两个后继节点中的右节点
  • 满二叉树:所有叶子节点均在同一层,且每层节点个数均为最大值
  • 完全二叉树:二叉树的编号(如果节点编号为n,左孩子编号:2n,右孩子编号为:2n+1)展开后是连续的, 称为完全二叉树

2.完全二叉树的遍历

  • 深度优先遍历
    • 前序遍历:访问节点,然后遍历左子树,最后遍历右子树(根左右)

    • 中序遍历:遍历左子树,访问节点,然后遍历右子树(主要用于二叉搜索树,得到排序序列)(左根右)

    • 后序遍历:遍历左子树,遍历右子树,最后访问节点(左右根)
  • 广度优先遍历
    • 层序遍历:逐层从左到右依次遍历

3、二叉树的操作

  1.节点的定义
typedef struct node{
    int no;                    //二叉树节点编号
    struct node *leftchild;    //左孩子地址
    struct node *leftchild;    //右孩子地址
}treenode;
  2.创建完全二叉树
        思路:
  • 使用函数递归完成二叉树的创建
  • 首先申请节点空间,存放数据编号
  • 如果存在左子树 / 右子树,创建左子树 / 右子树
        代码实现:
//startno:创建起始节点编号
//endno:结束节点编号

treenode *create_treenode(int startno,int endno)
{
    treenode *proot = NULL;

    proot = malloc(sizeof(treenode));              //申请变量空间
    if(NULL == proot)
    {
        perror("error to malloc");
        return NULL;
    }

    proot->no = startno;
    proot->leftchild = proot->rightchile = NULL;

    if(startno*2 <= endno)                          //判断是否有左子树
    {
        proot->leftchild = create_terrnode(starno*2,endno);     //函数递归调用
    }
    if(startno*2 + 1 <= endno)                      //判断是否有右子树
    {
        proot->rightchild = create_terrnode(starno*2 + 1,endno);//函数递归调用
    }

    return proot;
}
  3.二叉树的深度优先遍历(使用递归函数)
/* 前序遍历 */
treenode *head_treenode(treenode *proot)
{
    printf("%d ",proot->no);

    if(proot->pleftchild != NULL)
    {
        head_treenode(proot->pleftchild);
    }
    if(proot->prightchild != NULL)
    {
        head_treenode(proot->prightchild);
    }
}

 /* 中序遍历 */
int inorder_btree(treenode *proot)
{
   if (proot->pleftchild != NULL)
   {
       inorder_btree(proot->pleftchild);
   }
   printf("%d ", proot->no);
   if (proot->prightchild != NULL)
   {
       inorder_btree(proot->prightchild);
   }
   return 0;
}

 /* 后序遍历 */
 int postorder_btree(treenode *proot)
 {
    if (proot->pleftchild != NULL)
    {
        postorder_btree(proot->pleftchild);
    }
    if (proot->prightchild != NULL)
    {
        postorder_btree(proot->prightchild);
    }
    printf("%d ", proot->no);
    return 0;
 }
4.二叉树的层序遍历
        思路 :
  • 利用二叉树中数据出入队列过程实现二叉树的层序遍历
  • 上一层节点入队,判断左子树 / 右子树是否为空,若不是则让左子树 / 右子树入队,若两个子树均为空,则让该节点出队,并打印该节点标签号
        代码实现:
void layout_treenode(treenode *proot)
{
    treenode *ptmptree = NULL;      //存储二叉树
    linknode *ptmplink = NULL;      //存储队列

    ptmplink = malloc(sizeof(linknode));  //申请队列空间
    if(ptmplink == NULL)
    {
        perror("error to malloc");
    }

    enter_linknode(ptmplink,proot);       //将根节点插入队列
    while(!is_empty_linklist(ptmlink))    //判断队列是否为空
    {
        treenode = quit_linklist(ptmlink);  //将队头元素一处队列,并将队头元素地址赋treenode
        printt("%d ",treenode->no);         //打印树节点编号
        
        //当左右子树都为空时,不再有新节点插入队列,退出循环
        if(treenode->leftchild != NULL)
        {
            //若节点左子树不为空,则将左子树插入队列
            enter_linknode(ptmplink,treenode->leftchild);
        }
        if(treenode->rightchild != NULL)
        {
            //若节点左子树不为空,则将左子树插入队列
            enter_linknode(ptmplink,treenode->rightchild);
        }
    } 
}

5.二叉树的销毁

        思路:在后序遍历代码的基础上,将打印改为释放节点空间即可

        代码:

 /* 后序遍历 */
 int postorder_btree(treenode *proot)
 {
    if (proot->pleftchild != NULL)
    {
        postorder_btree(proot->pleftchild);
    }
    if (proot->prightchild != NULL)
    {
        postorder_btree(proot->prightchild);
    }    
    free(proot);
    return 0;
 } 
6.非完全二叉树的创建

        因非完全二叉树结构不固定,所以需要通过终端输入的方式创建非完全二叉树

        代码实现:

treenode *creat_uptreenode(void)
{
    treenode *ptmpnode = NULL;
    int data = 0;

    scanf("%d",&data);                        

    if(data == -1)
    {
        return NULL;
    }

    ptreenode = malloc(sizeof(treenode));
    if(ptreenode == NULL)
    {
        perror("error to malloc");
        return NULL;
    }

    ptreenode->no = data;
    ptrernode->leftchild = creak_uptreenode();
    ptreenode->rightchild = creak_uptreenode();
    
    return ptreenode;
}
7.获得树的高度 / 最大层数
/* 获得树的高度(最大层数) */
int get_bintree_height(treenode *proot)
{
    int leftheight = 0;   // 初始化左子树的高度变量
    int rightheight = 0;  // 初始化右子树的高度变量

    // 如果当前节点为空,说明到达了一片空树或叶子的下一层
    if (NULL == proot)
    {
        return 0; // 空树的高度为0
    }

    // 递归调用自身,计算左子树的高度
    leftheight = get_bintree_height(proot->pleftchild);
    // 递归调用自身,计算右子树的高度
    rightheight = get_bintree_height(proot->prightchild);

    // 两个子树中较高的高度 + 1(加上当前节点所在的层)
    // 这样递归返回到上层,就会得到整棵树的最大深度
    return (leftheight > rightheight ? leftheight : rightheight) + 1;
}
8.二叉树的深度遍历实现(非递归实现)
        思路:
  • 前序遍历与中序遍历
    • 从根结点出发到叶子节点,将所有左孩子压入栈中
    • 出栈一个节点,将该节点的右孩子按上一步的方法压入栈中
    • 重复上述步骤,直到栈空停止
    • 前序遍历在入栈时打印数据,中序遍历在出栈时打印数据
  • 后序遍历
    • 因为最后打印根节点,所以需要让根节点入栈两次
    • 第一次出栈是为了找到根节点的右孩子
    • 第二次如展示打印数据即为后序遍历的结果
        代码实现:
/* 前序遍历与中序遍历 */
void preorder_tree(treenode *proot)
{
    linknode *ptmpstack = NULL;
    treenode *ptmptree = NULL;

    ptmpstack = create_linkstack();          //申请栈空间
    
    ptmptree = proot;
    while(1)
    {
        while(ptmptree != NULL)
        {
            printf("%d ",ptmptree->data);     //前序遍历打印点
            push_linkstack(ptmstack,ptmptree);//找到最下端的左孩子
            ptmptree = ptmptree->liftchild;
        }

        if(is_empty_linkstack)
        {
            break;
        }
        ptmptree = pop_linkstack(ptmstack);   //元素出栈
        printf("%d ",ptmptree->data);         //中序遍历打印点
        ptmptree = ptmptree->rightchild;        
    }
}

/* 非递归后序遍历 */
 int postorder_btree_bystack(treenode *proot)
 {
    linknode *ptmpstack = NULL;
    treenode *ptmpnode = NULL;
    ptmpstack = create_empty_linkstack();
    
    ptmpnode = proot;
    while (1)
    {
        while (ptmpnode != NULL)
        {
            ptmpnode->flag = 1;                    //入栈次数标志位
            push_linkstack(ptmpstack, ptmpnode);
            ptmpnode = ptmpnode->pleftchild;
        }   
        if (is_empty_linkstack(ptmpstack))
        {
            break;
        }
        ptmpnode = pop_linkstack(ptmpstack);
        if (1 == ptmpnode->flag)
        {
            ptmpnode->flag = 0;
            push_linkstack(ptmpstack, ptmpnode);
            ptmpnode = ptmpnode->prightchild;
        }
        else if (0 == ptmpnode->flag)
        {
            printf("%c ", ptmpnode->data);
            ptmpnode = NULL;
        }
    }
    
    return 0;
 }


网站公告

今日签到

点亮在社区的每一天
去签到