• 数据结构——二叉树的遍历


      二叉树的遍历是指通过一定顺序访问二叉树的所有结点。遍历方法一般有四种:先序遍历、中序遍历、后序遍历及层次遍历。

    一、先序遍历

      对先序遍历来说,总是先访问根结点 root,然后才去访问左子树和右子树,因此先序遍历的遍历顺序是根结点 -> 左子树 -> 右子树。代码如下:

    1 // 先序遍历
    2 void preorder(node* root) {
    3     if(root == NULL) {
    4         return;        // 空树,递归边界 
    5     }
    6     printf("%d
    ", root->data);        // 访问该结点
    7     preorder(root->lchild);            // 访问左子树 
    8     preorder(root->rchild);            // 访问右子树  
    9 } 

      对一棵二叉树的先序遍历序列,序列的第一个一定是根结点。  

    二、中序遍历

      对中序遍历来说,总是先访问左子树,再访问根结点,最后访问右子树,因此中序遍历的遍历顺序是左子树 -> 根结点 -> 右子树。代码如下:

    1 // 中序遍历
    2 void inorder(node* root) {
    3     if(root == NULL) {
    4         return;        // 空树,递归边界 
    5     }
    6     inorder(root->lchild);            // 访问左子树 
    7     printf("%d
    ", root->data);        // 访问该结点
    8     inorder(root->rchild);            // 访问右子树  
    9 } 

      由于中序遍历总是把根结点放在左子树和右子树之间,因此只要知道根结点,就可以通过根结点在中序遍历序列中的位置区分出左子树和右子树。

    三、后序遍历

      对后序遍历来说,总是先访问左子树,再访问右子树,最后才访问根结点,因此后序遍历的遍历顺序是左子树 -> 右子树 -> 根结点。代码如下:

    1 // 后序遍历
    2 void postorder(node* root) {
    3     if(root == NULL) {
    4         return;        // 空树,递归边界 
    5     }
    6     postorder(root->lchild);            // 访问左子树 
    7     postorder(root->rchild);            // 访问右子树
    8     printf("%d
    ", root->data);            // 访问该结点  
    9 } 

    四、一个重要问题

      给定一棵二叉树的先序遍历序列和中序遍历序列,重建这棵二叉树。

      假设在递归过程中,当前先序序列的区间为 [preL, preR] ,中序序列的区间为 [inL, inR] ,且 ink = pre1 ,那么左子树的结点个数为 numLeft = k - inL。这样左子树的先序序列区间就是 [preL+1, pre+numLeft],左子树的中序序列区间是 [inL, k-1];右子树的先序序列区间是 [preL+numLeft+1, preR],右子树的中序序列区间是 [k+1, inR]。

      当先序序列的长度小于等于 0 时,当前二叉树就不存在了,这个条件可以作为递归边界。代码如下:

     1 int pre[maxn]={1, 2, 4, 5, 3, 6}, in[maxn]={4, 2, 5, 1, 3, 6};    // 储存先序、中序序列
     2 // 当前先序序列 [preL, preR],中序序列 [inL, inR],返回根结点地址
     3 node* create1(int preL, int preR, int inL, int inR) {
     4     if(preL > preR) {
     5         return NULL;    // 先序序列长度小于等于0,递归边界 
     6     }
     7     node* root = (node*)malloc(sizeof(node));
     8     root->data = pre[preL];        // 新结点的数据域为根结点的值
     9     int k;
    10     for(k=inL; k<=inR; ++k) {
    11         if(in[k] == pre[preL]) {    // 在中序序列中找到根结点 
    12             break;
    13         }
    14     } 
    15     int numLeft = k - inL;    // 左子树结点个数
    16     
    17     // 左子树的先序序列区间是 [preL+1, pre+numLeft],中序序列区间是 [inL, k-1]
    18     root->lchild = create1(preL+1, preL+numLeft, inL, k-1);
    19     // 右子树的先序序列区间是 [preL+numLeft+1, preR],中序序列区间是 [k+1, inR]
    20     root->rchild = create1(preL+numLeft+1, preR, k+1, inR);
    21     
    22     return root; 
    23 } 

      完整 C 代码如下:

      1 /*
      2     二叉树 
      3 */
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <math.h>
      8 #include <stdlib.h>
      9 #include <time.h>
     10 #include <stdbool.h>
     11 
     12 #define typename int
     13 #define maxn 20
     14 // 二叉树结构定义 
     15 typedef struct _node {
     16     typename data;        // 数据域
     17     struct _node* lchild;        // 指向左子树的根结点
     18     struct _node* rchild;        // 指向右子树的根结点 
     19 } node; 
     20 
     21 // 生成一个新节点,v为数据 
     22 node* newNode(typename v) {
     23     node* Node = (node*)malloc(sizeof(node));
     24     Node->data = v;
     25     // 初始状态下没有左右子树 
     26     Node->lchild = Node->rchild = NULL;
     27     return Node;
     28 } 
     29 
     30 // 在根结点为root的二叉树查找所有数据域为x的结点并修改为newdata 
     31 void search(node* root, typename x, typename newdata) {
     32     if(root == NULL) {
     33         return;        // 空树 
     34     }
     35     if(root->data == x) {    // 查到 x 
     36         root->data = newdata;    // 修改 
     37     }
     38     search(root->lchild, x, newdata);    // 遍历左子树
     39     search(root->rchild, x, newdata);    // 遍历右子树      
     40 } 
     41 
     42 // 在以*root为根结点的二叉树插入一个新节点 
     43 void insert(node** root, typename x) {
     44     if((*root) == NULL) {        // 找到插入位置 
     45         (*root) = newNode(x);
     46         return; 
     47     }
     48     if(x < (*root)->data) {        // 往左子树插入 
     49         insert(&((*root)->lchild), x);
     50     } else {                    // 往右子树插入 
     51         insert(&((*root)->rchild), x);
     52     }
     53 }
     54 
     55 // 二叉树的建立 
     56 node* create(typename data[], int n) {
     57     node* root = NULL;        // 新建空根结点 root 
     58     int i;
     59     for(i=0; i<n; ++i) {    // 将 data 中的数据插入二叉树 
     60         insert(&root, data[i]);
     61     } 
     62     return root;            // 返回根结点 
     63 } 
     64 
     65 // 先序遍历
     66 void preorder(node* root) {
     67     if(root == NULL) {
     68         return;        // 空树,递归边界 
     69     }
     70     printf("%d
    ", root->data);        // 访问该结点
     71     preorder(root->lchild);            // 访问左子树 
     72     preorder(root->rchild);            // 访问右子树  
     73 } 
     74 
     75 // 中序遍历
     76 void inorder(node* root) {
     77     if(root == NULL) {
     78         return;        // 空树,递归边界 
     79     }
     80     inorder(root->lchild);            // 访问左子树 
     81     printf("%d
    ", root->data);        // 访问该结点
     82     inorder(root->rchild);            // 访问右子树  
     83 } 
     84 
     85 // 后序遍历
     86 void postorder(node* root) {
     87     if(root == NULL) {
     88         return;        // 空树,递归边界 
     89     }
     90     postorder(root->lchild);            // 访问左子树 
     91     postorder(root->rchild);            // 访问右子树
     92     printf("%d
    ", root->data);            // 访问该结点  
     93 } 
     94 
     95 int pre[maxn]={1, 2, 4, 5, 3, 6}, in[maxn]={4, 2, 5, 1, 3, 6};    // 储存先序、中序序列
     96 // 当前先序序列 [preL, preR],中序序列 [inL, inR],返回根结点地址
     97 node* create1(int preL, int preR, int inL, int inR) {
     98     if(preL > preR) {
     99         return NULL;    // 先序序列长度小于等于0,递归边界 
    100     }
    101     node* root = (node*)malloc(sizeof(node));
    102     root->data = pre[preL];        // 新结点的数据域为根结点的值
    103     int k;
    104     for(k=inL; k<=inR; ++k) {
    105         if(in[k] == pre[preL]) {    // 在中序序列中找到根结点 
    106             break;
    107         }
    108     } 
    109     int numLeft = k - inL;    // 左子树结点个数
    110     
    111     // 左子树的先序序列区间是 [preL+1, pre+numLeft],中序序列区间是 [inL, k-1]
    112     root->lchild = create1(preL+1, preL+numLeft, inL, k-1);
    113     // 右子树的先序序列区间是 [preL+numLeft+1, preR],中序序列区间是 [k+1, inR]
    114     root->rchild = create1(preL+numLeft+1, preR, k+1, inR);
    115     
    116     return root; 
    117 } 
    118 
    119 /*// 输出二叉树,先序 
    120 void print(node* root) {
    121     if(root == NULL) {
    122         return;
    123     }
    124     printf("%d ", root->data);
    125     print(root->lchild);
    126     print(root->rchild);
    127 }*/
    128 
    129 int main() {
    130     /*int data[5] = {1, 2, 3, 4, 5};
    131     node* tree = create(data, 5);    // 创建二叉树 
    132     print(tree);                    // 输出二叉树 */
    133     
    134     node* tree = create1(0, 5, 0, 5);
    135     postorder(tree);
    136     
    137     return 0;
    138 }
    二叉树的遍历
  • 相关阅读:
    cpu gpu数据同步
    metal &object c
    unity macro 分平台处理
    unity reflection probe --- forward deferred transparent opaque
    unity pbr
    unity 实时间接光照 解决方案
    Gpu driven rendering pipelines & bindless texture
    在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke
    转自大楚网:微软SAPI:让你的软件能说会道
    (转) 使用Speech SDK 5.1文字转音频
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes21.html
Copyright © 2020-2023  润新知