今天是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。

  例题:1151 LCA in a Binary Tree

  思考:为什么这样写是错误的?

return left ? left: right ? right: root;

  这类问题也在PAT考试的过程中出现过,但是所考察的形式都比较简单,首先要明白大堆和小顶堆的概念,大顶堆就是根结点的值大于左子树和右子树中结点的值,小顶堆则与之相反,根结点中的value小于左子树和右子树中的值。至于,怎么构建大顶堆现在还没有遇到这样的题,之后遇到了再补上。

PAT归纳总结——关于二叉树的一些总结的更多相关文章

  1. PAT L2-011 玩转二叉树

    https://pintia.cn/problem-sets/994805046380707840/problems/994805065406070784 给定一棵二叉树的中序遍历和前序遍历,请你先将 ...

  2. PAT L2-011 玩转二叉树(二叉树层序遍历)

    给定一棵二叉树的中序遍历和前序遍历,请你先将树做个镜面反转,再输出反转后的层序遍历的序列.所谓镜面反转,是指将所有非叶结点的左右孩子对换.这里假设键值都是互不相等的正整数. 输入格式: 输入第一行给出 ...

  3. PAT 1020 Tree Traversals[二叉树遍历]

    1020 Tree Traversals (25)(25 分) Suppose that all the keys in a binary tree are distinct positive int ...

  4. PAT归纳总结——关于图的一些总结

    在刷题的过程中经常会碰到一些关于图论的问题,下面我将根据自己刷题的经验来对这些问题做一个总结. 图的表示方法 解决图论中的问题首先要解决的问题就是图的表示方法这一问题,图的表示方法主要有两种,一种使用 ...

  5. PAT归纳总结——关于模拟类问题的一些总结

    关于时间的模拟问题 在刷题的过程中碰到了一些关于时间先后顺序的模拟题目,刚开始做的时候确实挺麻烦的,今天把这类问题的解题思路来整理一下. 比较典型的有: 1017 Queueing at Bank 1 ...

  6. PAT归纳总结——关于C++输入输出格式问题的一些总结

    自从使用了C++就不再想使用C语言来刷题了,C++便捷的输入输出方式,以及一些STL库函数的使用都要比使用C语言方便的多.但是使用的时候还有一些需要注意的地方,在这篇博客中写一下.(更好的教程可以参看 ...

  7. PAT归纳总结——一些容易记混的概念

    在刷题的过程中,有时候会遇到一些数据结构中的一些概念,如果对这些概念理解不清楚,甚至理解有误的话,就很可能把题目做错.所以,专门找出在刷题过程中出现的一些概念,以免考试的时候用到想不起来. 拓扑排序 ...

  8. PAT甲级 二叉树 相关题_C++题解

    二叉树 PAT (Advanced Level) Practice 二叉树 相关题 目录 <算法笔记> 重点摘要 1020 Tree Traversals (25) 1086 Tree T ...

  9. go语言浅析二叉树

    Hello,各位小伙伴大家好,我是小栈君,今天给大家带来的分享是关于关于二叉树相关的知识点,并用go语言实现一个二叉树和对二叉树进行遍历. 我们主要针对二叉树的概念,go实战实现二叉树的前序遍历.中序 ...

随机推荐

  1. 虚拟机测试cobbler,网络安装加载最后出现 dracut:/#

    1.cobbler的几个重要概念: distro:发行版系统容,我理解为镜像来源,提供了kernel 和 initrd 文件以及repo源 profile:kickstart文件,用于定制系统,定制安 ...

  2. CVE-2019-11043-Nginx PHP 远程代码执行

    漏洞原因 Nginx 上 fastcgi_split_path_info 在处理带有 %0a 的请求时,会因为遇到换行符 \n 导致 PATH_INFO 为空.而 php-fpm 在处理 PATH_I ...

  3. CVE-2020-1938 -Tomcat-AJP任意文件读取/包含

    为什么这个漏洞被称作 Ghostcat(幽灵猫)? 这个漏洞影响全版本默认配置下的 Tomcat(在我们发现此漏洞的时候,确认其影响 Tomcat 9/8/7/6 全版本,而年代过于久远的更早的版本未 ...

  4. windows10 缺失 msvcp140.dll 解决办法

    1.问题描述 我更新完windows10 驱动后,出现计算机缺失msvcp140.dll文件,虚机和QQ都无法启动 2.解决办法 查找大量文章,最终发现通过重新安装 Visual Studio 201 ...

  5. linux_MYSQL 数据库自动备份并压缩和删除历史备份

    1. 创建shell脚本 #! /bin/bash# MySQL用户user="root"# MySQL密码userPWD="123456789"# 需要定时备 ...

  6. Go语言|类型转换和类型别名

    类型转换 同类型之间的转换 Go语言中只有强制类型转换,没有隐式类型转换.该语法只能在两个类型之间支持相互转换的时候使用. import "fmt" func main() { v ...

  7. android分析之智能指针

    智能指针是一个包装类,该类有一个指针指向真正的类对象 引用计数型智能指针,该引用计数是在应该被真正类所持有,而非包装类(智能指针) 为了方便,会将引用计数单独实现在一个类中,这样所有继承它的类都有计数 ...

  8. 关于python浮点数精度问题计算误差的原因分析

    在python中使用浮点数运算可能会出现如下问题 a = 0.1+0.2print(a) 输出的结果是 0.30000000000000004 原因如下: 出现上面的情况,主要还是因浮点数在计算机中实 ...

  9. python 常用的库

    本节大纲: 模块介绍 time &datetime模块 random os sys shutil json & picle shelve xml处理 yaml处理 configpars ...

  10. Cookie实现记住密码、自动登录

    前端代码 <form id="form" action="xxx" method="post"> <div> < ...