您现在的位置: 365建站网 > 365学习 > 二叉树遍历分析(前序、中序、后序、层次遍历及非递归)

二叉树遍历分析(前序、中序、后序、层次遍历及非递归)

文章来源:365jz.com     点击数:482    更新时间:2017-11-28 10:16   参与评论
所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。 遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。
一、基本概念
每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒。
性质:
1、非空二叉树的第n层上至多有2^(n-1)个元素。
2、深度为h的二叉树至多有2^h-1个结点。
满二叉树:所有终端都在同一层次,且非终端结点的度数为2。
在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1。
完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置。
对于完全二叉树,设一个结点为i则其父节点为i/2,2i为左子节点,2i+1为右子节点。

 简单二叉树遍历,可分为:先序,中序,后序。

  在此分别总结先序,中序,后序的结点输出顺序。

  先序: 1.访问根结点

    2.访问左子树

    3.访问右子树

 

先序较简单,不予以即系解释。

  中序:1.访问左子树

     2.访问根结点

       3.访问右子树

    原则:访问左子树。【先访问左子树中的左子树,再访问左子树中的右子树。】直到访问到叶子结点后输出。

       输出根。

         访问右子树。【先访问右子树中的左子树,再访问右子树中的右子树。】直到访问到叶子结点后输出。

具体步骤如下:

  A作为根。从A开始,先访问A的左子树。即

在看B的左子树,D。则输出D。B无左子树。访问完B的左子树。然后访问B。输出B。再看B的右子树。F有左子树E,则输出E。返回输出F。A的左子树全部输出完,再返回A,输出A。

  

同理,看A的右子树。。输出顺序为G,H,C,I。

 

所以,中序遍历输出的结果为:(D B E F)A(G H C I).

 

  后序:1.访问左子树

     2.访问右子树

          3.访问根

     原则:访问左子树。【先访问左子树中的左子树,再访问左子树中的右子树】。直到访问到叶子结点后输出。

        访问右子树。【先访问右子树中的左子树,再访问右子树中的右子树】。直到访问到叶子结点后输出。

        再返回访问根,并输出。

具体步骤:

  先访问A的左子树。再访问左子树中的左子树。【即:A的左子树为B,再访问B的左子树D。D没有左右子树,输出D。】,然后访问左子树中的右子树。【即:访问B的右子树F,F还有左子树,再访问F的左子树E,E没有左右子树。输出E。再输出F,再输出B。】。

  然后访问A的右子树。再访问右子树中的左子树。【即:A的右子树为C,再访问C的左子树G。G还有右子树H,输出H。再输出G,再输出G】,然后访问右子树中的右子树。【即:访问C的右子树I,I没有左右子树,输出I。在输出C。再输出A。】。

  所以,后序遍历输出结果为:(D E F B)(H G I C)A

二、存储结构
顺序存储:
将数据结构存在一块固定的数组中。

#define LENGTH 100 
typedef char datatype; 
typedef struct node{ 
    datatype data; 
    int lchild,rchild; 
    int parent; 
}Node; 
 
Node tree[LENGTH]; 
int length; 
int root; 

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

typedef char datatype; 
 
typedef struct BinNode{ 
    datatype data; 
    struct BinNode* lchild; 
    struct BinNode* rchild; 
}BinNode; 
 
typedef BinNode* bintree;          //bintree本身是个指向结点的指针 


三、二叉树的遍历
遍历即将树的所有结点访问且仅访问一次。按照根节点位置的不同分为前序遍历,中序遍历,后序遍历。
前序遍历:根节点->左子树->右子树
中序遍历:左子树->根节点->右子树
后序遍历:左子树->右子树->根节点
例如:求下面树的三种遍历

前序遍历:abdefgc
中序遍历:debgfac
后序遍历:edgfbca
四、遍历的实现
递归实现(以前序遍历为例,其他的只是输出的位置稍有不同)

void preorder(bintree t){ 
    if(t){ 
        printf("%c ",t->data); 
        preorder(t->lchild); 
        preorder(t->rchild); 
    } 


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

#define SIZE 100 
typedef struct seqstack{ 
    bintree data[SIZE]; 
    int tag[SIZE];   //为后续遍历准备的 
    int top;     //top为数组的下标 
}seqstack; 
 
void push(seqstack *s,bintree t){ 
 
    if(s->top == SIZE){ 
        printf("the stack is full\n"); 
    }else{ 
        s->top++; 
        s->data[s->top]=t; 
    } 

 
bintree pop(seqstack *s){ 
    if(s->top == -1){ 
        return NULL; 
    }else{ 
        s->top--; 
        return s->data[s->top+1]; 
    } 

1、前序遍历

void preorder_dev(bintree t){ 
    seqstack s; 
    s.top = -1;     //因为top在这里表示了数组中的位置,所以空为-1 
    if(!t){ 
        printf("the tree is empty\n"); 
    }else{ 
        while(t || s.stop != -1){ 
            while(t){    //只要结点不为空就应该入栈保存,与其左右结点无关     
                  printf("%c ",t->data); 
                push(&s,t); 
                t= t->lchild; 
            } 
            t=pop(&s); 
            t=t->rchild; 
        } 
    } 


2、中序遍历
 

void midorder(bintree t){ 
    seqstack s; 
    s.top = -1; 
    if(!t){ 
        printf("the tree is empty!\n"); 
    }else{ 
        while(t ||s.top != -1){ 
            while(t){ 
                push(&s,t); 
                t= t->lchild; 
            } 
            t=pop(&s); 
            printf("%c ",t->data); 
            t=t->rchild; 
        } 
    } 


3、后序遍历
因为后序遍历最后还要要访问根结点一次,所以要访问根结点两次。采取夹标志位的方法解决这个问题。
这段代码非常纠结,对自己有信心的朋友可以尝试独立写一下。反正我是写了很长时间。逻辑不难,我画了一张逻辑图:

代码:
 

void postorder_dev(bintree t){ 
    seqstack s; 
    s.top = -1; 
    if(!t){ 
        printf("the tree is empty!\n"); 
    }else{ 
        while(t || s.top != -1){    //栈空了的同时t也为空。 
            while(t){ 
                push(&s,t); 
                s.tag[s.top] = 0;   //设置访问标记,0为第一次访问,1为第二次访问 
                t= t->lchild; 
            } 
            if(s.tag[s.top] == 0){  //第一次访问时,转向同层右结点 
                t= s.data[s.top];   //左走到底时t是为空的,必须有这步! 
                s.tag[s.top]=1;      
                t=t->rchild; 
            }else { 
                while (s.tag[s.top] == 1){ //找到栈中下一个第一次访问的结点,退出循环时并没有pop所以为其左子结点 
                    t = pop(&s); 
                    printf("%c ",t->data); 
                } 
                t = NULL; //必须将t置空。跳过向左走,直接向右走 
            } 
        } 
    } 


4、层次遍历:即每一层从左向右输出
元素需要储存有先进先出的特性,所以选用队列存储。
队列的定义:

#define MAX 1000 
 
typedef struct seqqueue{ 
    bintree data[MAX]; 
    int front; 
    int rear; 
}seqqueue; 
 
 
void enter(seqqueue *q,bintree t){ 
    if(q->rear == MAX){ 
        printf("the queue is full!\n"); 
    }else{ 
        q->data[q->rear] = t; 
        q->rear++; 
    } 

 
bintree del(seqqueue *q){ 
    if(q->front == q->rear){ 
        return NULL; 
    }else{ 
        q->front++; 
        return q->data[q->front-1]; 
    } 


遍历实现

void level_tree(bintree t){ 
    seqqueue q; 
    bintree temp; 
    q.front = q.rear = 0; 
    if(!t){ 
        printf("the tree is empty\n"); 
        return ; 
    } 
    enter(&q,t); 
    while(q.front != q.rear){ 
        t=del(&q); 
        printf("%c ",t->data); 
        if(t->lchild){ 
            enter(&q,t->lchild); 
        } 
        if(t->rchild){ 
            enter(&q,t->rchild); 
        } 
    } 



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

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


6、二叉树的查找

bintree search_tree(bintree t,datatype x){ 
    if(!t){ 
        return NULL; 
    } 
    if(t->data == x){ 
        return t; 
    }else{ 
        if(!search_tree(t->lchild,x)){ 
            return search_tree(t->rchild,x); 
        } 
        return t; 
    } 


7、统计结点个数

int count_tree(bintree t){ 
    if(t){ 
        return (count_tree(t->lchild)+count_tree(t->rchild)+1); 
    } 
    return 0; 


8、比较两个树是否相同

int is_equal(bintree t1,bintree t2){ 
    if(!t1 && !t2){      //都为空就相等 
        return 1; 
    } 
    if(t1 && t2 && t1->data == t2->data){      //有一个为空或数据不同就不判断了 
        if(is_equal(t1->lchild,t2->lchild)) 
            if(is_equal(t1->rchild,t2->rchild)){ 
                return 1; 
            } 
    } 
    return 0; 


9、求二叉树的深度

int hight_tree(bintree t){ 
    int h,left,right; 
    if(!t){ 
        return 0; 
    } 
    left = hight_tree(t->lchild); 
    right = hight_tree(t->rchild); 
    h = (left>right?left:right)+1; 
    return h; 
}  



如对本文有疑问,请提交到交流论坛,广大热心网友会为你解答!! 点击进入论坛


发表评论 (482人查看0条评论)
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
用户名: 验证码: 点击我更换图片
最新评论
------分隔线----------------------------