PAT归纳总结——关于二叉树的一些总结
今天是6月26日到下个月的这个时候已经考过试了,为了让自己考一个更高的分数,所以我打算把PAT的相关题型做一个总结。目前想到的方法就是将相关的题型整理到一起然后,针对这种题型整理出一些方法。
二叉树的构建
有很多题目需要自己构造一棵二叉树有的是给出层序遍历的结果然后自己来构造二叉树,有的是给出二叉树的前序和中序或者给出二叉树的中序和后序来构造二叉树(如果给出前序和后序遍历的结果我们是不能够确定一棵二叉树的,很少数特殊的情况下可以唯一确定)
给出层序遍历的结果来构造二叉树:
1 void buildTree() {
2 root->value = que.front();
3 que.pop();
4 nodeQue.push(root);
5 while(!que.empty()) {
6 node parent = nodeQue.front();
7 nodeQue.pop();
8 node left = new Node();
9 node right = new Node();
10 if (!que.empty()) {
11 left->value = que.front();
12 parent->left = left;
13 nodeQue.push(left);
14 que.pop();
15 }
16 if (!que.empty()) {
17 right->value = que.front();
18 parent->right = right;
19 nodeQue.push(right);
20 que.pop();
21 }
22 }
23 }
给出了中序遍历和后序遍历的结果来构建二叉树:
后一种树的构建方法是通过前序遍历和中序遍历以及后序遍历中结点的遍历次序来实现的。比如:前序遍历中第一个数字是根节点,然后找出根节点在中序遍历中的位置pos,pos左边的就是左子树,pos右边的就是右子树。然后再递归的查找左右子树,直到叶子节点为止。中序遍历和后序遍历的情况与之相同,只不过后序遍历的最后一个数字代表的是根节点。
1 // 由中序遍历和后序遍历构建二叉树
2 node buildTree(int l1, int r1, int l2, int r2) {
3 if (l1 > r1 || l2 > r2) return NULL;
4 int val = postOrder[r1];
5 node root = new Node(val);
6 int pos = 0;
7 for (int i = l2; i <= r2; ++i) {
8 if (inOrder[i] == val) {
9 pos = i;
10 break;
11 }
12 }
13 int rightLen = r2 - pos;
14 int leftLen = pos - l2;
15 root->right = buildTree(r1 - rightLen, r1 - 1, pos + 1, r2);
16 root->left = buildTree(l1, l1 + leftLen - 1, l2, l2 + leftLen - 1);
17 return root;
18 }
BST + 前序遍历构造二叉树
还有一种情况是,题目说明了这棵树是BST(二叉搜索树),然后又给出了这棵二叉树的前序遍历序列,由此来构造这棵二叉树。(例题:1143 Lowest Common Ancestor)因为BST满足左子树都比根结点小,右子树都比根结点大的条件,所以我们可以通过前序遍历,找到根结点,然后再在前序遍历序列中找到第一个比根结点大的结点,此节点右侧(包含该结点)为右子树的结点,左侧为左子树的结点。然后,通过递归函数构建左子树和右子树,直到叶节点。
(递归法)
1 node buildTree(vector<int>& v, int start, int end) {
2 if (start > end) return NULL;
3 node root = new Node(v[start]);
4 int pos = start + 1;
5 while (pos <= end && v[pos] < v[start]) ++pos;
6 if (pos == start + 1) {
7 root->right = buildTree(v, start + 1, end);
8 } else if (pos == end + 1) {
9 root->left = buildTree(v, start + 1, end);
10 } else {
11 root->left = buildTree(v, start + 1, pos - 1);
12 root->right = buildTree(v, pos, end);
13 }
14 return root;
15 }
(非递归法)
1 node buildTree(vector<node>& v) {
2 node root = v[0];
3 for (int i = 1; i < v.size(); ++i) {
4 node temp = root;
5 while (temp != NULL) {
6 if (v[i]->val < temp->val) {
7 if (temp->left != NULL)
8 temp = temp->left;
9 else {
10 temp->left = v[i];
11 break;
12 }
13 } else {
14 if (temp->right != NULL)
15 temp = temp->right;
16 else {
17 temp->right = v[i];
18 break;
19 }
20 }
21 }
22 }
23 return root;
24 }
给出前序遍历和后序遍历来构造二叉树
一般情况下由前序遍历和后序遍历是不能够构造出一颗二叉树的,但是在一些特殊条件下还是能够唯一确定这棵二叉树的。因为当每一个根结点既有左子树又有右子树时,那么就不会存在左右子树不确定的情况出现,这时这可二叉树也就是一棵确定的二叉树。但是我们要怎样判断一个根结点是不是既有左子树又有右子树呢?
PreOrder: 1 2 3 4 6 7 5
PostOrder: 2 6 7 4 5 3 1
当所给的序列是上面的这种情况的时候,以前序遍历为基准来确定根结点,可知,1是这棵树的根结点,那么1后面的2一定是它的一个孩子结点。通过在后序遍历中2的位置我们可以得出,2前面没有数字,也即2不可能作为一个根节点存在。并且,2与其父亲结点之间存在其他的数字,这说名2是其父节点的一个左子树,2和1之间的数字为右子树结点,根据前序遍历的序列可以得出2后面的数字3是右子树的根结点,然后再依据此规律递归。
PreOrder: 1 2 3 4 6 7 5
PostOrder:6 7 4 5 3 2 1
当所给的序列是上面的这种情况的时候,同样以前序遍历为基准,可以得出1为根结点,2为1的一个孩子结点,但是在后序遍历中2和1是紧挨着的。假设结点1有两个孩子结点,如果2为1的左子树,那么在后序遍历序列中2和1之间一定还会有其他的数字来充当右子树;如果2为1的右子树,那么在前序遍历的序列中,一定还会有其他的数字来充当左子树。显然,这两种情况都与所给出的序列矛盾,也即在这种情况下结点1只有一个孩子结点,这个孩子结点是右子树,还是左子树是不确定的,这也是导致了所构造出来的二叉树不唯一的根本原因。
1 int preOrder[32], postOrder[32];
2 int site = 0, num = 0, n;
3 bool isUniq = true;
4
5 void Build(Tree &node, int s, int e) {
6 if (s > e) {
7 return;
8 }
9 node = (Tree)malloc(sizeof(TreeNode));
10 node->val = postOrder[e];//后续遍历的最后一个节点肯定是当前子树的根节点
11 node->left = node->right = NULL;
12 site++;
13 int child = s;
14 for (; child < e; child++) {//寻找当前根节点的一个孩子
15 if (postOrder[child] == preOrder[site])break;
16 }
17 if (e - child == 1) {//当前根节点只有一个孩子,此时树不唯一
18 isUniq = false;
19 Build(node->left, s, e - 1);
20 }
21 else if (e - child > 1) {//当前根节点有俩个孩子,且这个孩子定是左孩子
22 Build(node->left, s, child);
23 Build(node->right, child + 1, e - 1);
24 }
25 }
26
27 // 原文链接:https://blog.csdn.net/qq_40504851/article/details/100590035
LCA问题 (值得注意)
刷题的过程中也遇到一些寻找二叉树中最近公共祖先结点的问题,这类问题需要我们根据题中所给的数据首先要构建出这棵二叉树(构建的方法上文已经给出),LCA(The Lowest Common Ancestor)这类问题有一个很好用的模板可以使用:
1 node lowestCommonAncestor(node root, int n1, int n2) {
2 if (!root || root->val == n1 || root->val == n2) return root;
3 node left = lowestCommonAncestor(root->left, n1, n2);
4 node right = lowestCommonAncestor(root->right, n1, n2);
5 return !left ? right : !right ? left : root;
6 }
从根节点对这棵二叉树的左右子树进行递归遍历,当遍历到叶子结点或者某个要查询的结点的时候,就将该结点返回,如果某个根结点的左子树和右子树都非空的话则说明该节点就是所查询结点的LCA。如果一个结点为空另一个结点非空的话,则说明该非空的结点就是所要查询的LCA,也即所查寻的两个结点中一个结点是另一个结点的祖先结点。如果所查询的两个结点都是NULL的话则返回NULL。
思考:为什么这样写是错误的?
return left ? left: right ? right: root;
堆
这类问题也在PAT考试的过程中出现过,但是所考察的形式都比较简单,首先要明白大堆和小顶堆的概念,大顶堆就是根结点的值大于左子树和右子树中结点的值,小顶堆则与之相反,根结点中的value小于左子树和右子树中的值。至于,怎么构建大顶堆现在还没有遇到这样的题,之后遇到了再补上。
PAT归纳总结——关于二叉树的一些总结的更多相关文章
- PAT L2-011 玩转二叉树
https://pintia.cn/problem-sets/994805046380707840/problems/994805065406070784 给定一棵二叉树的中序遍历和前序遍历,请你先将 ...
- PAT L2-011 玩转二叉树(二叉树层序遍历)
给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列.所谓镜面反转,是指将所有非叶结点的左右孩子对换.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出 ...
- PAT 1020 Tree Traversals[二叉树遍历]
1020 Tree Traversals (25)(25 分) Suppose that all the keys in a binary tree are distinct positive int ...
- PAT归纳总结——关于图的一些总结
在刷题的过程中经常会碰到一些关于图论的问题,下面我将根据自己刷题的经验来对这些问题做一个总结. 图的表示方法 解决图论中的问题首先要解决的问题就是图的表示方法这一问题,图的表示方法主要有两种,一种使用 ...
- PAT归纳总结——关于模拟类问题的一些总结
关于时间的模拟问题 在刷题的过程中碰到了一些关于时间先后顺序的模拟题目,刚开始做的时候确实挺麻烦的,今天把这类问题的解题思路来整理一下. 比较典型的有: 1017 Queueing at Bank 1 ...
- PAT归纳总结——关于C++输入输出格式问题的一些总结
自从使用了C++就不再想使用C语言来刷题了,C++便捷的输入输出方式,以及一些STL库函数的使用都要比使用C语言方便的多.但是使用的时候还有一些需要注意的地方,在这篇博客中写一下.(更好的教程可以参看 ...
- PAT归纳总结——一些容易记混的概念
在刷题的过程中,有时候会遇到一些数据结构中的一些概念,如果对这些概念理解不清楚,甚至理解有误的话,就很可能把题目做错.所以,专门找出在刷题过程中出现的一些概念,以免考试的时候用到想不起来. 拓扑排序 ...
- PAT甲级 二叉树 相关题_C++题解
二叉树 PAT (Advanced Level) Practice 二叉树 相关题 目录 <算法笔记> 重点摘要 1020 Tree Traversals (25) 1086 Tree T ...
- go语言浅析二叉树
Hello,各位小伙伴大家好,我是小栈君,今天给大家带来的分享是关于关于二叉树相关的知识点,并用go语言实现一个二叉树和对二叉树进行遍历. 我们主要针对二叉树的概念,go实战实现二叉树的前序遍历.中序 ...
随机推荐
- linux之docker 安装 mysql
首先进入docker : 命令:systemctl start docker 查詢一下docker的状态: 命令:docker images 现在开始安装mysql了,第一步拉取镜像 命令:doc ...
- 后端程序员之路 26、CAP理论
可能是CAP理论的最好解释 - 西代零零发 - 博客频道 - CSDN.NEThttp://blog.csdn.net/dc_726/article/details/42784237 CAP理论 - ...
- DNS Rebinding漏洞原理
目录 SSRF过滤器设计 背景知识 DNS TTL 公网DNS服务器 DNS重绑定 自建DNS服务器 利用步骤图解 实战中的注意事项 防御 参考 DNS Rebinding 广泛用于绕过同源策略.SS ...
- 打造综合性智慧城市之朔州开发区 3D 可视化
前言 近几年,我国智慧城市建设步伐也不断加快,党中央和国务院也更加注重智慧园区的建设与发展,智慧园区建设与园区产业发展相结合,向着创新化.生态化发展,更加注重高新技术.绿色环保型等产业的发展,将管 ...
- 漏洞复现-CVE-2014-3120-ElasticSearch 命令执行漏洞
0x00 实验环境 攻击机:Win 10 靶机也可作为攻击机:Ubuntu18 (docker搭建的vulhub靶场) 0x01 影响版本 < ElasticSearch 1.2的版本 ...
- python基础学习之类的继承、魔法方法
什么是继承 即类A可以使用类B的方法,即B是A的父类,A是B的子类,AB之间是继承关系 class Father(): # 父类 def __init__(self,name,age): self. ...
- go中sync.Mutex源码解读
互斥锁 前言 什么是sync.Mutex 分析下源码 Lock 位运算 Unlock 总结 参考 互斥锁 前言 本次的代码是基于go version go1.13.15 darwin/amd64 什么 ...
- Python深入:setuptools进阶
作者:gqtcgq 来源:CSDN 原文:https://blog.csdn.net/gqtcgq/article/details/49519685 Setuptools是Python Distuti ...
- macbook/macOS下打开多个相同应用(应用多开)
1.部分应用可使用common+n快捷键.如qq:打开qq主界面后使用common+n即可新起一个qq程序. 2.在终端使用命令 open -n +程序路径.如启动多个qq : open -n /A ...
- IndentationError:unexpected indent”、“IndentationError:unindent does not match any outer indetation level”以及“IndentationError:expected an indented block Python常见错误
错误的使用缩进量 记住缩进增加只用在以:结束的语句之后,而之后必须恢复到之前的缩进格式. 经典错误,一定要注意缩进,尤其是在非界面化下环境的代码修改