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

一、基本概念

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

性质:

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

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

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

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

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

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

二、存储结构

顺序存储:

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

[cpp] view plaincopy

  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. Node tree[LENGTH];
  9. int length;
  10. int root;
虽然在遍历速度上有一定的优势,但因所占空间比较大,是非主流二叉树。二叉树通常以链式存储。

链式存储:

[cpp] view plaincopy

  1. typedef char datatype;
  2. typedef struct BinNode{
  3. datatype data;
  4. struct BinNode* lchild;
  5. struct BinNode* rchild;
  6. }BinNode;
  7. typedef BinNode* bintree;          //bintree本身是个指向结点的指针

三、二叉树的遍历

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

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

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

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

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

前序遍历:abdefgc

中序遍历:debgfac

后序遍历:edgfbca

四、遍历的实现

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

[cpp] view plaincopy

  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 plaincopy

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

1、前序遍历

[cpp] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

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

遍历实现

[cpp] view plaincopy

  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 plaincopy

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

6、二叉树的查找

[cpp] view plaincopy

  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 plaincopy

  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 plaincopy

  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 plaincopy

  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;
  10. }
时间: 2024-07-30 10:17:04

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

算法实验-二叉树的创建和前序-中序-后序-层次 遍历

对于二叉树的创建我是利用先序遍历的序列进行创建 能够对于树节点的内容我定义为char型变量 '0'为空,即此处的节点不存在 头文件 Tree.h //链式二叉树的头文件 #pragma once #include<iostream> #include<queue> using namespace std; class BinaryTreeNode { public: char data; BinaryTreeNode *leftChild,*rightChild; BinaryTr

二叉树遍历算法——包含递归前、中、后序和层次,非递归前、中、后序和层次遍历共八种

首先,要感谢网上的参考资料. http://mengliao.blog.51cto.com/876134/1178079(作者:BlackAlpha) http://blog.csdn.net/fzh1900/article/details/14056735(作者:_云淡风轻) http://blog.csdn.net/stpeace/article/details/8138458(作者:stpeace) 二叉树是使用的比较广泛的一种数据结构,这里我写了二叉树的相关操作,包括初始化.新建.以及遍

二叉树的前序中序后序遍历相互求法

二叉树的前中后序遍历,他们的递归非递归.还有广度遍历,参见二叉树的前中后序遍历迭代&广度遍历和二叉树的前中后序遍历简单的递归 现在记录已知二叉树的前序中序后序遍历的两个,求另外一个.一般,这两个中一定有中序遍历. 1.已知前序和中序,求后序遍历: 前序:ABDECFG  中序:DBEAFCG 思路简单:前序的第一个节点就是根节点, 中序中找到根节点的位置,根节点之前是其左子树,之后是右子树   按此顺序,依次在左子树部分遍历,右子树部分遍历 C++ 代码: TreeNode *BinaryTre

二叉树的广义表创建及中序遍历、后序遍历、层次遍历的非递归算法(C语言)

广义表创建二叉树关于用广义表的形式表示二叉树的形式如下 ①广义表中的一个字母代表一个结点的数据信息.②每个根结点作为由子树构成的表的名字放在义表的前面.③每个结点的左子树与右子树之间用逗号分开.若结点只有右子树面无左子树,则该逗号不能省略.④在整个广义表的末尾加一个特殊符号(如“@”)作为结束标志. 下面先用自然语言描述算法如下.依次从广义表中取得-个元素,并对取得的元素做如下相应的处理. ①若当前取得的元素为字母,则按如下规则建立一个新的(链)结点.a)若该结点为二叉树的根结点,则将该结点的地

已知二叉树前、中序遍历,求后序 / 已知二叉树中、后序遍历,求前序

void solve(int start,int end,int root) { // 前序和中序 -> 后序 // 每次调用solve()函数,传入pre-order的start,end,root if (start > end) // 递归边界 return; int i = start; while (i < end && in.at(i) != pre.at(root)) // 找到左右子树的分割点 i++; solve(start, i - 1, root +

日常学习随笔-用链表的形式实现普通二叉树的新增、查找、遍历(前、中、后序)等基础功能(侧重源码+说明)

一.二叉树 1.二叉树的概念 二叉树是每个节点最多有两个子树的树结构.通常子树被称作"左子树"(left subtree)和"右子树"(right subtree),其次序不能任意颠倒. 2.性质 (1)若二叉树的层次从0开始,则在二叉树的第i层至多有2^i个结点(i>=0): (2)高度为k的二叉树最多有2^(k+1) - 1个结点(k>=-1). (空树的高度为-1): (3)对任何一棵二叉树,如果其叶子结点(度为0)数为m, 度为2的结点数为n,

二叉树的遍历方法之层序-先序-中序-后序遍历的简单讲解和代码示例

二叉树的基础性质及二叉树的建立参见前面两篇博文: http://blog.csdn.net/why850901938/article/details/51052936 http://blog.csdn.net/why850901938/article/details/51052156 首先为了讲解方便,我建立了如图所示的二叉树: 取名为:树A 1.何为层序遍历? 层序遍历就是按照二叉树的层次由上到下的进行遍历,每一层要求访问的顺序为从左到右: 以树A为例,层序遍历得到的结果为: 5 2 6 1

经典白话算法之二叉树中序前序序列(或后序)求解树

这种题一般有二种形式,共同点是都已知中序序列.如果没有中序序列,是无法唯一确定一棵树的. <1>已知二叉树的前序序列和中序序列,求解树. 1.确定树的根节点.树根是当前树中所有元素在前序遍历中最先出现的元素. 2.求解树的子树.找出根节点在中序遍历中的位置,根左边的所有元素就是左子树,根右边的所有元素就是右子树.若根节点左边或右边为空,则该方向子树为空:若根节点 边和右边都为空,则根节点已经为叶子节点. 3.递归求解树.将左子树和右子树分别看成一棵二叉树,重复1.2.3步,直到所有的节点完成定

算法进阶面试题03——构造数组的MaxTree、最大子矩阵的大小、2017京东环形烽火台问题、介绍Morris遍历并实现前序/中序/后序

接着第二课的内容和带点第三课的内容. (回顾)准备一个栈,从大到小排列,具体参考上一课.... 构造数组的MaxTree [题目] 定义二叉树如下: public class Node{ public int value; public Node left; public Node right; public Node(int data){ this.value=data; } } 一个数组的MaxTree定义如下: ◆ 数组必须没有重复元素 ◆ MaxTree是一颗二叉树,数组的每一个值对应一

二叉树的先序-中序-后序遍历(一)-循环----绝对白痴好记的方法

接着上一篇 二叉树的先序-中序-后序遍历(一)-递归 的讲,这篇该循环遍历了. 之前一直没有找到好的方法来循环遍历树,以前我老认为有些递归的能做的东西很难换成循环实现. 后来看了一些别人写的代码,然后又问了朋友,才发现...哦,原来这样的啊,我可以自己弄个栈来维护一下. 想到了可以弄个栈以后,至少在我认为,把递归转成循环已经是可行的了,至于怎么实现,这几天在想(因为太笨,看人家的代码总看一半就不想看),今天找到了一个好用的方法,在这里分享一下. 算法的核心是:你看二叉树的时候心里怎么想的,程序就