Loading

算法笔记--数据结构--二叉树的遍历

二叉树的遍历

二叉树的遍历是指通过一定顺序访问二叉树的所有结点

遍历方法

  • 1️⃣先序遍历
  • 2️⃣中序遍历
  • 3️⃣后序遍历
  • 4️⃣层次遍历

深度优先算法:先序、中序、后序遍历

广度优先算法:层次遍历

对于前三种遍历:

  • 无论哪种遍历,左子树一定先于右子树
  • 所谓先中后是对根结点root而言的
    • 先序:root -> 左子树 -> 右子树
    • 中序:左子树 -> root -> 右子树
    • 后序:左子树 -> 右子树 -> root

先序遍历

Snipaste_2020-04-09_11-17-05

递归边界:空树

递归式:root -> 左子树 -> 右子树

void preorder(node* root){
    if(root == NULL)
        return;			// 空树
    printf("%d\n", root->data);
    preorder(root->lchild);		// 左子树
    preorder(root->rchild);		// 右子树
}

性质

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

中序遍历

Snipaste_2020-04-09_11-21-47

递归边界:空树

递归式:左子树 -> root -> 右子树

void inorder(node* root){
    if(root == NULL)
        return;					// 空树
    inorder(root->lchild);	
    printf("%d\n", root->data);
    inorder(root->rchild);
}

性质

  • 只要知道根结点,就可以通过根结点在中序遍历中的位置区分出左子树和右子树

如,上图的中序遍历为DBEACF

  • A是根结点,所以DBE是左子树结点,CF是右子树结点
  • B是根结点,所以D是左子树结点,E是右子树结点

如果事先知道是根结点:利用先序遍历

🟢所以,利用先序遍历和中序遍历可以确定一颗二叉树

后序遍历

Snipaste_2020-04-09_11-31-47

递归边界:空树

递归式:左子树 -> 右子树 -> root

void postorder(node* root){
    if(root == NULL)
        return;				// 空树
    postorder(root->lchild);
    postorder(left->rchild);
    printf("%d\n", root->data);
}

性质

  • 序列的最后一个一定是根结点

如,上图的后序遍历序列是DEBFCA

  • A是根结点,A的左子树是BED,它们的后序排列是DEB,所以B是左子树的根结点

🟢所以,利用后序遍历和中序遍历可以确定一颗二叉树

🉐无论是先序遍历还是后序遍历,都必须知道中序遍历序列才能唯一地确定一棵树

层序遍历

层序遍历:按照层次的顺序从根结点向下逐层进行遍历,且对同一层的结点从左往右遍历

Snipaste_2020-04-09_11-42-26

上图的遍历序列为ABCDEF,所以二叉树的层序遍历就相当于二叉树从根结点开始的广度优先搜索

  • 1️⃣将根结点root加入队列q
  • 2️⃣取出队列首结点,访问它
  • 3️⃣如果该结点有左孩子,将左孩子入队
  • 4️⃣如果该结点有右孩子,将右孩子入队
  • 5️⃣返回2️⃣,直到队列为空
void LayerOrder(node* root){
    queue<node*> q;				// 注意队列类型
    q.push(root);
    while(!q.empty()){
        node* now = q.front();
        q.pop;
        printf("%d ", now->data);
        if(now->lchild != NULL) q.push(now->lchild);
        if(now->rchild != NULL) q.push(now->rchild);
    }
}

注意队列中的元素是node*型而不是node型,因为队列中保存的只是原元素的一个副本,因此如果队列中直接存放了node型,当需要修改队首元素时,就无法对原元素进行修改,而放node*型,就可以通过访问地址去修改原元素

很多题目要求计算出每个结点所处的层次,所以要定义结构体:

struct node {
    int data;		// 数据域
    int layer;		// 层次
    node* lchild;	// 左子树
    node* rchild;	// 右子树
}

在根结点入队前就先令根结点的layer为1来表示根结点的第一层,之后now->lchildnow->rchild入队前,把它们的层号都记为当前结点now的层号加 1

void LayerOrder(node* root){
    queue<node*> q;				// 注意队列是存放地址
    root->layer = 1;			// 根结点的层号为1
    q.push(root);				// 将根结点地址入队
    while(!q.empty()){
        node* now = q.front();	// 取出队首元素
        q.pop();
        printf("%d ", now->data);	// 访问队首元素
        if(now->lchild != NULL){	// 左子树非空
            now->lchild->layer = now->layer + 1;	// 左孩子的层号为当前层加1
            q.push(now->lchild);
        }
        if(now->rchild != NULL){	// 右子树非空
            now->rchild->layer = now->layer + 1;	// 右孩子的层号为当前层加1
            q.push(now->rchild);
        }
    }
    
}

重建二叉树

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

Snipaste_2020-04-09_14-45-18

如图,先序序列的第一个元素\(pre_1\)是当前二叉树的根结点,由中序序列的性质可知,当前二叉树的根结点将中序序列划分为左子树和右子树,因此,要做的就是在中序序列中找到某个结点\(in_k\),使得ink == pre1,这样就在中序序列中找到了根结点。所以左子树的结点个数numLeft = k - 1,这样,左子树的先序区间就是[2, k],左子树的中序序列区间是[1, k-1];右子树的先序序列区间是[k+1, n],右子树的中序序列区间是[k+1, n],接着只要往左子树和右子树进行递归构建二叉树

Snipaste_2020-04-09_14-55-50

换种表示方法,如果递归过程中当前先序序列的区间是[preL, preR],中序序列的区间是[inL, inR],那么左子树的结点个数为numLeft = k - inL,这样左子树的先序序列区间就是[preL+1, preL+numLeft],左子树的中序序列区间是[inL, k-1],右子树的先序序列区间是[preL+numLeft+1, preR],右子树的中序序列区间是[k+1, inR]

递归边界:只要先序序列的长度小于等于0时,当前二叉树就不存在了,即为递归边界

node* create(int preL,int preR, int inL, int inR){
    if(preL > preR){
        return NULL;	// 先序序列长度小于等于0时,直接返回
    }
    node* root = new node;
    root->data = pre[preL];
    int k;
    for(k = inL; k <= inR; k++){
        if(in[k] == pre[preL]){
            break;
        }
    }
    int numLeft = k - inL;
    
    root->lchild = create(preL + 1, preL + numLeft, inL, k - 1);
    root->rchild = create(preL + numLeft + 1, preR, k + 1, inR);
    return root;
}

上面代码就构建出了一棵二叉树

中序序列可以和先序、后序、层序中的任意一个来构建唯一的二叉树,而后三者两两搭配或者三个在一起都无法构建唯一二叉树

实例[PAT A1020]

题目

Suppose that all the keys in a binary tree are distinct positive integers. Given the postorder and inorder traversal sequences, you are supposed to output the level order traversal sequence of the corresponding binary tree.

输入格式

Each input file contains one test case. For each case, the first line gives a positive integer N (≤30), the total number of nodes in the binary tree. The second line gives the postorder sequence and the third line gives the inorder sequence. All the numbers in a line are separated by a space.

输出格式

For each test case, print in one line the level order traversal sequence of the corresponding binary tree. All the numbers in a line must be separated by exactly one space, and there must be no extra space at the end of the line.

输入样例

7
2 3 1 5 7 6 4
1 2 3 4 5 6 7

输出样例

4 1 6 3 5 7 2

解题思路

Snipaste_2020-04-09_15-52-03

代码

#include<cstdio>
#include<queue>
using namespace std;
const int maxn = 32;

struct node{
    int data;
    node* lchild;
    node* rchild;
};

int in[maxn], post[maxn];   // 中序遍历序列 和 后序遍历序列
int n;                      // 序列长度

node* create(int postL, int postR, int inL, int inR){
    if(postL > postR){
        return NULL;
    }
    node* root = new node;
    root->data = post[postR];
    int k;
    for(k = inL; k <= inR; k++){
        if(in[k] == post[postR]){
            break;
        }
    }
    int numLeft = k - 1 - inL + 1;
    root->lchild = create(postL, postL + numLeft - 1, inL, k - 1);
    root->rchild = create(postL + numLeft, postR - 1, k + 1, inR);
    return root;
}

int num = 0;                // 用于控制输出空格
void BFS(node* root){       // 利用广度优先搜索来实现层序遍历
    queue<node*> q;
    q.push(root);
    while(!q.empty()){
        node* now = q.front();
        q.pop();
        printf("%d", now->data);
        num++;
        if(num < n)printf(" ");
        if(now->lchild != NULL){
            q.push(now->lchild);
        }
        if(now->rchild != NULL){
            q.push(now->rchild);
        }
    }
}

int main(){
    scanf("%d", &n);
    for(int i = 0; i < n; i++){
        scanf("%d", &post[i]);
    }
    for(int i = 0; i < n; i++){
        scanf("%d", &in[i]);
    }
    node* root = create(0, n - 1, 0, n - 1);
    BFS(root);
    return 0;
}

Write by Gqq

posted @ 2020-04-09 17:43  ZHGQCN  阅读(364)  评论(0)    收藏  举报