题目要求:给出二叉树的后序遍历序列和中序遍历序列,输出二叉树的层次遍历序列。

传送门

Sample Input

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

Sample Output

4 1 6 3 5 7 2

解题思路

首先,我们在数据结构课程中学过下面的结论:

  • 后序遍历: 左 右 根
  • 中序遍历: 左 根 右

显然,后序序列中的最后一个元素总是某个子树的根,叶子节点也算是一个子树 (可能你觉得这是一句废话)。

此外, 只要确定根,那么可以在中序的某一片段序列中 “分离” 出左右子树。

下面我们来分析一下 Sample ,解析如何从如上2个结论唯一确定一颗二叉树。

在后序序列中:

2 3 1 5 7 6 4

4 毫无疑问是整个二叉树的根。现在在中序序列中找出 4 的位置。

1 2 3 4 5 6 7

也就是说可以得到二叉树的基本结构如下:

图1
4
/ \
1,2,3 5,6,7

再看后序序列剩下的元素:

2 3 1 5 7 6

最后一个元素是 6, 说明在 6 是根。

在中序序列中:

1 2 3 | 4 | 5 6 7

说明在 图1 中, 6 是右子树的根。

图2
4
/ \
1,2,3 6
/ \
5 7

继续遍历后序序列:

2 3 1 5 7

5 和 7 既是叶子节点也是一个特殊的根。

此时,后序序列剩下:

2 3 1

中序序列为:

1 2 3

显然, 1 是根, 2 和 3 是 1 的右子树。以此类推, 2 是 3 的左子树。那么中序序列和后序序列唯一确定的二叉树如下:

         4
/ \
1 6
\ / \
3 5 7
/
2

概括一下算法要点:

  • 从右往左遍历后序序列,得到根
  • 在中序序列中找到根,分离出左右子树
  • 对左右子树进行同样的操作

不难看出,是一个递归。

数据结构课程上,在纸上这类题目大家都会画出来,但是一到 Code 层面,完整实现还是有点难度的。

最后解析一下代码实现。

  • 参数 lr: 中序序列inorder[l...r], 代表当前所处理的子树
  • rootIdxPost: 后序序列中根的位置。(实际上取值变化就是: (N-1), ..., 0 )
Tree createTree(int l, int r, int &rootIdxPost)
{
if (l > r)
return NULL;
if (l == r)
{
Tree t = new TreeNode(inorder[l]);
rootIdxPost--;
return t;
}
int rootVal = postorder[rootIdxPost--];
int rootIdxIn = findRoot(inorder, rootVal);
Tree root = new TreeNode(rootVal);
root->right = createTree(rootIdxIn + 1, r, rootIdxPost);
root->left = createTree(l, rootIdxIn - 1, rootIdxPost);
return root;
}

完整代码:

#include <cstdio>
#include <vector>
#include <assert.h>
#include <queue>
#define NMAX 35
using namespace std;
struct TreeNode
{
int val;
TreeNode *left, *right;
TreeNode(int v = -1, TreeNode *l = NULL, TreeNode *r = NULL) : val(v), left(l), right(r) {}
};
typedef TreeNode *Tree;
vector<int> postorder(NMAX), inorder(NMAX);
int N = 0;
int findRoot(vector<int> &inorder, int rootVal)
{
size_t len = inorder.size();
for (size_t i = 0; i < len; i++)
{
if (inorder[i] == rootVal)
return i;
}
assert(0);
return -1;
}
Tree createTree(int l, int r, int &rootIdxPost)
{
if (l > r)
return NULL;
if (l == r)
{
Tree t = new TreeNode(inorder[l]);
rootIdxPost--;
return t;
}
int rootVal = postorder[rootIdxPost--];
int rootIdxIn = findRoot(inorder, rootVal);
Tree root = new TreeNode(rootVal);
root->right = createTree(rootIdxIn + 1, r, rootIdxPost);
root->left = createTree(l, rootIdxIn - 1, rootIdxPost);
return root;
}
void level(Tree root)
{
if (root == NULL)
return;
queue<Tree> q;
printf("%d", root->val);
if (root->left)
q.push(root->left);
if (root->right)
q.push(root->right);
while (!q.empty())
{
Tree p = q.front();
q.pop();
printf(" %d", p->val);
if (p->left)
q.push(p->left);
if (p->right)
q.push(p->right);
}
}
int main()
{
scanf("%d", &N);
for (int i = 0; i < N; i++)
{
scanf("%d", &postorder[i]);
}
for (int i = 0; i < N; i++)
{
scanf("%d", &inorder[i]);
}
int rootIdxPost = N - 1;
Tree root = createTree(0, N - 1, rootIdxPost);
level(root);
}

markdown写bolg,"那是真的niubility"。

[算法笔记] PAT-ADV-1020的更多相关文章

  1. 学习Java 以及对几大基本排序算法(对算法笔记书的研究)的一些学习总结(Java对算法的实现持续更新中)

    Java排序一,冒泡排序! 刚刚开始学习Java,但是比较有兴趣研究算法.最近看了一本算法笔记,刚开始只是打算随便看看,但是发现这本书非常不错,尤其是对排序算法,以及哈希函数的一些解释,让我非常的感兴 ...

  2. 算法笔记--数位dp

    算法笔记 这个博客写的不错:http://blog.csdn.net/wust_zzwh/article/details/52100392 数位dp的精髓是不同情况下sta变量的设置. 模板: ]; ...

  3. 算法笔记--lca倍增算法

    算法笔记 模板: vector<int>g[N]; vector<int>edge[N]; ][N]; int deep[N]; int h[N]; void dfs(int ...

  4. 算法笔记--STL中的各种遍历及查找(待增)

    算法笔记 map: map<string,int> m; map<string,int>::iterator it;//auto it it = m.begin(); whil ...

  5. 算法笔记--priority_queue

    算法笔记 priority_queue<int>que;//默认大顶堆 或者写作:priority_queue<int,vector<int>,less<int&g ...

  6. 算法笔记--sg函数详解及其模板

    算法笔记 参考资料:https://wenku.baidu.com/view/25540742a8956bec0975e3a8.html sg函数大神详解:http://blog.csdn.net/l ...

  7. 算法笔记——C/C++语言基础篇(已完结)

    开始系统学习算法,希望自己能够坚持下去,期间会把常用到的算法写进此博客,便于以后复习,同时希望能够给初学者提供一定的帮助,手敲难免存在错误,欢迎评论指正,共同学习.博客也可能会引用别人写的代码,如有引 ...

  8. 算法笔记_067:蓝桥杯练习 算法训练 安慰奶牛(Java)

    目录 1 问题描述 2 解决方案   1 问题描述 问题描述 Farmer John变得非常懒,他不想再继续维护供奶牛之间供通行的道路.道路被用来连接N个牧场,牧场被连续地编号为1到N.每一个牧场都是 ...

  9. 算法笔记(c++)--回文

    算法笔记(c++)--回文 #include<iostream> #include<algorithm> #include<vector> using namesp ...

  10. 算法笔记(c++)--完全背包问题

    算法笔记(c++)--完全背包和多重背包问题 完全背包 完全背包不同于01背包-完全背包里面的东西数量无限 假设现在有5种物品重量为5,4,3,2,1  价值为1,2,3,4,5  背包容量为10 # ...

随机推荐

  1. ElasticSearch系列四 CURD

    1: ES 类似JPA操作 1.1 编写实体类 1.2 编写映射文件 xxx.json 1.3编写repository继承 ElasticSearchrepository 1.4 编写admin 的C ...

  2. C++走向远洋——(项目二、存储班长信息的学生类、派生)

    */ * Copyright (c) 2016,烟台大学计算机与控制工程学院 * All rights reserved. * 文件名:text.cpp * 作者:常轩 * 微信公众号:Worldhe ...

  3. paillier加密算法原理详解

    paillier加密算法是一种公钥加密算法,基于复合剩余类的困难问题.满足加法同态,即密文相乘等于明文相加:D(E(m1)·E(m2))=m1+m2.这里详细介绍其加密解密是如何推导的,需要具备数论. ...

  4. Java设计模式之结构模式

    一.外观模式 分析:外观模式是为子系统的一组接口提供一个统一的界面,数据库JDBC连接应用就是外观模式的一个典型例子,特点:降低系统的复杂度,增加灵活性.结果:代码示例: public class D ...

  5. Go语言基础篇(1) —— 编写第一个Go程序

    创建文件hello_world.go package main //包,表名代码所在的包 import "fmt" //引入依赖 //main方法 func main(){ fmt ...

  6. vue+element tree(树形控件)组件(2)

    今天记录组件的代码和一个调用它的父组件的代码,接口接收数据直接传element直接能用的,也就是经过上一章函数处理过的数据以下是代码 父组件 <template> <commonfi ...

  7. 我折腾的shell笔记

    目录 Mac一些常用的快捷键记录 iTerm2或者命令行相关 Mac桌面上或者某目录下操作 一些实用脚本示例 代码无提示或者其他抽风症状,清除Xcode缓存 查看当前网络ip地址 日常提交推送git代 ...

  8. Linux进程操作记录

    关于Gunicorn如何终止进程: 1.用进程树显示主进程PID: pstree -ap | grep gunicorn 2.如果有daemon进程无法用kill -9删除(可能是因为daemon屏蔽 ...

  9. 使用PHP语言制作具有加减乘除取余功能的简单计算器

    准备工作: 使用环境 :PHPStudy 开启Apache和Mysql 打开代码编辑器 <!DOCTYPE html> <html lang="en"> & ...

  10. 026.掌握Service-外部访问

    一 集群外部访问 由于Pod和Service都是Kubernetes集群范围内的虚拟概念,所以集群外的客户端默认情况,无法通过Pod的IP地址或者Service的虚拟IP地址:虚拟端口号进行访问.通常 ...