当前位置: 首页 > news >正文

数据结构:二叉树的链式存储

数据结构:二叉树的链式存储(C语言版)

 1.写在前面

  数组表示的优势和弊端

  二叉树同样有两种存储方式,数组和链式存储,对于数组来说,我们利用二叉树的性质然后利用下标可以方便的找到一个节点的子节点和父节点。

    

  

二叉树的性质:
  1.二叉树的第i层上至多有2i-1个节点
  2.深度为K的二叉树至多有2k-1个节点
  3.任何一个二叉树中度数为2的节点的个数必度数为0的节点数目少1.
    说明:度数为0,为叶子节点。
  4.具有n个节点的完全二叉树的深度为|_Log2N_|+1
  5.若完全二叉树中的某节点编号为i,则若有左孩子编号为2i,若有右孩子编号为2i+1,母亲节点为i/2。

  结合第5条性质:

    若完全二叉树中的某节点编号为i,则若有左孩子编号为2i,若有右孩子编号为2i+1,母亲节点为i/2。

  可以在这种完全二叉树中十分方便的找到任何相关联(父子、兄弟等)的元素。

  但是由于顺序存储天生适配于完全二叉树,对于下面这种非完全二叉树并不合适,主要体现在空间上的浪费,所以我们需要用到另一种存储方式——链式存储。

   

  

 

 

 

  引入链表存储

  在链式存储中,每个节点的结构如下

  

结构描述:

  一个存储数据的变量与两个指向孩子的指针域。

  利用指针域我们便可以完美的存储非完全二叉树,如下:

 

 

 

 

 

 

2.代码分解

►首先根据上述图示,我们不难给出节点的描述

typedef struct BiTNode
{
    TElemType data;
    struct BiTNode *lchild,*rchild; 
}BiTNode,*BiTree;

►创建链式二叉树     

  创建二叉树不是很容易的,并且它的创建方式也对应三种顺序。

  其实二叉树的遍历和二叉树的创建可以说是几乎一样的过程。无非是创建的过程是给data赋值,遍历是取出data。  

  我们先不给出创建的代码,而是要明白:

说明:

  1.叶子节点不是没有左右孩子,只不过左右孩子都是空
  2.我们的二叉树是手动输入数据进行创建的 我在实际开发中不可能是这样创建的,所以我们要处理空节点的问题。
  3.创建和遍历的执行顺序(先序、中序、后序)是一样的,那么输入和取出的值才会是一样的。

  比如我们要先序创建一个上述图片中的二叉树,我们应该怎样输入呢?
    AB##CD##EF##G##

 # 在这里是表示叶子节点的左或右节点为空。


  代码如下:

void createBitree(Bitree &T)
{
    char ch;
    if((ch=getchar())=='#')
        T=NULL;
    else
    {
        T=(Bitnode*)malloc(sizeof(Bitnode));
        T->data=ch;
        createBitree(T->Lchild);
        createBitree(T->Rchild);
    }
}                

►遍历代码和创建代码基本一样:

/*先序遍历*/
void preTraverse(Bitree T)
{
    if(T!=NULL)
    {
        printf("%c",T->data);
        preTraverse(T->Lchild);
        preTraverse(T->Rchild);
       }
}
/*中序遍历*/
void  inorder(Bitree T)
{
    if(T!=NULL)
    { 
        inorder(T->Lchild); 
        printf("%c",T->data);
        inorder(T->Rchild);
    } 
}    
/*后序遍历*/
void  postorder(Bitree T)
{
       
if(T!=NULL)
{
postorder(T->lchild);
postorder(T->rchild);
printf("%c",T->data);
      }
}

  ►我们输入字符后三种遍历情况如下:   

    AB##CD##EF##G##
    先序:ABCDEFG
    中序:BADCFEG
    后序:BDFGECA

►求二叉树的深度

int   Depth(Bitree T)
{//返回深度
    int d,dl,dr;
    if(!T)
        d=0;
    else {
        dl=Depth(T->Lchild);
        dr=Depth(T->Rchild);
        d=1+(dl>dr?dl:dr) ;
    }

    return d;
}

 ►二叉树的层序遍历

    queue<Bitree> TreeQueue; //使用队列
    TreeQueue.push(tree);   //先将队头元素加入队列
    Bitree p = tree;    
    while (!TreeQueue.empty())  //循环判断队列是否未空,若不空则
    {
        p = TreeQueue.front();  //获取队列头节点,并出队列
        TreeQueue.pop();        
        printf(" %c ", p->data); //打印队列元素

        if (p->Lchild)     //如果该节点有左节点
        {
            TreeQueue.push(p->Lchild);  //加入队列
        } 
        if (p->Rchild)    //如果该节点有右节点
        {
            TreeQueue.push(p->Rchild); //加入队列
        }
    }

  |说明:

    1.算法轨迹:

      依旧使用下图:

    

 

 

 

 

  演示:

      我们首先将A加入队列, 此时对列 中只有    ⇐[A]

      我们将A出弹出队列,并将它的左右子树 BC加入队列,此时队列中有 ⇐[BC] ,打印出 A

      我们将B出弹出队列,它没有子树,我们不做任何操作,此时队列中有 ⇐[C] ,打印出 ABC

      我们将C出弹出队列,并将它的左右子树 DE加入队列,此时队列中有 ⇐[DE] ,打印出 ABC

      我们将D出弹出队列,它没有子树,我们不做任何操作,此时队列中有 ⇐[E] ,打印出 ABCD

      我们将E出弹出队列,并将它的左右子树 FG加入队列,此时队列中有 ⇐[FG] ,打印出 ABCDE

      我们将F出弹出队列,它没有子树,我们不做任何操作,此时队列中有 ⇐[G] ,打印出 ABCDEF

      我们将G出弹出队列,它没有子树,我们不做任何操作,此时队列中有 ⇐[null] ,打印出 ABCDEFG

  结论:

      根据轨迹我们不难发现,队列是保证层序遍历的基础,因为它保证了先加入队列的上层元素会必后加入队列的下层元素优先出队列。

    2.这段代码直接使用会出现问题,因为我们使用了C++标准模板库中的queue。所以,我们需要加入头文件

      #include<queue>,并且要使用命名空间 using namespace std;

    3.我们对待数据结构最重要的是理解和使用,实现它只是帮助我们理解,我们无须重复造轮子,在以后的算法中,我们将尽可能的引用标准模板库。

 

转载于:https://www.cnblogs.com/MrSaver/p/6059918.html

相关文章:

  • 用正则表示式分析网页
  • iOS AFNetworking 打印从服务器返回的错误提示信息
  • Ubuntu16.04安装网易云音乐
  • OC 图片圆角实现
  • 常用设计模式之适配器
  • 加速度传感器检测物体倾角的原理
  • codeforces 734E(DFS,树的直径(最长路))
  • php-fpm服务启动脚本
  • html关于图片和链接的笔记
  • jQuery 语法
  • 【FFMPEG】FFMPEG介绍
  • [原创软件]Maya语言切换工具
  • 【GoLang】GoLang 错误处理 -- 异常处理思路示例
  • Tower 实战一:MavLink的连接与通信
  • hive 数据清理--数据去重
  • 【vuex入门系列02】mutation接收单个参数和多个参数
  • 2017-09-12 前端日报
  • Akka系列(七):Actor持久化之Akka persistence
  • Angular Elements 及其运作原理
  • angular2 简述
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • vue 个人积累(使用工具,组件)
  • 闭包--闭包之tab栏切换(四)
  • 程序员该如何有效的找工作?
  • 飞驰在Mesos的涡轮引擎上
  • 关于 Cirru Editor 存储格式
  • 紧急通知:《观止-微软》请在经管柜购买!
  • 聊聊directory traversal attack
  • 使用SAX解析XML
  • 学习笔记TF060:图像语音结合,看图说话
  •  一套莫尔斯电报听写、翻译系统
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • 微龛半导体获数千万Pre-A轮融资,投资方为国中创投 ...
  • ​中南建设2022年半年报“韧”字当头,经营性现金流持续为正​
  • (04)Hive的相关概念——order by 、sort by、distribute by 、cluster by
  • (175)FPGA门控时钟技术
  • (NO.00004)iOS实现打砖块游戏(九):游戏中小球与反弹棒的碰撞
  • (Redis使用系列) SpringBoot 中对应2.0.x版本的Redis配置 一
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (四)Android布局类型(线性布局LinearLayout)
  • (原)Matlab的svmtrain和svmclassify
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • .bat批处理(二):%0 %1——给批处理脚本传递参数
  • .Net Core与存储过程(一)
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .NET6使用MiniExcel根据数据源横向导出头部标题及数据
  • .NET高级面试指南专题十一【 设计模式介绍,为什么要用设计模式】
  • .NET实现之(自动更新)
  • @KafkaListener注解详解(一)| 常用参数详解
  • [ Linux 长征路第二篇] 基本指令head,tail,date,cal,find,grep,zip,tar,bc,unname
  • [Android]使用Retrofit进行网络请求
  • [AX]AX2012 R2 出差申请和支出报告
  • [Bada开发]初步入口函数介绍
  • [codevs 1288] 埃及分数 [IDdfs 迭代加深搜索 ]