数据结构 ——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现

2023-11-13

一、基本概念

每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。

性质:

1、非空二叉树的第n层上至多有2^(n-1)个元素。

2、深度为h的二叉树至多有2^h-1个结点。

满二叉树:所有终端都在同一层次,且非终端结点的度数为2。

在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。

完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置。

对于完全二叉树,设一个结点为i则其父节点为i/2,2i为左子节点,2i+1为右子节点。

二、存储结构

顺序存储:

数据结构存在一块固定的数组中。

[cpp]  view plain  copy  print ?
  1. #define LENGTH 100  
  2. typedef char datatype;  
  3. typedef struct node{  
  4.     datatype data;  
  5.     int lchild,rchild;  
  6.     int parent;  
  7. }Node;  
  8.   
  9. Node tree[LENGTH];  
  10. int length;  
  11. int root;  

   虽然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。

链式存储:

[cpp]  view plain  copy  print ?
  1. typedef char datatype;  
  2.   
  3. typedef struct BinNode{  
  4.     datatype data;  
  5.     struct BinNode* lchild;  
  6.     struct BinNode* rchild;  
  7. }BinNode;  
  8.   
  9. typedef BinNode* bintree;          //bintree本身是个指向结点的指针  


 三、二叉树的遍历

遍历即将树的所有结点访问且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。

前序遍历:根节点->左子树->右子树

中序遍历:左子树->根节点->右子树

后序遍历:左子树->右子树->根节点

例如:求下面树的三种遍历

 

前序遍历:abdefgc

中序遍历:debgfac

后序遍历:edgfbca

四、遍历的实现

递归实现(以前序遍历为例,其他的只是输出的位置稍有不同)

[cpp]  view plain  copy  print ?
  1. void preorder(bintree t){  
  2.     if(t){  
  3.         printf("%c ",t->data);  
  4.         preorder(t->lchild);  
  5.         preorder(t->rchild);  
  6.     }  
  7. }  


非递归的实现

因为当遍历过根节点之后还要回来,所以必须将其存起来。考虑到后进先出的特点,选用栈存储。数量确定,以顺序栈存储。

[cpp]  view plain  copy  print ?
  1. #define SIZE 100  
  2. typedef struct seqstack{  
  3.     bintree data[SIZE];  
  4.     int tag[SIZE];   //为后续遍历准备的  
  5.     int top;     //top为数组的下标  
  6. }seqstack;  
  7.   
  8. void push(seqstack *s,bintree t){  
  9.   
  10.     if(s->top == SIZE){  
  11.         printf("the stack is full\n");  
  12.     }else{  
  13.         s->top++;  
  14.         s->data[s->top]=t;  
  15.     }  
  16. }  
  17.   
  18. bintree pop(seqstack *s){  
  19.     if(s->top == -1){  
  20.         return NULL;  
  21.     }else{  
  22.         s->top--;  
  23.         return s->data[s->top+1];  
  24.     }  
  25. }  

1、前序遍历 

[cpp]  view plain  copy  print ?
  1. void preorder_dev(bintree t){  
  2.     seqstack s;  
  3.     s.top = -1;     //因为top在这里表示了数组中的位置,所以空为-1  
  4.     if(!t){  
  5.         printf("the tree is empty\n");  
  6.     }else{  
  7.         while(t || s.stop != -1){  
  8.             while(t){    //只要结点不为空就应该入栈保存,与其左右结点无关      
  9.                   printf("%c ",t->data);  
  10.                 push(&s,t);  
  11.                 t= t->lchild;  
  12.             }  
  13.             t=pop(&s);  
  14.             t=t->rchild;  
  15.         }  
  16.     }  
  17. }  


 2、中序遍历

 

[cpp]  view plain  copy  print ?
  1. void midorder(bintree t){  
  2.     seqstack s;  
  3.     s.top = -1;  
  4.     if(!t){  
  5.         printf("the tree is empty!\n");  
  6.     }else{  
  7.         while(t ||s.top != -1){  
  8.             while(t){  
  9.                 push(&s,t);  
  10.                 t= t->lchild;  
  11.             }  
  12.             t=pop(&s);  
  13.             printf("%c ",t->data);  
  14.             t=t->rchild;  
  15.         }  
  16.     }  
  17. }  

 

3、后序遍历

因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次。采取夹标志位的方法解决这个问题。

这段代码非常纠结,对自己有信心的朋友可以尝试独立写一下。反正我是写了很长时间。逻辑不难,我画了一张逻辑图:

 代码:

 

[cpp]  view plain  copy  print ?
  1. void postorder_dev(bintree t){  
  2.     seqstack s;  
  3.     s.top = -1;  
  4.     if(!t){  
  5.         printf("the tree is empty!\n");  
  6.     }else{  
  7.         while(t || s.top != -1){    //栈空了的同时t也为空。  
  8.             while(t){  
  9.                 push(&s,t);  
  10.                 s.tag[s.top] = 0;   //设置访问标记,0为第一次访问,1为第二次访问  
  11.                 t= t->lchild;  
  12.             }  
  13.             if(s.tag[s.top] == 0){  //第一次访问时,转向同层右结点  
  14.                 t= s.data[s.top];   //左走到底时t是为空的,必须有这步!  
  15.                 s.tag[s.top]=1;       
  16.                 t=t->rchild;  
  17.             }else {  
  18.                 while (s.tag[s.top] == 1){ //找到栈中下一个第一次访问的结点,退出循环时并没有pop所以为其左子结点  
  19.                     t = pop(&s);  
  20.                     printf("%c ",t->data);  
  21.                 }  
  22.                 t = NULL; //必须将t置空。跳过向左走,直接向右走  
  23.             }  
  24.         }  
  25.     }  
  26. }  


 4、层次遍历:即每一层从左向右输出

元素需要储存有先进先出的特性,所以选用队列存储。

队列的定义:

[cpp]  view plain  copy  print ?
  1. #define MAX 1000  
  2.   
  3. typedef struct seqqueue{  
  4.     bintree data[MAX];  
  5.     int front;  
  6.     int rear;  
  7. }seqqueue;  
  8.   
  9.   
  10. void enter(seqqueue *q,bintree t){  
  11.     if(q->rear == MAX){  
  12.         printf("the queue is full!\n");  
  13.     }else{  
  14.         q->data[q->rear] = t;  
  15.         q->rear++;  
  16.     }  
  17. }  
  18.   
  19. bintree del(seqqueue *q){  
  20.     if(q->front == q->rear){  
  21.         return NULL;  
  22.     }else{  
  23.         q->front++;  
  24.         return q->data[q->front-1];  
  25.     }  
  26. }  


遍历实现 

[cpp]  view plain  copy  print ?
  1. void level_tree(bintree t){  
  2.     seqqueue q;  
  3.     bintree temp;  
  4.     q.front = q.rear = 0;  
  5.     if(!t){  
  6.         printf("the tree is empty\n");  
  7.         return ;  
  8.     }  
  9.     enter(&q,t);  
  10.     while(q.front != q.rear){  
  11.         t=del(&q);  
  12.         printf("%c ",t->data);  
  13.         if(t->lchild){  
  14.             enter(&q,t->lchild);  
  15.         }  
  16.         if(t->rchild){  
  17.             enter(&q,t->rchild);  
  18.         }  
  19.     }  
  20. }  


 

5、利用前序遍历的结果生成二叉树

[cpp]  view plain  copy  print ?
  1. //递归调用,不存点,想的时候只关注于一个点,因为还会回来的,不要跟踪程序运行,否则容易多加循环  
  2.   
  3. void createtree(bintree *t){        
  4.     datatype c;  
  5.     if((c=getchar()) == '#')  
  6.         *t = NULL;  
  7.     else{  
  8.         *t = (bintree)malloc(sizeof(BinNode));  
  9.         (*t)->data = c;  
  10.         createtree(&(*t)->lchild);  
  11.         createtree(&(*t)->rchild);  
  12.     }  
  13. }  


6、二叉树的查找

[cpp]  view plain  copy  print ?
  1. bintree search_tree(bintree t,datatype x){  
  2.     if(!t){  
  3.         return NULL;  
  4.     }  
  5.     if(t->data == x){  
  6.         return t;  
  7.     }else{  
  8.         if(!search_tree(t->lchild,x)){  
  9.             return search_tree(t->rchild,x);  
  10.         }  
  11.         return t;  
  12.     }  
  13. }  


7、统计结点个数

[cpp]  view plain  copy  print ?
  1. int count_tree(bintree t){  
  2.     if(t){  
  3.         return (count_tree(t->lchild)+count_tree(t->rchild)+1);  
  4.     }  
  5.     return 0;  
  6. }  


8、比较两个树是否相同

[cpp]  view plain  copy  print ?
  1. int is_equal(bintree t1,bintree t2){  
  2.     if(!t1 && !t2){      //都为空就相等  
  3.         return 1;  
  4.     }  
  5.     if(t1 && t2 && t1->data == t2->data){      //有一个为空或数据不同就不判断了  
  6.         if(is_equal(t1->lchild,t2->lchild))  
  7.             if(is_equal(t1->rchild,t2->rchild)){  
  8.                 return 1;  
  9.             }  
  10.     }  
  11.     return 0;  
  12. }  


9、求二叉树的深度

[cpp]  view plain  copy  print ?
  1. int hight_tree(bintree t){  
  2.     int h,left,right;  
  3.     if(!t){  
  4.         return 0;  
  5.     }  
  6.     left = hight_tree(t->lchild);  
  7.     right = hight_tree(t->rchild);  
  8.     h = (left>right?left:right)+1;  
  9.     return h;  

  1. }  
转自:http://blog.csdn.net/fansongy/article/details/6798278

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

数据结构 ——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现 的相关文章

随机推荐

  • kubernetes pv回收策略

    本文最近更新于2021 9 11 kubernetes pv回收策略 当用户不再使用其存储卷时 他们可以从 API 中将 PVC 对象删除 从而允许该资源被回收再利用 PersistentVolume 对象的回收策略告诉集群 当其被从申领中
  • 冈萨雷斯《数字图像处理》学习总结及感悟:第一章 绪论 百闻不如一见

    前往老猿Python博文目录 https blog csdn net LaoYuanPython 一 引言 好几月前开始自学OpenCV Python 但老猿以前没接触过图像基础知识 数学知识基本上也都忘光了 因此在自学OpenCV Pyt
  • Python处理txt数据实例

    现在有一个具体的案例是这样的 CST电磁仿真软件得到一些txt数据在origin data文件夹中 需要其中的一些数据来通过origin软件绘制曲线分析一些问题 而且需要里面的所有数据曲线显示在同一个图形中 如果通过手动将txt数据一一复制
  • LeetCode第55题解析

    给定一个非负整数数组 你最初位于数组的第一个位置 数组中的每个元素代表你在该位置可以跳跃的最大长度 判断你是否能够到达最后一个位置 示例 1 输入 2 3 1 1 4 输出 true 解释 我们可以先跳 1 步 从位置 0 到达 位置 1
  • js的闭包的理解

    js的变量的作用域分为全局变量和局部变量 函数内部的变量称为局部变量 在函数的内部可以访问到全局变量 但是函数外部无法访问函数内部的变量 闭包可以解决无法访问函数内部的变量的问题 且可以隐藏这个变量 不被外部直接访问 闭包 函数内部的子函数
  • JavaScript 搜索引擎 lunr.js

    lunr js 实现了在网页上的搜索引擎 类似 Solr 示例代码 view source print 01 定义索引 02 var idx lunr function 03 this field title boost 10 04 thi
  • flask需求文件requirements.txt的创建和使用

    flask需求文件requirements txt的创建及使用 简介 flask项目中包含一个requirements txt 文件 用于记录所有依赖包及其精确的版本号用以新环境部署 创建 生成需求文件 在命令行输入 pip freeze
  • 服务器一直被攻击怎么办?

    有很多人问说 网站一直被攻击 什么被挂马 什么被黑 每天一早打开网站 总是会出现各种各样的问题 这着实让站长们揪心 从修改服务器管理账号开始 到修改远程端口 什么措施都做了 还是会被攻击挂马 服务器一直被攻击时 要怎么做 1 切断网络 对服
  • 秋招-算法-动态规划篇

    秋招 算法 动态规划篇 只求秋招笔试能过 所以本文更多是怎么使用模板来解动态规划题 能过就好 对时间和空间的考虑不会太多 介绍 动态规划通过组合子问题的解得到原问题的解 适合动态规划解决的问题具有重叠子问题和最优子结构两大特征 通常使用空间
  • caffe 学习率设置问题

    solver算是caffe的核心的核心 它协调着整个模型的运作 caffe程序运行必带的一个参数就是solver配置文件 运行代码一般为 caffe train solver slover prototxt 在Deep Learning中
  • Unity和VS2019下载及配置流程

    https www jianshu com p 6fe2dc4de4c3
  • 可编程手机蓝牙App控制直流电机速度

    我们将通过 Android 应用程序 app 介绍直流电机速度控制 对于该项目 该应用程序安装在智能手机设备中 该设备使用蓝牙向控制直流电机速度的电路发送命令 此 Android 应用程序具有引人注目且易于使用的图形用户界面 GUI 我们将
  • IBM区块链负责人Jesse Lund的“你问我答”

    点击上方 蓝色字 可关注我们 编辑 铅笔盒 IBM区块链部门负责人Jesse Lund抽空参与了 你问我答 活动 Ask Me Anything AMA 回答了关于加密货币及IBM与Stellar合作关系的一些问题 具体内容如下 IBM现在
  • ue4 DerivedDataCache报错

    启动ue4时报错 报错堆栈如下 从堆栈可以看出是DerivedDataCache报错 从堆栈的CachedDataProbablyExists函数更可以看出是在判断cache是否存在 故而想到删除项目目录下DerivedDataCache中
  • RPC框架的异步处理

    RPC异步调用 以tars rpc框架为示例说明 引入工作线程池和io收发线程池将工作线程和io收发线程两者的同步关系解除 RPC中的上下文十分重要 因为请求包的发送 响应包的callback回调不在同一个工作线程中完成 需要一个conte
  • 计算机组长原理,期末项目考核-计算机组成原理-组长学号-组长姓名

    2012 2013 第一学期第一学期 计算机组成原理计算机组成原理 期末考核期末考核 职位姓名学号在项目中担任的工作成绩 组长 组员 组员 组员 一 项目一 项目 1 描述 描述 设 CPU 共有 16 根地址线 8 根数据线 R W 作读
  • 山洪灾害预警方案(山洪预警解决方案的组成)

    随着气候变化的不断加剧 山洪灾害在许多地区成为了极具威胁性的自然灾害之一 为了帮助地方政府和居民更好地预防和应对山洪灾害 我们设计了一套基于星创易联的SR600工业路由器和DTU200的山洪灾害预警方案 并成功在某地区进行了部署 案例背景
  • Transaction rolled back because it has been marked as rollback-only 解决办法

    今天早上高高兴兴上班 居然收到一大堆报警信息 仔细看了一下具体内容 都在提示这个错误 Transaction rolled back because it has been marked as rollback only 我一看就觉得奇怪了
  • Linux I/O编程 实验内容

    一 实验目的 练习用UNIX I O进行文件读写的编程方法 用UNIX I O函数管理文本信息 二进制数据 结构体数据 掌握UNIX I O的基本编程方法 练习测时函数使用 通过测量UNIX I O函数运行时间 建立UNIX I O API
  • 数据结构 ——二叉树 前序、中序、后序、层次遍历及非递归实现 查找、统计个数、比较、求深度的递归实现

    一 基本概念 每个结点最多有两棵子树 左子树和右子树 次序不可以颠倒 性质 1 非空二叉树的第n层上至多有2 n 1 个元素 2 深度为h的二叉树至多有2 h 1个结点 满二叉树 所有终端都在同一层次 且非终端结点的度数为2 在满二叉树中若