数据结构A3,补充

二叉树和BST

树与二叉树

基本概念

树是一种非线性结构,其严格的数学定义是:如果一组数据中除了第一个节点(第一个节点称为根节点,没有直接前驱节点)之外,其余任意节点有且仅有一个直接前驱,有零个或多个直接后继,这样的一组数据形成一棵树。这种特性简称为一对多的逻辑关系。即用于描述具有层次关系,类似组织架构关系的一种数据结构。

树的组成:根,分支,叶子

常见例子

日常生活中,很多数据的组织形式本质上是一棵树。比如一个公司中的职员层级关系,一个学校中的院系层级关系,淘汰赛中的各次比赛队伍,一个家族中的族谱成员关系等,这些都是树状逻辑结构。由于树状结构表现出来都是具有层次的,因此也被称为层次结构。

相关术语

通常,在逻辑上表达一棵抽象的树状结构的时候,习惯于将树根放在顶部,树枝树杈向下生长,如下图所示

在这里插入图片描述

对于一棵树来说,有如下基本术语:

  1. 结点:
    树中的元素 及其子树

  2. 根(root):
    树的第一个节点,没有直接前驱。如上图中的A。

  3. 双亲节点/父节点(parent):
    某节点的直接前驱称为该节点的双亲节点,或成为父节点。例如上图中A是B的父节点。

  4. 孩子节点/子节点(child):
    某节点的直接后继称为该节点的孩子节点。例如上图中B、C、D均为A的孩子节点。

  5. 节点的层次(level):
    根节点所在的层次规定为第1层,其孩子所在的层次为第2层,后代节点以此类推。比如上图中节点E的层次是3。

  6. 节点的度(degree):
    一个节点拥有的孩子节点的总数,称为该节点的度。比如上图中节点B的度为2。

  7. 叶子(leaf):
    一棵树中度等于0的节点,被称为叶子,又称为终端节点。比如上图中K、L、F、G、M、I、J均为叶子。

  8. 树的高度(height):
    一棵树中所有节点的层次的最大值,称为这棵树的高度,又称为树的深度。比如上图的树的高度为4。

  9. 有序树与无序树:
    一棵树中,如果某个节点的孩子节点之间是有次序的,则称这棵树为有序树,反之称为无序树。

二叉树

在各种不同的树状结构中,最常见也最重要的是二叉树(Binary Tree),下面是二叉树的定义:

  • 有序树
  • 任意节点的度小于等于2

比如如下这棵树就是一棵二叉树。其中8是根节点,14是10的右孩子(因为二叉树是有序树,因此严格区分左右),而13则是14的左孩子。

在这里插入图片描述

为了方便对二叉树进行操作,通常会对一棵它进行标号:从上到下,从左到右进行标号:

在这里插入图片描述

对于二叉树而言,有如下特性:

  1. 第𝑖层上,最多有2𝑖−1个节点。

  2. 高度为𝑘的二叉树,最多有2𝑘−1个节点。

  3. 假设叶子数目为𝑛0,度为2的节点数目为𝑛2,则有:n0 = n2 + 1

二叉树的一般结构:
满二叉树
一棵深度为k,且有2^k-1个结点的二叉树,称为满二叉树。这种树的特点是每一层上的结点数都是最大结点数。
简单理解: 除了叶子节点之外,其余节点的度都为2;其特点是: 如果深度为 K,则节点数为 2^K-1

在这里插入图片描述

完全二叉树
在一棵二叉树中,除最后一层外,若其余层都是满的,或者最后一层是满的,或者是最后一层在右边缺少连续若干结点,则此二叉树为完全二叉树。

简单理解:除最后一层叶子节点外。是一颗满二叉树,最后一层由右向左有连续缺省的0个,1个或多个节点。

在这里插入图片描述
二叉搜索树(BST)
特点:

  1. 如果节点具有左子树,则左子树上所有节点都不大于该节点的值;
  2. 节点具有右子树,则右子树上所有节点都不小于该节点的值;
  3. 子树又是二叉搜索数

在这里插入图片描述

二叉搜索树(BST)

二叉树(BST)的组成

  • 根指针:指向根节点的指针

  • 节点:

  • 数据域 (存储的实际数据)

  • 指针域 (左,右指针)

  • 结构设计:

typedef int data_t;
typedef struct _node
{
data_t data; // 数据域
struct _node *left; // 左子树指针
struct _node *right;// 右子树指针
}NODE;

二叉树(BST)的算法

创建二叉树

int btree_create(NODE** root,data_t data); 

二叉树数据添加

int btree_add(NODE** root,data_t data); 

在这里插入图片描述

二叉树数据遍历

  • 前序遍历(先序遍历,即 根左右 ))
void Preorder(const NODE* root); 

前序遍历通俗的说就是从二叉树的根结点出发,先输出根结点数据,然后输出左结点,最后输
出右结点的数据。

在这里插入图片描述

从根结点出发,则第一次到达结点A,故输出A;继续向左访问,第一次访问结点B,故输出B;
按照同样规则,输出D,输出H;当到达叶子结点H,返回到D,此时已经是第二次到达D,故不
在输出D,进而向D右子树访问,D右子树不为空,则访问至I,第一次到达I,则输出I;I为叶子
结点,则返回到D,D左右子树已经访问完毕,则返回到B,进而到B右子树,第一次到达E,故
输出E;向E左子树,故输出J;按照同样的访问规则,继续输出C、F、G。
前序遍历输出结果:ABDHIEJCFG

  • 中序遍历(即 左根右 )

    void Midorder(const NODE* root); 
    

    中序遍历通俗的说就是从二叉树的根结点出发,先输出左结点数据,然后输出根结点,最后输
    出右结点的数据。

    在这里插入图片描述

    从根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出
    B;继续到达D,H;
    到达H,H左子树为空,则返回到H,此时第二次访问H,故输出H;H右子树为空,则返回至
    D,此时第二次到达
    D,故输出D;由D返回至B,第二次到达B,故输出B;按照同样规则继续访问,输出J、E、A、
    F、C、G;
    中序遍历输出结果:HDIBJEAFCG

    • 后序遍历(即 左右根 )

      void Postorder(const NODE* root); 
      

      后序遍历通俗的说就是从二叉树的根结点出发,先输出左结点数据,然后输出右结点,最后输出根结点的数据。

      在这里插入图片描述

      从根结点出发,则第一次到达结点A,不输出A,继续向左访问,第一次访问结点B,不输出
      B;继续到达D,H;
      到达H,H左子树为空,则返回到H,此时第二次访问H,不输出H;H右子树为空,则返回至
      H,此时第三次到达
      H,故输出H;由H返回至D,第二次到达D,不输出D;继续访问至I,I左右子树均为空,故第
      三次访问I时,输出
      I;返回至D,此时第三次到达D,故输出D;按照同样规则继续访问,输出J、E、B、F、G、
      C,A

      后序遍历输出为: HIDJEBFGCA

      • 层序遍历
      void Levelorder(const NODE* root); 
      
      • 二叉树数据查询
      NODE* btree_find(const NODE* root,data_t data); 
      

      从根结点出发
      ② 如果比根节点小,那么就去其左子树找
      ③ 如果比根节点大就去其右子树找
      ④ 找到叶子都没找到, 就代表查找失败

      在这里插入图片描述

      • 二叉树数据更新
      int btree_update(const NODE* root,data_t old,data_t newdata); 
      
      • 二叉树回收
      void btree_destroy(NODE** root); 
      
      • 二叉树数据删除
      int btree_delete(NODE** root,data_t data); 
      

      原则:将待删除的节点尽量转换为删除叶子节点,因为删除叶子节点对BST树影响是最小的;
      思路:
      ① 从根节点开始遍历BST找到待删除的节点;
      ② 对待删除的节点状态进行判断,如果节点有左子树,找到左子树中最大的节点,然后利用
      左子树中最大的节
      点数据替换待删除的节点数据,删除左子树中最大的节点;左子树中最大的节点大概率
      是叶子节点。
      ③ 如果节点只有右子树,找到右子树中最小的节点,然后 利用右子树中最小的节点数据替
      换待删除的节点数
      据,删除右子树中最小的节点;右子树中最小的节点大概率是叶子节点。
      ④ 如果待删除节点是叶子节点,直接删除。

      在这里插入图片描述

      二叉树(BST)完整实现

      队列实现

      • SQueue.h
      Queue.h
          SQueue.c
          #ifndef __SQUEUE_H
          #define __SQUEUE_H
          #include "btree.h"
          typedef NODE* type_t;
      typedef struct
      {
          type_t *pData;
          int size;
          int head;
          int tail;
      }SQueue;
      int SQ_init(SQueue *q, int num);
      int SQ_isfull(SQueue *q);
      int SQ_isempty(SQueue *q);
      int SQ_push(SQueue *q,type_t data);
      int SQ_pop(SQueue *q,type_t *data);
      int SQ_free(SQueue *q);
      #endif
      
      • SQueue.c
      #include <stdlib.h>
      #include "SQueue.h"
      int SQ_init(SQueue* q,int num)
      {
          q -> pData = (type_t*)calloc(sizeof(type_t),num);
          if(q -> pData == NULL)
              return -1;
          q -> size = num ;
          q -> head = q -> tail = 0;
          return 0;
      }
      int SQ_isfull(SQueue *q)
      {
          return (q -> tail + 1) % q -> size == q -> head;
      }
      int SQ_isempty(SQueue *q)
      {
          return q -> tail == q -> head;
      }
      int SQ_push(SQueue *st,type_t data)
      {
          if(SQ_isfull(st))
              return -1;
          st -> pData[st -> tail] = data;
          st -> tail = (st -> tail+1) % st -> size;
          return 0;
      }
      int SQ_pop(SQueue *st,type_t *data)
      {
          if(SQ_isempty(st))
              return -1;
          *data = st -> pData[st -> head];
          st -> head = (st -> head+1) % st -> size;
          return 0;
      }
      int SQ_free(SQueue *st)
      {
          if(st -> pData)
          {
              free(st->pData);
              st -> pData = NULL;
          }
          st -> head = st -> tail = 0;
      }    
      

      树的实现

      • btree.h
      ree.h
      btree.c
      #ifndef __BTREE_H
      #define __BTREE_H
      typedef int data_t;
      typedef struct _node
      {
      data_t data; // 节点上的数据
      struct _node *left; // 该节点左侧子节点的地址
      struct _node *right;// 该节点右侧子节点的地址
      }NODE;
      // 创建搜索二叉树
      int btree_create(NODE** root,data_t data);
      // 二叉树数据添加
      int btree_add(NODE** root,data_t data);
      // 二叉树数据删除
      int btree_delete(NODE** root,data_t data);
      // 二叉树前序遍历
      void Preorder(const NODE* root);
      // 二叉树中序遍历
      void Midorder(const NODE* root);
      // 二叉树后序遍历
      void Postorder(const NODE* root);
      // 二叉树层序遍历
      void Levelorder(const NODE* root);
      // 二叉树数据查询
      NODE* btree_find(const NODE* root,data_t data);
      // 更新二叉树数据old 为 newdata
      int btree_update(const NODE* root,data_t old,data_t newdata);
      // 二叉树回收
      void btree_destroy(NODE** root);
      #endif
      
      • btree.c
      #include "btree.h"
      #include <stdlib.h>
      #include <stdio.h>
      #include <string.h
      #include "SQueue.h"
      /*
      @function: int btree_create(NODE** root,data_t data)
      @brief: 创建搜索二叉树
      @argument: root: 根指针地址
      @argument: data: 存储的数据
      @ret : 0 成功
      -1 失败
      */
      int btree_create(NODE** root,data_t data)
      {
          if(*root)
              return -1;
          NODE* p = (NODE*)malloc(sizeof(NODE));
          if(!p)
              return -1;
          p -> data = data;
          p -> left = NULL;
          p -> right = NULL;
          *root = p;
          return 0;
      }
      /*
      @function: int btree_add(NODE** root,data_t data)
      @brief: 二叉树数据添加
      @argument: root: 根指针地址
      @argument: data: 添加的数据
      @ret : 0 成功
      -1 失败
      */
      int btree_add(NODE** root,data_t data)
      {
          NODE* pNew = (NODE*)malloc(sizeof(NODE));
          if(!pNew)
              return -1;
          pNew -> data = data;
          pNew -> left = NULL;
          pNew -> right = NULL;
          NODE* p = *root, *q = NULL;
          if(!p)
          {
              *root = pNew;
              return 0;
          }
          while(p)
          {
              q = p;
              if(memcmp(&data,&(p -> data),sizeof(data_t)) < 0)
                  p = p -> left;
              else
                  p = p -> right;
          }
          if(memcmp(&data,&(q -> data),sizeof(data_t)) < 0)
              q -> left = pNew;
          else
              q -> right = pNew;
          return 0;
      }
      /*
      @function: int btree_delete(NODE** root,data_t data)
      @brief: 二叉树数据删除
      @argument: root: 根指针地址
      @argument: data: 待删除的节点数据
      @ret : 0 成功
      -1 失败
      */
      int btree_delete(NODE** root,data_t data)
      {
          /*
      原则:将待删除的节点尽量转换为删除叶子节点,因为删除叶子节点对BST树影响是最小的;
      思路:
      1. 从根节点开始遍历BST找到待删除的节点;
      2. 对待删除的节点状态进行判断,如果节点有左子树,找到左子树中最大的节点,然后利用左子树中最大的
      节点数据替换待删除 的节点数据,删除左子树中最大的节点;左子树中最大的节点大概率是叶子节点
      3. 如果节点只有右子树,找到右子树中最小的节点,然后 利用右子树中最小的节点数据替换待删除的节点
      数据,删除右子树中 最小的节点;右子树中最小的节点大概率是叶子节点
      4. 如果待删除节点是叶子节点,直接删除。
      */  
          NODE* del = *root; //指向待删除的节点
          NODE* parent = NULL; //指向实际删除节点的双亲节点
          NODE* replace = NULL; //指向实际删除的节点
          while(del)
          {
              if(memcmp(&(del -> data),&data,sizeof(data_t)) < 0) //待删除的数据大于当前节点数据,向右子
                  树继续查找
              {
                  parent = del;
                  del = del -> right;
              }
              else if(memcmp(&(del->data),&data,sizeof(data_t)) > 0) //待删除的数据小于当前节点数据,向
                  左子树继续查找
              {
                  parent = del;
                  del = del -> left;
              }
              else // 找到了待删除的节点
              {
                  if(del -> left) //待删除的节点有左子树
                  {
                      parent = del ;
                      replace = del -> left;
                      while(replace -> right) //找左子树中最大的节点
                      {
                          parent = replace;
                          replace = replace -> right;
                      }
                      del -> data = replace -> data;
                      if(parent -> right == replace) //实际删除的节点在双亲节点的右子树上
                          parent -> right = replace -> left;
                      else
                          parent -> left = replace -> left;
                      free(replace);
                  }
                  else if(del -> right) //待删除的节点只有右子树
                  {
                      parent = del;
                      replace = del -> right;
                      while(replace -> left)
                      {
                          parent = replace;
                          replace = replace -> left;
                      }
                      del -> data = replace -> data;
                      if(parent -> right == replace)
                          parent -> right = replace -> right;
                      else
                          parent -> left = replace -> right;
                      free(replace);
                  }
                  else //待删除的节点是叶子
                  {
                      if(!parent)
                      {
                          free(del);
                          *root = NULL;
                          return 0;
                      }
                      if(parent -> left == del)
                          parent -> left = NULL;
                      else
                          parent -> right = NULL;
                      free(del);
                  }
                  return 0;
              }
          }
          return -1;
      }
      /*
      @function: void Preorder(const NODE* root)
      @brief: 二叉树前序遍历
      @argument: root: 根指针
      @ret : 无
      */
      void Preorder(const NODE* root)
      {
          if(!root)
              return ;
          printf("%4d",root -> data);
          Preorder(root->left);
          Preorder(root->right);
      }
      /*
      @function: void Midorder(const NODE* root)
      @brief: 二叉树中序遍历
      @argument: root: 根指针
      @ret : 无
      */
      void Midorder(const NODE* root)
      {
          if(!root)
              return ;
          Midorder(root->left);
          printf("%4d",root -> data);
          Midorder(root->right);
      }
      /*
      @function: void Postorder(const NODE* root)
      @brief: 二叉树后序遍历
      @argument: root: 根指针
      @ret : 无
      */
      void Postorder(const NODE* root)
      {
          if(!root)
              return ;
          Postorder(root->left);
          Postorder(root->right);
          printf("%4d",root -> data);
      }
      /*
      @function: void Postorder(const NODE* root)
      @brief: 二叉树层序遍历
      @argument: root: 根指针
      @ret : 无
      */
      void Levelorder(const NODE* root)
      {
          SQueue q;
          SQ_init(&q,100);
          SQ_push(&q,(type_t)root);
          while(!SQ_isempty(&q))
          {
              type_t dt;
              if(0 == SQ_pop(&q,&dt))
              {
                  printf("%4d",dt -> data);
                  if(dt -> left)
                      SQ_push(&q,dt->left);
                  if(dt -> right)
                      SQ_push(&q,dt->right);
              }
          }
          printf("\n");
          SQ_free(&q);
      }
      /*
      @function: NODE* btree_find(const NODE* root,data_t data)
      @brief: 二叉树数据查询
      @argument: root: 根指针
      @argument: data: 待查询的数据
      @ret : 成功:返回查询到的节点地址
      失败: NULL
      */
      NODE* btree_find(const NODE* root,data_t data)
      {
          const NODE *p = root;
          while(p)
          {
              if(memcmp(&data,&(p -> data),sizeof(data_t)) < 0)
                  p = p -> left;
              else if(memcmp(&data,&(p -> data),sizeof(data_t)) > 0)
                  p = p -> right;
              else
                  return (NODE*)p;
          }
          return NULL;
      }
      /*
      @function: int btree_update(const NODE* root,data_t old,data_t newdata)
      @brief: 更新二叉树数据old 为 newdata
      @argument: root: 根指针
      @argument: old: 待更新的数据
      @argument: newdata: 更新后的数据
      @ret : 成功:0
      失败: -1
      */
      int btree_update(const NODE* root,data_t old,data_t newdata)
      {
          NODE* pfind = btree_find(root,old);
          if(!pfind)
              return -1;
          pfind -> data = newdata;
          return 0;
      }
      /*
      @function: void btree_destroy(NODE** root)
      @brief: 二叉树回收
      @argument: root: 根指针地址
      @ret : 无
      */
      static void btree_free(NODE* root)
      {
          if(!root)
              return ;
          btree_free(root->left);
          btree_free(root->right);
          free(root);
      }
      void btree_destroy(NODE** root)
      {
          btree_free(*root);
          *root = NULL;
      }
      
      • btree_main.c
      #include "btree.h"
      #include <stdio.h>
      #include <time.h>
      #include <stdlib.h>
      #define N 10
      int main(void)
      {
          NODE *root = NULL;
          register int i = 0;
          data_t a[] = {20,15,25,10,19,28,6,13,26,30,11,27};
          int n = sizeof a / sizeof a[0];
          srand(time(NULL));
          for(; i < n ; i++)
          {
              // int data = rand() % 99 + 1;
              // printf("%4d",data);
              // btree_add(&root,data);
              btree_add(&root,a[i]);
          }
          printf("\n");
          puts("====先序遍历====");
          Preorder(root);
          printf("\n");
          puts("====中序遍历====");
          Midorder(root);
          printf("\n");
          puts("====后序遍历====");
          Postorder(root);
          printf("\n");
          puts("====层序遍历====");
          Levelorder(root);
          printf("\n");
          data_t deldata = 0;
          while(1)
          {
              printf("请输入要删除的数据(-1退出):");
              scanf("%d",&deldata);
              if(deldata == -1)
                  break;
              if(btree_delete(&root,deldata) == -1)
              {
                  puts("删除失败请重试...");
                  continue;
              }
              puts("====先序遍历====");
              Preorder(root);
              printf("\n");
          }
          btree_destroy(&root);
          return 0;
      }    
      

    puts(“中序遍历”);
    Midorder(root);
    printf(“\n”);
    puts(“后序遍历”);
    Postorder(root);
    printf(“\n”);
    puts(“层序遍历”);
    Levelorder(root);
    printf(“\n”);
    data_t deldata = 0;
    while(1)
    {
    printf(“请输入要删除的数据(-1退出):”);
    scanf(“%d”,&deldata);
    if(deldata == -1)
    break;
    if(btree_delete(&root,deldata) == -1)
    {
    puts(“删除失败请重试…”);
    continue;
    }
    puts(“先序遍历”);
    Preorder(root);
    printf(“\n”);
    }
    btree_destroy(&root);
    return 0;
    }
    ```

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值