二叉树,通常应当是研究其他一些复杂的数据结构的基础。因此,通常我们应该精通它,而不是了解;当然,可能并不是每个人都认同这种观点,甚至有些人认为理解数据结构就行了!根本没有必要去研究如何实现,因为大多数高级语言已经包含了非常好用的实现接口,直接调用即可。我曾经很难理解为什么有些同学那么的努力去学习算法,然后参加ACM比赛,我甚至连最基本的斐波那契也无法实现。但是我任然心里嘲笑他们,因为我当时正在学习Linux系统编程。我觉得自己做的才是正确的有意义的事。其实,现在我觉得算法是一个程序员的基本素质高低的体现;

好啦!扯远啦,继续我们的二叉树04版本;

我很想在这个版本就终结了,但是二叉树实在太多内容啦;

故事是这样开始的:

项目经理说,你看你上次写的二叉树展示的功能很形象嘛!

我都不用在过多解释,BOSS就看出来是树的结构啦;

所以,我们都很相信你接下来的任务也会完成的很好的;

Task

1,我们需要一个销毁整棵树的功能;

2,我们需要三种常见的遍历方式;

3,我们还需要一种叫做水平遍历的功能;

4,我们需要你给我讲讲如何对树进行旋转;

Problem

树的销毁,是对每一个节点的释放;

因此,我们需要遍历每一个节点;

但是和查找遍历不同,我们会删除掉节点,因此我们一定要想清楚先释放哪个;

一定不要“打草惊蛇”让其他节点跑啦;

Solution

理解的结构才能很好的销毁它,先看看下面的图吧;

我们显然不应该先删除3节点;

我们应该是先删除叶子节点;

所以我们可以使用一种叫做后序遍历的方式解决;

我们要删除的是这样一颗树,我们都知道,树的操作一般都可以用递归解决;

因此,首先思考递归怎么解决吧;

  1. void destroy_recursive(Node *root)
  2. {
  3. if(root != NULL){
  4. //TODO
  5. }
  6. }

我们知道,只有这个节点不为空,才能够说对其进行销毁;

所以,首先要习惯判空,然后进行下一步;

  1. void destroy_recursive(Node *root)
  2. {
  3. if(root != NULL){
  4. free(root);
  5. }
  6. }

这样写,或许这是你的第一想法。但是想想,对那棵树而言你只是把树的根砍啦;

你没有上树去摘果子,这就照成浪费啦(大量的内存泄露);

  1. void destroy_recursive(Node *root)
  2. {
  3. if(root != NULL){
  4. destroy_recursive(root->link[]);
  5. destroy_recursive(root->link[]);
  6. free(root);
  7. }
  8. }

这样做得话,你就顺利的从树根到树顶,把所有的果子摘下来后;

还把上面的树枝砍啦,一直到当年下到地面上得时候把根也抛啦;

任务就完成啦;

但是,你的函数由于是一个递归的裸函数,所以你需要把它包装一下;

  1. void destroy(Tree *tree)
  2. {
  3. destroy_recursive(tree->root);
  4. }

这个函数我们待会在测试吧,就好比刚发明的炸弹最后等我们研究透再点火;

不然炸了我们都不知道怎么再造;

Problem

刚才,我们已经使用过递归遍历啦;

显然递归解决树的问题真是手到擒来;

对于三种常见的遍历也是非常简单的,而且是非常相似的;

  1. void preorder_recursive(Node *root)
  2. {
  3. if(root != NULL){
  4. printf("%d\n",root->data);
  5. preorder_recursive(root->link[]);
  6. preorder_recursive(root->link[]);
  7. }
  8. }
  9.  
  10. void preorder(Tree *tree)
  11. {
  12. preorder_recursive(tree->root);
  13. }

上面是前序遍历,如果你已经知道中序和后序怎么写,你最好暂时不要往下看啦;

中序遍历:

  1. void inorder_recursive(Node *root)
  2. {
  3. if(root != NULL){
  4. inorder_recursive(root->link[]);
  5. printf("%d\n",root->data);
  6. inorder_recursive(root->link[]);
  7. }
  8. }
  9.  
  10. void inorder(Tree *tree)
  11. {
  12. inorder_recursive(tree->root);
  13. }

后序遍历:

  1. void postorder_recursive(Node *root)
  2. {
  3. if(root != NULL){
  4. postorder_recursive(root->link[]);
  5. postorder_recursive(root->link[]);
  6. printf("%d\n",root->data);
  7. }
  8. }
  9.  
  10. void postorder(Tree *tree)
  11. {
  12. postorder_recursive(tree->root);
  13. }

好啦任务又完成啦一个;

Problem

水平遍历就比较特殊啦,我们不能够使用递归解决啦;

  1. void level_order(Tree *tree)
  2. {
  3. Node *it = tree->root;
  4. Node *queue[];
  5. int current = ;
  6. int after_current = ;
  7. if(it == NULL){
  8. return ;
  9. }
  10. //TODO
  11.  
  12. }

首先解释一下我们的局部变量,这些变量是请来协助大家解决问题的;

自我介绍一下吧;

hey,我叫queue数组,我的类型是Node类型,因为我将存储Node类型的数据,我的功能是保存你当前访问到的节点;

hello,我叫current,的类型是基本类型,我主要负责记录当前在树的哪一个节点上;

wow,我叫after_current,我不好意思来晚啦。我的工作很简单,就是负责跟在current后面记录它走过的最新节点之前的一个;

我们再看看我们的实施方案吧,先把图再打开;

水平访问无法是

第一层,3

第二层,1,4

第三层,0,2

好啦,我们已经知道要实现的效果啦;

current你先和queue把当前的it记录一下;

我们的it待会可能要改变自己啦;

  1. queue[current++]=it;

current说,我已经记录下来啦,并且我现在准备记录下一条信息,我现在已经是1“岁”啦;

  1. void level_order(Tree *tree)
  2. {
  3. Node *it = tree->root;
  4. Node *queue[];
  5. int current = ;
  6. int after_current = ;
  7. if(it == NULL){
  8. return ;
  9. }
  10.  
  11. queue[current++] = it;
  12. while(current != after_current){
  13. //TODO
  14. }
  15. }

after_current很不耐烦的对while说,你到底要干什么,你难道非得让我和current比较吗?你明明知道他提前比我长了一岁;

while很亲切的说不用担心,你只要按照我的方式你会赶上它的;

  1. void level_order(Tree *tree)
  2. {
  3. Node *it = tree->root;
  4. Node *queue[];
  5. int current = ;
  6. int after_current = ;
  7. if(it == NULL){
  8. return ;
  9. }
  10.  
  11. queue[current++] = it;
  12. while(current != after_current){
  13. it = queue[after_current++];
  14. printf("%d\n",it->data);
  15. //TODO
  16. }
  17. }

果然,after_current也长啦一岁;

此时,current不高兴啦,他头也不回的往前走啦;

抱歉,请您再看看这一份图,此时current应该走到哪呢?

它可以选择先从左边走也可以先从右边走;

假设先从左边吧;

  1. void level_order(Tree *tree)
  2. {
  3. Node *it = tree->root;
  4. Node *queue[];
  5. int current = ;
  6. int after_current = ;
  7. if(it == NULL){
  8. return ;
  9. }
  10.  
  11. queue[current++] = it;
  12. while(current != after_current){
  13. it = queue[after_current++];
  14. printf("%d\n",it->data);
  15. if(it->link[] != NULL){
  16. queue[current++] = it->link[];
  17. }
  18. //TODO
  19. }
  20. }

好啦current开心的笑啦!它又比after_current大一岁啦;

while说小伙子不用骄傲,看看能不能继续往前走;

  1. while(current != after_current){
  2. it = queue[after_current++];
  3. printf("%d\n",it->data);
  4. if(it->link[] != NULL){
  5. queue[current++] = it->link[];
  6. }
  7.  
  8. if(it->link[] != NULL){
  9. queue[current++] = it->link[];
  10. }
  11. }

不知不觉又长了一岁;现在current已经是2岁的小伙子;

哈哈,queue也很高兴自己现在保存啦三个数字啦;

queue == {3,1,4}

现在after_current不高兴啦,为什么它那么快就长了两岁啦;

while拍拍它肩膀说你不要着急,你继续走走看;

  1. it = queue[after_current++];

哈哈,它很高兴,自己现在长了一岁啦;

it现在已经指向啦1号节点;

我们从图可以看到it的左右子树不为空,所以current将会连续又长两岁;

而将会是 queue == {3,1,4,0,2}

此时,after_current也没有哭闹啦,它很快又遇见啦;

  1. it = queue[after_current++];

而此时it是4,4没有左右孩子,所以我们的current不再增长;

current已经长大成人了不会再长啦;

而此时after_current还是慢慢成长;

直到current==after_current,while结束啦;

此时所有的数据已经遍历完成;

好啦!我们分析一下存在的问题,queue为什么指定为10呢,其实你只要大于等于4都能够运行。

但是,这是在我们已经知道节点只有那么一点,而不需要那么多的空间;

因此,这种硬编码在实际的应用会出现问题的,我们可能需要链表的方式来实现这个queue而不是固定数组;

好啦,这个先到这啦,下一个任务;

Problem

我们需要简单解释一下树的旋转;

这个其实很容易;

看看下面几幅图吧

好啦,这个是右旋一次的结果。更多东西下次再聊;

吃饭去啦;

Thanks:)

C语言实现二叉树-04版的更多相关文章

  1. C语言实现二叉树-02版

    ---恢复内容开始--- 昨天,提交完我们的二叉树项目后,今天早上项目经理早早给我打电话: 他说,小伙子干的不错.但是为什么你上面的insert是recusive的呢? 你难道不知道万一数据量大啦!那 ...

  2. C语言实现二叉树-03版

    我们亲爱的项目经理真是有创意,他说你给我写得二叉树挺好的: 功能还算可以:插入节点,能够删除节点: 可是有时候我们只是需要查找树的某个节点是否存在: 所以我希望你能够给我一个find功能: 还有就是, ...

  3. C语言实现二叉树-01版

    故事是这样开始的,项目经理有一天终于还是拍拍我肩膀说: 无论你的链表写得多么的好,无论是多么的灵活,我也得费老半天才查找到想要的数据: 这让我的工作非常苦恼,听说有一种叫做二叉树的数据结构,你看能不能 ...

  4. 数据结构与抽象 Java语言描述 第4版 pdf (内含标签)

    数据结构与抽象 Java语言描述 第4版 目录 前言引言组织数据序言设计类P.1封装P.2说明方法P.2.1注释P.2.2前置条件和后置条件P.2.3断言P.3Java接口P.3.1写一个接口P.3. ...

  5. C语言实现二叉树-利用二叉树统计单词数目

    昨天刚参加了腾讯2015年在线模拟考: 四道大题的第一题就是单词统计程序的设计思想: 为了记住这一天,我打算今天通过代码实现一下: 我将用到的核心数据结构是二叉树: (要是想了解简单二叉树的实现,可以 ...

  6. 《C++程序设计语言(英文第四版)》【PDF】下载

    <C++程序设计语言(英文第四版)>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230382177 内容简介 本书是C++领域经典的参 ...

  7. Rust语言之HelloWorld Web版

    Rust语言之HelloWorld Web版 下面这篇文章值得仔细研读: http://arthurtw.github.io/2014/12/21/rust-anti-sloppy-programmi ...

  8. R语言实战(第二版)-part 1笔记

    说明: 1.本笔记对<R语言实战>一书有选择性的进行记录,仅用于个人的查漏补缺 2.将完全掌握的以及无实战需求的知识点略去 3.代码直接在Rsudio中运行学习 R语言实战(第二版) pa ...

  9. C语言实现单链表-04版

    前面的版本似乎没能让项目经理满意,他还希望这个链表有更多的功能: 我们接下来要解决几个比较简单的功能: Problem 1,更加友好的显示数据: 2,能够通过名字删除节点: Solution 首先我们 ...

随机推荐

  1. Spring —— 三种配置数据源的方式:spring内置、c3p0、dbcp

    01.Spring内置数据源配置Class:DriverManagerDataSource全限定名:org.springframework.jdbc.datasource.DriverManagerD ...

  2. ueditor 发布到服务器提示“后端配置项没有正常加载,上传插件不能正常使用!”

    原来是发布后缺少文件,直接从本地的复制过去就好了.

  3. stm32 MDK5软件仿真之查看io口输出

    软件MDK5 stm32的pack     打开MDK,添加工程 一.首先找到Project的Options选项,里面的Debug选为Use Simulator,也就是选择软件仿真. 然后再Logic ...

  4. css 层的嵌套

    html <div class="menu"> <ul> <li><a class="li1" title=" ...

  5. HDOJ 1512 几乎模板的左偏树

    题目大意:有n个猴子.每个猴子有一个力量值,力量值越大表示这个猴子打架越厉害.如果2个猴子不认识,他们就会找他们认识的猴子中力量最大的出来单挑,单挑不论输赢,单挑的2个猴子力量值减半,这2拨猴子就都认 ...

  6. DateTools时间插件

    import java.text.DateFormat;import java.text.ParsePosition;import java.text.SimpleDateFormat;import ...

  7. C#中不同的线程对控件的更改

    .net 不允许跨线程个性其它线程创建的控件. 要想实现这个功能就需要用 InvokeRequired 检查是不是由该线程创建的控件,如果是直接操作,如果不是则 用Invoke 添加一个委托再加上参数 ...

  8. mormot orm rest注意事项

    mORMot创建ORM RestFul,当CreateMissingTables的时候报错,搜了n多资料没搞定,后来胡乱建了个表测试了一通搞定了. ORM对应的每个数据表都需要一个ID字段并设置主键, ...

  9. Appium客户端

    Appium版本:1.5.3 Xcode有两个版本:Xcode8.1   Xcode7.2.1 iOS10以下只能用Xcode7.2.1 iOS10及以上可以用Xcode8.1   1.Appium客 ...

  10. SQlserver 行转列

    列转行编程中很容易碰到,小弟在此总结下, 行转列暂时还没遇到,遇到再补充. 列转行:  , , , ,  以上都是以逗号分隔,分隔符可以自定义.