zoukankan      html  css  js  c++  java
  • 二叉树的遍历(递归、非递归)及应用(构造二叉树,输出叶子结点,输出树的高度...)

      二叉树遍历的核心问题,是二维结构的线性化(栈、队列)。无论先序遍历、中序遍历、后续遍历,遍历过程中经过结点的路线是一样的,只是访问各结点的时机不同而已,每个结点都有三次访问机会。  

      一、二叉树结点,C语言定义

    1 typedef struct TNode *Position;
    2 typedef Position BinTree; /* 二叉树类型 */
    3 struct TNode{ /* 树结点定义 */
    4     ElementType Data; /* 结点数据 */
    5     BinTree Left;     /* 指向左子树 */
    6     BinTree Right;    /* 指向右子树 */
    7 };

      二、递归遍历,C语言实现(其实递归也是使用堆栈实现的)

     1 /* 先序遍历 */
     2 void PreorderTraversal( BinTree BT )
     3 {
     4     if( BT ) {
     5         printf("%d ", BT->Data );
     6         PreorderTraversal( BT->Left );
     7         PreorderTraversal( BT->Right );
     8     }
     9 }
    10 
    11 /* 中序遍历 */
    12 void InorderTraversal( BinTree BT )
    13 {
    14     if( BT ) {
    15         InorderTraversal( BT->Left );
    16         /* 此处假设对BT结点的访问就是打印数据 */
    17         printf("%d ", BT->Data); /* 假设数据为整型 */
    18         InorderTraversal( BT->Right );
    19     }
    20 }
    21 
    22 /* 后序遍历 */
    23 void PostorderTraversal( BinTree BT )
    24 {
    25     if( BT ) {
    26         PostorderTraversal( BT->Left );
    27         PostorderTraversal( BT->Right );
    28         printf("%d ", BT->Data);
    29     }
    30 }

      三、非递归遍历

     1 /* 先序遍历 */
     2 void PreorderTraversal( BinTree BT )
     3 { 
     4     BinTree T = BT;
     5     Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
     6     while( T || !IsEmpty(S) )/* 树不空或栈不空 */
     7     {
     8         while(T)/*一直向左并将沿途结点压入堆栈*/
     9         { 
    10             Push(S,T);  /* 结点压栈(第一次遇到结点) */
    11             printf("%5d", T->Data); /*(访问)打印结点*/
    12             T = T->Left;/* 一直向左 */
    13         }
    14         if(!IsEmpty(S)) /* 栈不空 */
    15         {
    16             T = Pop(S);  /*结点弹出堆栈(第二次遇到结点)*/            
    17             T = T->Right;/*转向右子树*/
    18         }
    19     }
    20 }
    21 
    22 /* 中序遍历    
    23     非递归遍历算法:
    24     1.遇到一个结点,就把它压栈,并去遍历它的左子树;
    25     2.当左子树遍历结束后,从栈顶弹出这个结点并访问它;
    26     3.然后按其右指针再去中序遍历该结点的右子树。
    27 */
    28 void InOrderTraversal( BinTree BT )
    29 { 
    30     BinTree T = BT;
    31     Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
    32     while( T || !IsEmpty(S) )/* 树不空或栈不空 */
    33     {
    34         while(T)/*一直向左并将沿途结点压入堆栈*/
    35         { 
    36             Push(S,T);  /* 结点压栈(第一次遇到结点) */
    37             T = T->Left;/* 一直向左 */
    38         }
    39         if(!IsEmpty(S)) /* 栈不空 */
    40         {
    41             T = Pop(S); /*结点弹出堆栈(第二次遇到结点)*/
    42             printf("%5d", T->Data); /*(访问)打印结点*/
    43             T = T->Right;           /*转向右子树*/
    44         }
    45     }
    46 }
    47 
    48 /* 后序遍历 */
    49 void PostorderTraversal( BinTree BT )
    50 {
    51     BinTree T = BT, P = NULL; /* P上一个已被访问的结点 */
    52     Stack S = CreatStack( MaxSize ); /*创建并初始化堆栈S*/
    53 
    54     while( T || !IsEmpty(S) )/* 树不空或栈不空 */
    55     {
    56         while(T)/*一直向左并将沿途结点压入堆栈*/
    57         {
    58             Push(S,T);  /* 结点压栈(第一次遇到结点) */            
    59             T = T->Left;/* 一直向左 */
    60         }    
    61             
    62         if( !IsEmpty(S))
    63         {    
    64             T = Pop(S); /* 先弹出结点 */
    65             if(T->Right == P || T->Right == NULL)
    66             {   /* 右孩子已访问或右孩子不存在, 弹出结点 */                        
    67                 printf("%5d", T->Data);  /* 访问结点 */
    68                 P = T; /* P指向被访问结点 */
    69                 T = NULL; /* 树置空(该树的左右根结点已经访问) */
    70             }
    71             else
    72             {    
    73                 Push(S,T); /* 否则,不应该弹出结点, 结点再次入栈 */
    74                 T = T->Right;/* 继续遍历右子树 */                
    75             }
    76         }
    77     }        
    78 }
    79 
    80 /* 层次遍历 */
    81 void LevelorderTraversal ( BinTree BT )
    82 { 
    83     Queue Q; 
    84     BinTree T;
    85  
    86     if ( !BT ) return; /* 若是空树则直接返回 */
    87      
    88     Q = CreatQueue(); /* 创建空队列Q */
    89     AddQ( Q, BT );
    90     while ( !IsEmpty(Q) ) 
    91     {
    92         T = DeleteQ( Q );
    93         printf("%d ", T->Data); /* 访问取出队列的结点 */
    94         if ( T->Left )   AddQ( Q, T->Left );
    95         if ( T->Right )  AddQ( Q, T->Right );
    96     }
    97 }

      四、遍历二叉树的应用

      1、二叉树的构造,用先序序列或后序序列和中序序列可以构造唯一一棵二叉树,用扩充先序序列或扩充的后序序列可以构造唯一一棵二叉树不同的二叉树可能具有相同的先序中序后序序列,因此,仅一个先、中、后序序列不能构造唯一一棵二叉树;用扩充中序序列不能构造唯一一棵二叉树)

      (1)、后序序列+中序序列,构造一棵二叉树

    /* 
        a数组存储树的后序序列, b数组存储中序序列 
        int a[] = {2,3,1,5,7,6,4} ;  //a数组
        int b[] = {1,2,3,4,5,6,7} ;  //b数组
    */
    
    BinTree  BuildTree(int a[],int b[],int i,int j,int s,int e) 
    {   /* i,j树的后序序列的起止, s,e树的中序序列的起止 */
        int k; 
        BinTree p;
        if( i > j )    return NULL;//递归终止
        
        p = NewNode(a[j]); /* 后序的根a[j]创建结点 */
        //p = NewNode(a[i]); /* 先序的根a[i]创建结点 */
        
        k = s;  /* 寻找树根位置 */
        while( ( k <= e ) && ( b[k] != a[j] ) )    k++; 
        
        if( k > e )    exit(ERROR); 
        
        p->Left  = BuildTree(a, b, i, i+(k-s)-1, s, k-1); /* 左子树 */   
        p->Right = BuildTree(a, b, i+(k-s), j-1, k+1, e); /* 右子树 */
        
        /* 
        //a为先序序列, b为中序序列
        p->Left  = BuildTree(a, b, i+1, i+(k-s), s, k-1);    
        p->Right = BuildTree(a, b, i+(k-s)+1, j, k+1, e);  
        */
        return p;
    }

      (2)、扩充的先序序列构造二叉树,先序遍历二叉树时,如果当前要访问的结点不空,就记下这个结点值;如果为空,就记下“空”字所得 到的遍历序列,比如:A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0,

     1 #include <stdio.h>
     2 #include <malloc.h>
     3 
     4 /* 二叉树结点结构体 */
     5 typedef char ElementType;
     6 typedef struct TNode* BinTree;
     7 struct TNode{
     8     ElementType Data;
     9     BinTree Left;
    10     BinTree Right;
    11 };
    12 
    13 /* 创建结点 */
    14 BinTree NewNode(int x)
    15 {
    16     BinTree t = (BinTree)malloc(sizeof(struct TNode));
    17     t->Data = x;
    18     t->Left = t->Right = NULL;
    19     return t;
    20 }
    21 
    22 /* 扩充的先序序列构造二叉树 */
    23 BinTree PreCreate() 
    24 {  
    25     char ch;
    26     scanf(" %c,",&ch);
    27     /* 输入'0'符号即 NULL */
    28     if(ch=='0')
    29         return NULL;  
    30     /* 根结点 */
    31     BinTree t = NewNode(ch);
    32     /* 递归左右子树结点 */
    33     t->Left  = PreCreate();     
    34     t->Right = PreCreate();     
    35     return t; 
    36 } 
    37 
    38 /* 先序遍历 */
    39 void PreorderTraversal( BinTree BT ){
    40     if(BT){
    41         printf("%5c",BT->Data);
    42         PreorderTraversal(BT->Left);
    43         PreorderTraversal(BT->Right);
    44     }
    45 } 
    46 /* A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0,  */
    47 int main()
    48 {
    49     BinTree BT = PreCreate();
    50     PreorderTraversal(BT);    
    51     return 0;
    52 }

      2、输出二叉树中的叶子结点,在二叉树的遍历算法中增加检测结点的左右子树是否都为空

    void PreOrderPrintLeaves( BinTree BT )
    {
        if( BT ) {
            if ( !BT-Left && !BT->Right )
                printf("%d", BT->Data );
            PreOrderPrintLeaves ( BT->Left );
            PreOrderPrintLeaves ( BT->Right );        
        }
    }
    int leaf(BinTree root)
    {
        int LeafCount;
        if(root == NULL)
            LeafCount = 0;
        else if((root->Left == NULL) && (root->Right == NULL))
            LeafCount = 1;
        else
            LeafCount = leaf(root->Left) + leaf(root->Right);
        return LeafCount;
    }

      3、求二叉树的高度

    int PostOrderGetHeight( BinTree BT )
    { 
        int HL, HR, MaxH;
        if( BT ) {
            HL = PostOrderGetHeight(BT->Left); /*求左子树的深度*/
            HR = PostOrderGetHeight(BT->Right); /*求右子树的深度*/
            MaxH = (HL > HR) ? HL : HR; /*取左右子树较大的深度*/
            return ( MaxH + 1 ); /*返回树的深度*/
        }
        else return 0; /* 空树深度为0 */
    }
    int depth = 0;
    void PreTreeDepth(BinTree root, int h)
    {
        if(root != NULL)
        {
            if(h>depth) depth = h;
            PreTreeDepth(root->Left, h+1);
            PreTreeDepth(root->Right, h+1);
        }
    }

      4、根据后序和中序遍历输出先序遍历

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 #define N 30
     4 
     5 /* 
     6 7
     7 2 3 1 5 7 6 4
     8 1 2 3 4 5 6 7 
     9 */
    10 
    11 void  Preorder(int a[],int b[],int i,int j,int s,int e) 
    12 {   /* i,j树的后序序列的起止, s,e树的中序序列的起止 */
    13     int k; 
    14     if( i > j )    return;
    15     printf(" %d", a[j]);    
    16     for(k = s; k <= e && b[k] != a[j] ; k++); 
    17     Preorder(a, b, i, i+(k-s)-1, s, k-1); /* 左子树 */   
    18     Preorder(a, b, i+(k-s), j-1, k+1, e); /* 右子树 */
    19 }
    20 
    21 int main()
    22 {
    23     int a[N*2];
    24     int n;
    25     scanf("%d", &n);
    26     for(int i=0;i<n*2;i++)
    27         scanf("%d", &a[i]);
    28     printf("Preorder:");
    29     Preorder(a,&a[n],0,n-1,0,n-1);
    30     return 0;
    31 }
     1 #include <cstdio>
     2 #define MAXN 50
     3 
     4 int pre[MAXN], in[MAXN], post[MAXN];
     5 
     6 void InputPostAndIn(int N);
     7 void solve(int preL, int inL, int postL, int n);
     8 void OutputPost(int N);
     9 
    10 int main()
    11 {
    12     int N; scanf("%d", &N);
    13     InputPostAndIn(N);
    14     solve(0, 0, 0, N);
    15     OutputPost(N);
    16 }
    17 
    18 void InputPostAndIn(int N)
    19 {
    20    
    21     for (int i=0; i<N; i++) {
    22         scanf("%d", &post[i]);
    23     }
    24     for (int i=0; i<N; i++) {
    25         scanf("%d", &in[i]);
    26     }
    27     
    28 }
    29 
    30 void solve(int preL, int inL, int postL, int Num)
    31 {
    32     if (Num == 0) {
    33         return;
    34     }
    35    
    36     int root, i, LTreeNodeNum, RTreeNodeNum;
    37     root = post[postL + Num - 1];
    38     pre[preL] = root;
    39     
    40     for (i=inL; in[i]!=root; i++) {}
    41     
    42     LTreeNodeNum = i - inL; RTreeNodeNum = Num - LTreeNodeNum - 1;
    43     
    44     solve(preL+1, inL, postL, LTreeNodeNum);
    45     solve(preL+1+LTreeNodeNum, inL+1+LTreeNodeNum, postL + LTreeNodeNum, RTreeNodeNum);
    46 }
    47 
    48 void OutputPost(int N)
    49 {
    50     printf("Preorder: ");
    51     printf("%d", pre[0]);
    52     for (int i=1; i<N; i++) {
    53         printf(" %d", pre[i] );
    54     }
    55     printf("
    ");
    56 }

      5、下列代码的功能是计算给定二叉树T的宽度。二叉树的宽度是指各层结点数的最大值。函数Queue_rear和Queue_front分别返回当前队列Q中队尾和队首元素的位置

     1 int Width( BinTree T )
     2 {
     3     BinTree  p;
     4     Queue Q;
     5     int Last, temp_width, max_width;
     6     
     7     temp_width = max_width = 0;
     8     Q = CreateQueue(MaxElements);
     9     Last = Queue_rear(Q);
    10     
    11     if ( T == NULL) 
    12         return 0;
    13     else 
    14     {
    15         Enqueue(T, Q);
    16         while (!IsEmpty(Q)) 
    17         {    /* 队头出队 */
    18             p = Front_Dequeue(Q); 
    19             /* 层宽加1,其左右子树根结点入队 */
    20             temp_width++           
    21             if ( p->Left != NULL )  
    22                 Enqueue(p->Left, Q);
    23             if ( p->Right != NULL )  
    24                 Enqueue(p->Right, Q); 
    25             /* 如果到了队尾更新最大宽度 */
    26             if ( Queue_front(Q) > Last ) {
    27                 Last = Queue_rear(Q);
    28                 if ( temp_width > max_width ) 
    29                     max_width = temp_width;           
    30                 temp_width=0; /* 临时变量归零 */
    31             } 
    32         }      
    33     }
    34     return  max_width; /* 返回最大宽度 */
    35 }

      6、层序遍历,输出层数及结点

      1 /* 
      2 以二叉链表作为存储结构,编写算法打印输出每个结点及所在的层次数(设根结点为第1层)
      3 算法:
      4 一、以扩充的先序序列构造二叉树,其他形式也可以;
      5 二、采取层序遍历的方式输出每层的结点及层次数;
      6 1、层序遍历的队列采取循环队列,定义层数level = 1;
      7 2、定义结点指针T、last、tail,初始指向树根,T = last = tail = BT;
      8 3、树根出队,树根的左右子树根结点入队,tail随入队结点变化;
      9 4、第一次循环,tail即指向第二层的最后的一个结点;
     10 5、循环步骤3,出队的结点指针指向last即更新last(last = tail;);
     11 注意:
     12 1、last始终指向每层的最后一个结点,出队结点指向last,输出level++;
     13 2、每次结点出队打印结点;
     14 ****
     15     tail是随入队的结点不断变化的;
     16     每一层的结点出队完毕,tail即指向了下层的最后的结点;    
     17     用last记录下tail,tail再随结点入队变化...
     18     
     19     判断每一层出队完毕条件是出队结点指针是否指向last;
     20     last开始指向树根结点的,树根出队后(一层完毕),tail指向第二层最后结点,
     21     更新last即last = tail,last指向了第二层的最后一个结点;
     22     第二层出队,tail随出队结点的左右子树的根结点的入队,不断变化,更新;
     23     第二层的结点出队完毕,last指向第三层的最后一个结点(last = tail);
     24     以此类推...
     25 ****
     26 */
     27 
     28 #include <stdio.h>
     29 #include <malloc.h>
     30 #define MAXSIZE 100
     31 
     32 /* 二叉树结点结构体 */
     33 typedef char ElementType;
     34 typedef struct TNode* BinTree;
     35 struct TNode{
     36     ElementType Data;
     37     BinTree Left;
     38     BinTree Right;
     39 };
     40 
     41 /* 创建结点 */
     42 BinTree NewNode(int x)
     43 {
     44     BinTree t = (BinTree)malloc(sizeof(struct TNode));
     45     t->Data = x;
     46     t->Left = t->Right = NULL;
     47     return t;
     48 }
     49 
     50 /* 扩充的先序序列构造二叉树 */
     51 BinTree PreCreate()
     52 {
     53     char ch;
     54     scanf(" %c,",&ch);
     55     /* 输入'0'符号即 NULL */
     56     if(ch=='0')
     57         return NULL;
     58     /* 根结点 */
     59     BinTree t = NewNode(ch);
     60     /* 递归左右子树结点 */
     61     t->Left  = PreCreate();
     62     t->Right = PreCreate();
     63     return t;
     64 }
     65 
     66 /* 先序遍历 */
     67 void PreorderTraversal( BinTree BT ){
     68     if(BT){
     69         printf("%5c",BT->Data);
     70         PreorderTraversal(BT->Left);
     71         PreorderTraversal(BT->Right);
     72     }
     73 }
     74 
     75 /* 层序遍历 */
     76 void LevelorderTraversal ( BinTree BT )
     77 {
     78     if ( !BT ) return; /* 若是空树则直接返回 */
     79     
     80     BinTree Queue[MAXSIZE]; /* 循环队列 */
     81     int front = 0, rear = 0;
     82     
     83     int level = 1; /* 层数 */
     84     BinTree T, last, tail; /* 记录层尾的last,tail */
     85 
     86     Queue[rear++] = BT;  /* 进队 */
     87     rear %= MAXSIZE;
     88     T = last = tail = BT;
     89     
     90     /* last依次指向层的最右边的结点 */
     91     while ( front!=rear )
     92     {   /* 结束 */
     93         if(T == last){ /* 为了输出层数,这段代码由最下上移 */
     94             printf("
    level %d:
    ",level);
     95             last = tail; /* 到了层尾部加一层 */
     96             level++;        
     97         }
     98         /* 开始 */
     99         T = Queue[front++];  /* 出队 */
    100         front %= MAXSIZE;
    101         
    102         printf("%5c", T->Data); /* 访问取出队列的结点 */
    103         
    104         if ( T->Left ){
    105             Queue[rear++] = T->Left;  /* 进队 */
    106             rear %= MAXSIZE;
    107             tail = T->Left;  /* 更新尾部 */
    108         }
    109         if ( T->Right ){
    110             Queue[rear++] = T->Right; /* 进队 */
    111             rear %= MAXSIZE;
    112             tail = T->Right; /* 更新尾部 */
    113         }        
    114     }
    115     printf("
    ");
    116 }
    117 
    118 /* A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0,  */
    119 int main()
    120 {
    121     BinTree BT = PreCreate();
    122     //PreorderTraversal(BT);
    123     LevelorderTraversal(BT);
    124     return 0;
    125 }

      7、请写出二叉树层次遍历的算法,即从根结点开始按层次由上至下,从左到右访问二叉树中的每个结点

    #include <stdio.h>
    #include <malloc.h>
    #define MAXSIZE 100
    
    /* 二叉树结点结构体 */
    typedef char elemtype;
    typedef struct btnode{
        elemtype data;
        struct  btnode  *lchild ,*rchild;
    }bitnode, *bitree;
    
    /* 创建结点 */
    bitree NewNode(int x)
    {
        bitree t = (bitree)malloc(sizeof(struct btnode));
        t->data = x;
        t->lchild = t->rchild = NULL;
        return t;
    }
    
    /* 扩充的先序序列构造二叉树 */
    bitree PreCreate()
    {
        char ch;
        scanf(" %c,",&ch);
        /* 输入'0'符号即 NULL */
        if(ch=='0')
            return NULL;
        /* 根结点 */
        bitree t = NewNode(ch);
        /* 递归左右子树结点 */
        t->lchild  = PreCreate();
        t->rchild = PreCreate();
        return t;
    }
    
    /* 先序遍历 */
    void PreorderTraversal( bitree BT ){
        if(BT){
            printf("%5c",BT->data);
            PreorderTraversal(BT->lchild);
            PreorderTraversal(BT->rchild);
        }
    }
    
    /* 层序遍历 */
    void LevelorderTraversal ( bitree BT )
    {
        if ( !BT ) return; /* 若是空树则直接返回 */
    
        bitree Queue[MAXSIZE]; /* 循环队列 */
        int front = 0, rear = 0;
    
        int level = 1; /* 层数 */
        bitree T, last, tail; /* 记录层尾的last,tail */
    
        Queue[rear++] = BT;  /* 进队 */
        rear %= MAXSIZE;
        T = last = tail = BT;
    
        /* last依次指向层的最右边的结点 */
        while ( front!=rear )
        {   /* 结束 */
            if(T == last){ /* 为了输出层数,这段代码由最下上移 */
                printf("
    level %d:
    ",level);
                last = tail; /* 到了层尾部加一层 */
                level++;
            }
            /* 开始 */
            T = Queue[front++];  /* 出队 */
            front %= MAXSIZE;
    
            printf("%5c", T->data); /* 访问取出队列的结点 */
    
            if ( T->lchild ){
                Queue[rear++] = T->lchild;  /* 进队 */
                rear %= MAXSIZE;
                tail = T->lchild;  /* 更新尾部 */
            }
            if ( T->rchild ){
                Queue[rear++] = T->rchild; /* 进队 */
                rear %= MAXSIZE;
                tail = T->rchild; /* 更新尾部 */
            }
        }
        printf("
    ");
    }
    /* 扩充的先序序列构造二叉树,样例
       A,B,D,0,H,0,0,E,0,I,0,0,C,F,0,0,G,J,0,K,0,0,0,  */
    int main()
    {
        bitree BT = PreCreate();
        //PreorderTraversal(BT); printf("
    ");
        LevelorderTraversal(BT);
        return 0;
    }
  • 相关阅读:
    eclipse如何安装反编译插件
    (原创)Android 沉浸式状态栏实现(来自FlymeAPI)
    android:scaleType属性(转)
    Eclipse快捷键(转)
    Debug调试
    (Android)Activity生命周期详解(原创)
    Android Handler机制简介
    Android学习路线(来自mars老师)
    Android AsyncTask异步任务
    Android Service(二)
  • 原文地址:https://www.cnblogs.com/GoldenEllipsis/p/11372936.html
Copyright © 2011-2022 走看看