文章的主体内容
1.链式二叉树链式二叉树的表示法
2.二叉树的前中后序遍历思想
3.求二叉树节点的个数及叶子节点的个数及第k层节点数
5.查找二叉树值为x的节点
6.二叉树的层序遍历
7.二叉树的深度
8…二叉树的销毁

**正文开始: **
  在上一篇博客里,我们已经接触过了二叉树的顺序存储结构以及堆的实现和TOK问题。那么今天我们介绍的是二叉树的链式存储结构,相对于顺序结构而言,在二叉树的OJ题里更多是链式结构形式更多!所有二叉树的链式存储结构我们同样也要掌握!另外,从现在开始,我们看一棵树的角度就要发生转变:即我们看一棵树都要看成:根节点---->左子树---->右子树,这点对于我们后面的一些思想和OJ题目里非常重要!
一.链式二叉树及表示法
  从名字不难看出,这是用链表的结构来实现二叉树,通过指针域来连接父节点和左右孩子节点.那么具体的结构定义如下:

typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;//值域
struct BinaryTreeNode* left; //左孩子
struct BinaryTreeNode* right;//右孩子
}BTNode;

使用的时候根据实际的二叉树的图链接指针域即可.
二叉树的前序,中序,后序遍历以及分治算法
  和线性表不一样,树不支持顺序遍历,而是提供了三种遍历节点的方式:前序,中序,后序

前序:先访问根节点,再访问左子树,最后访问右子树
中序:先访问左子树,再访问根节点,最后访问右子树
后序:先访问左子树,再访问右子树,最后访问根节点

  这三种遍历的算法的核心思想就是分治算法,所谓的分治算法的核心就是大问题化解成小的子问题,小的子问题再分解成更小的子问题,一直知道子问题可以拿到结果的一种算法思想,这种算法思想在二叉树的身上更是体现的淋漓尽致!就拿前序遍历来说,把遍历整棵树分解成访问根节点和左子树和右子树,接着又可以再对左子树进行分解,直到分解成空树就得到了结果!由于,递归的思想就是大问题化成小问题,所以在分治算法中会经常见到递归的身影!对于递归算法,没有更好的方法去理解,只有老老实实地化递归展开图来帮助理解。
给定一棵如下的二叉树:要求写出前序遍历的代码实现:

在这里我先给出代码,然后接下来结合代码画递归展开图来帮助大家分析和理解是怎么得到结果的:

/ 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{if (root == NULL)return;printf("%d ", root->data);BinaryTreePrevOrder(root->left);BinaryTreePrevOrder(root->right);
}


因为篇幅的原因,这里我把A的左子树的递归展开图花了出来,右子树的递归展开图类似,我这里就不画出来了。
最后前序遍历的结果是ABDCEF
中序遍历和后序遍历的流程也是类似的,不理解的读者可以自行去画递归展开图帮助理解。
求二叉树节点的个数及叶子节点的个数及第k层节点数
还是以前面的二叉树为例:

那么还是利用分治的思想:如果当前的树是空树,那么节点个数就是0,否则就是求左子树的节点个数和右子树个数相加再加一就是总的个数
具体实现代码如下:

// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

我们同样画出递归展开图来帮助理解代码:

求叶子节点的个数也是类似的思想,具体的思想是:
1.如果是空树,返回 0
2.如果当前节点非空并且左子树为空,右子树为空,返回1
3.如果不是1,2两种情况,递归左子树和右子树
具体代码如下:

int BinaryTreeLeafSize(BTNode* root)
{if (root == NULL)return 0;if (root->left == NULL && root->right == NULL)return 1;return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

递归展开图和求节点类似,这里就不做赘述了。
3.求二叉树第K层的节点个数:
思想和前面的求节点数类似,但是在实际的执行过程中会相对难以理解,这里我会非常仔细的画出递归展开图来帮助理解这个流程

// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{assert(k >= 1);if (NULL == root)return 0;if (k == 1)return 1;return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}

这里我们用一颗层数稍微多一点的二叉树来分析:


3.查找二叉树中值为x的节点
查找有三种情况:

1.空树返回NULL
2.如果当前节点的值是x,直接返回当前节点,
3.如果不是,递归左子树,如果找到了就返回找到的人节点值,如果没有,递归右子树
4.右子树也没有找到,返回NULL

本质上和前序的思想是一样的,不同的是查找是需要记录相应的返回值,找到就可以不用继续递归查找了

/ 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{if (root == NULL)return NULL;if (root->data == x)return root;BTNode* ret1 = BinaryTreeFind(root->left, x);if (ret1)return ret1;BTNode* ret2 = BinaryTreeFind(root->right, x);if (ret2)return ret2;return NULL;

二叉树的层序遍历
  除了前面的前中后序遍历方式,二叉树还有一种遍历方式叫做层序遍历。,那么层序遍历的就是利用队列的先进先出性质,一层带一层地把节点带出,我们来看一下整个层序遍历的流程。

  注意:队列里面存储的不是树的节点,而是树节点的指针,如果取了节点,那么接下来就拿不到节点的左孩子和有孩子,这点要特别注意。
  由于C语言缺乏相应得数据结构,所以我们把先前写好的队列的代码拷贝到当前的项目里,这样队列就可以为我所用。
如果拷贝到当前目录下以后,编译报了一堆错误。不要惊慌失措,大概率是因为头文件展开的时候,编译器向上扫描找不到对应的定义导致,这时候在Queue.h里面加上一个前置声明就可以解决!

struct BinaryTreeNode;//这句话的作用是告诉编译器这是一个结构体。注意一定要用原生的结构体类型,不能是typedef的类型!!!

层序遍历的具体代码如下:

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{Queue q;QueueInit(&q);//一层带一层if (root){QueuePush(&q, root);}while (!QueueEmpty(&q)){BTNode* front = QueueFront(&q);printf("%d ", front->data);QueuePop(&q);//左右孩子非空才入队列if (front->left){QueuePush(&q, front->left);}if (front->right){QueuePush(&q, front->right);}}//队列不用就要释放,防止内存泄露QueueDestroy(&q);
}

二叉树的深度
求二叉树的深度是一种后序遍历的思想,当前节点非空,那么二叉树的深度就是左右孩子深度比较深的+1。
那么对应的代码如下:

int TreeDepth(BTNode* root)
{if (root == NULL)return 0;int leftDepth = TreeDepth(root->left);int rightDepth = TreeDepth(root->right);return leftDepth > rightDepth ? leftDepth + 1 : rightDepth + 1;
}

可能你会好奇为什么不是这样写:

return TreeDepth(root->left) > TreeDepth(root->right) ? TreeDepth(root->left) + 1 : TreeDepth(root->right) + 1;

  这样写得到逻辑上来是正确的,但是第一次递归计算完的结果没有得到保存就又重复递归下去,做了冗余的计算,所以我们选择第一种写法。
二叉树的销毁
销毁一颗二叉树也应用了后序的思想,不能把根节点先释放,否则左右子树就找不到了,所以应该递归销毁左子树,递归销毁右子树,最后再销毁根节点

// 二叉树销毁
void BinaryTreeDestory(BTNode* root)
{if (root == NULL)return;BinaryTreeDestory(root->left);BinaryTreeDestory(root->right);free(root);
}

这里free完没有对root置空的原因是,root是一个局部变量,即使这里置空了,对外界的root也没有实际性的影响,所以再调用这个函数以后,需要用户自动把外界的root置空!
总结:
  对于链式二叉树,每一个节点我们用分治的思想分解成根,左子树,右子树,用分治的眼光看待前中后序遍历,还有层序遍历一层带一层的思想,这些在下一篇的二叉树的常见OJ里面都有体现。
文章如有错误或者不足之处还望指出,希望能和大家共同进步。

二叉树(二)----->链式二叉树(上)相关推荐

  1. 二叉树的链式存储结构--二叉链表

    1 二叉树的链式存储结构 //二叉链表的结点结构定义typedef int TElemType; typedef struct BiTNode {TElemType data;struct BiTNo ...

  2. c语言二叉树链式存储,C语言 二叉树的链式存储实例

    二叉树的链式存储 实现二叉树的基本操作:建立.遍历.计算深度.结点数.叶子数等. 输入C,先序创建二叉树,#表示空节点: 输入H:计算二叉树的高度: 输入L:计算二叉树的叶子个数: 输入N:计算二叉树 ...

  3. 链式二叉树的代码实现

    目录 二叉树的存储结构: 链式二叉树的实现 1.二叉树的结构体类型 2.创建二叉树结点 代码实现: 3.二叉树的前序遍历 测试结果: 4.二叉树的中序遍历 测试结果: 5.二叉树的后序遍历 测试结果: ...

  4. 顺序二叉树(堆)与链式二叉树的C语言实现

    文章目录 树的概念及结构 树的概念 树的相关概念 树的表示 树在实际中的运用 二叉树的概念及结构 二叉树的概念 现实中的二叉树 特殊的二叉树 二叉树的性质 二叉树的存储结构 二叉树的顺序结构及实现 二 ...

  5. 二叉树的链式结构的非递归遍历

    二叉树的链式结构的非递归遍历 一. 非递归前序遍历和非递归中序遍历 1.    Stack.h #ifndef__STACK_H__ #define__STACK_H__ #include<st ...

  6. C语言手写二叉树(链式存储结构)

    C语言手写二叉树(链式存储结构) 二叉树结构 二叉树基本运算 代码 图例(main函数执行过程如下:) 阶段I 阶段II 阶段III 阶段IV 阶段V 先序遍历输出过程 二叉树结构 二叉树可以用顺序存 ...

  7. 【数据结构初阶】第八篇——二叉树的链式结构(二叉树的前、中和后序遍历+层序遍历+链式结构的实现+相关简单的递归问题)

    ⭐️本篇博客我要来和大家一起聊一聊数据结构中的二叉树的链式结构的实现及相关的一些问题的介绍 ⭐️博客代码已上传至gitee:https://gitee.com/byte-binxin/data-str ...

  8. 二叉树的链式存储结构

    文章目录 前言 正文 总结 前言 上一节讲了二叉树的顺序存储,通过学习你会发现,其实二叉树并不适合用数组存储,因为并不是每个二叉树都是完全二叉树,普通二叉树使用顺序表存储或多或多会存在空间浪费的现象. ...

  9. (数据结构)二叉树的链式存储结构

    二叉树的顺序存储的缺点 因为并不是每个二叉树都是完全二叉树,普通二叉树使用顺序表存储或多或少会存在空间浪费的现象 图 1 普通二叉树的转化 如上图 1,普通二叉树里只有二个元素,最好的存储方式当然是开 ...

最新文章

  1. Gradle脚本基础全攻略
  2. 小程序f2自定义html,微信小程序个人产品添加上传样式设计制作开发教程(2)
  3. [SDOI2015]星际战争
  4. C#中提示:System.Runtime.Serialization.SerializationException
  5. MySQL查看表占用空间大小
  6. Oracle11g rac监听,关于oracle11g RAC 监听器使用中出现的no services以及no listener分析...
  7. MySQL索引原理、失效情况
  8. python函数的使用场景_详解python中strip函数的使用场景
  9. ASP.NET2.0入门经典(第4版)—3.5 服务器控件的类型(2)--zt
  10. 数据库设计:范式与反范式
  11. ActiveMQ(19):高级特性之独有消费者(Exclusive Consumer)
  12. 优酷路由宝 OpenWrt 刷机
  13. php mysql购物系统_基于PHPMySQL 的网上购物系统设计与实现
  14. cdlinux教程wpa2无线网络密码破解
  15. 文件恢复:Docrepair-MS Word文档修复软件
  16. 【2022年火爆的商铺共享WiFi系统源码】
  17. 计算机里的word怎么重装,word能卸载重装吗 word卸载重装
  18. 用友u8服务器无法自动启动,u8服务有的没有启动,启动服务,没有反应-用友U8...
  19. 支持向量机(SVM)算法原理
  20. Python 文件 tell() 方法

热门文章

  1. 【重识云原生】第六章容器基础6.4.5.3节——Deployment实现原理解析
  2. 零基础自学Java编程大概需要多久时间?
  3. 手把手教你写代码生成器(也算ORM的续)
  4. 数据库自增id没有从0开始
  5. 禅道的部署测试如何使用禅道
  6. 联想笔记本安装什么系统好?
  7. 芯片底部焊接不良失效分析
  8. Docker踩坑笔记 - 解决Docker下载速度超级慢的问题
  9. 拉伸流变仪VADER 1000-丹麦RHEO FILAMENT
  10. 常见电子面试知识(2/4)