PTA刷题实战:如何用C++判断一个序列是二叉搜索树的前序遍历?

张开发
2026/4/5 1:51:55 15 分钟阅读

分享文章

PTA刷题实战:如何用C++判断一个序列是二叉搜索树的前序遍历?
从PTA真题解析二叉搜索树前序序列的判定与转换策略二叉搜索树BST作为数据结构中的经典问题在各类算法考试和面试中频繁出现。PTA平台上这道搜索树判断题目要求我们验证一个序列是否构成某棵二叉搜索树或其镜像的前序遍历结果并输出对应的后序遍历序列。这看似简单的需求背后实则考察了我们对树结构的深刻理解和递归算法的灵活运用。1. 理解题目本质与二叉搜索树特性二叉搜索树之所以成为算法题中的常客关键在于它有序性与递归性的完美结合。根据题目定义标准BST任意节点的左子树仅包含严格小于该节点的值右子树包含大于或等于该节点的值镜像BST与标准BST相反左子树包含大于等于的值右子树包含严格小于的值前序遍历序列的特点是第一个元素为根节点随后是左子树和右子树的遍历结果。我们需要验证给定的序列是否符合这种结构特征。1.1 前序序列的隐含结构信息观察前序遍历序列[8,6,5,7,10,8,11]我们可以分解出根节点8序列第一个元素左子树所有连续小于8的元素[6,5,7]右子树剩余元素[10,8,11]这种分解方式正是我们解题的关键思路。通过递归地应用这种分解我们可以构建出整棵树的结构。// 伪代码前序序列分解示意 void analyzePreorder(vectorint pre, int start, int end) { if(start end) return; int root pre[start]; int left_end start; while(left_end1 end pre[left_end1] root) { left_end; } // 此时pre[start1...left_end]为左子树 // pre[left_end1...end]为右子树 }1.2 两种验证策略的比较题目提供了两种验证思路先构造再验证按照BST规则构建树然后检查其前序遍历是否匹配输入序列直接验证不显式构建树而是通过递归验证序列是否符合BST前序特征第一种方法如原始代码所示直观但效率较低需要完整构建两棵树标准BST和镜像BST。第二种方法更为高效只需遍历序列一次即可完成验证。2. 高效验证算法的实现让我们设计一个不依赖显式树结构的验证方案直接在序列上进行操作。2.1 递归验证框架bool isBSTPreorder(vectorint pre, int start, int end, bool isMirror false) { if(start end) return true; int root pre[start]; int leftEnd start; // 根据是否为镜像决定比较方式 auto compare [](int a, int b) { return isMirror ? (a b) : (a b); }; // 寻找左子树结束位置 while(leftEnd1 end compare(pre[leftEnd1], root)) { leftEnd; } // 验证右子树是否都满足相反条件 for(int i leftEnd1; i end; i) { if(compare(pre[i], root)) return false; } // 递归验证左右子树 return isBSTPreorder(pre, start1, leftEnd, isMirror) isBSTPreorder(pre, leftEnd1, end, isMirror); }2.2 后序遍历序列的生成验证通过后我们需要生成后序遍历序列。同样可以采用递归方式void getPostorder(vectorint pre, int start, int end, vectorint post, bool isMirror false) { if(start end) return; int root pre[start]; int leftEnd start; auto compare [](int a, int b) { return isMirror ? (a b) : (a b); }; while(leftEnd1 end compare(pre[leftEnd1], root)) { leftEnd; } // 后序左-右-根 getPostorder(pre, start1, leftEnd, post, isMirror); getPostorder(pre, leftEnd1, end, post, isMirror); post.push_back(root); }3. 完整解决方案与优化结合上述思路我们可以给出一个完整的解决方案#include iostream #include vector using namespace std; bool isBSTPreorder(const vectorint pre, int start, int end, bool isMirror false) { if(start end) return true; int root pre[start]; int leftEnd start; while(leftEnd1 end (isMirror ? (pre[leftEnd1] root) : (pre[leftEnd1] root))) { leftEnd; } for(int i leftEnd1; i end; i) { if(isMirror ? (pre[i] root) : (pre[i] root)) { return false; } } return isBSTPreorder(pre, start1, leftEnd, isMirror) isBSTPreorder(pre, leftEnd1, end, isMirror); } void getPostorder(const vectorint pre, int start, int end, vectorint post, bool isMirror false) { if(start end) return; int root pre[start]; int leftEnd start; while(leftEnd1 end (isMirror ? (pre[leftEnd1] root) : (pre[leftEnd1] root))) { leftEnd; } getPostorder(pre, start1, leftEnd, post, isMirror); getPostorder(pre, leftEnd1, end, post, isMirror); post.push_back(root); } int main() { int n; cin n; vectorint sequence(n); for(int i 0; i n; i) { cin sequence[i]; } vectorint post; bool isBST isBSTPreorder(sequence, 0, n-1); bool isMirrorBST isBSTPreorder(sequence, 0, n-1, true); if(isBST || isMirrorBST) { cout YES endl; getPostorder(sequence, 0, n-1, post, !isBST); for(int i 0; i post.size(); i) { if(i 0) cout ; cout post[i]; } } else { cout NO; } return 0; }3.1 性能分析与优化这种方法相比原始方案有几个优势空间效率不需要显式构建树结构节省内存时间效率每个元素只被处理一次时间复杂度O(n)代码简洁逻辑集中易于理解和维护对于PTA这类在线评测系统这些优化可以显著提高程序的运行效率和通过率。4. 常见错误与调试技巧在解决这类问题时有几个常见的陷阱需要注意4.1 边界条件处理空序列或单元素序列的处理所有元素都在左子树或右子树的极端情况重复元素的处理特别是等于根节点的情况4.2 递归深度问题对于最坏情况如完全左斜或右斜树递归深度可能达到1000层题目中N≤1000。虽然现代编译器通常能处理这种深度的递归但在某些环境下可能需要考虑非递归实现。4.3 输出格式要求PTA题目对输出格式要求严格需要注意行末不能有多余空格大小写敏感YES/NO换行符的使用调试建议在本地测试时可以添加调试输出打印递归过程帮助理解程序执行流程。例如在isBSTPreorder函数中添加当前处理的子序列范围打印。5. 扩展应用与类似问题掌握这种序列验证方法后可以解决一系列类似问题验证后序序列类似思路但根节点在序列末尾验证中序序列BST的中序序列必然是升序的根据前序和后序重建树虽然不能唯一确定但可以找出可能的树结构例如验证后序序列的代码框架bool isBSTPostorder(vectorint post, int start, int end) { if(start end) return true; int root post[end]; int leftEnd start - 1; for(int i start; i end; i) { if(post[i] root) leftEnd i; else break; } for(int i leftEnd1; i end; i) { if(post[i] root) return false; } return isBSTPostorder(post, start, leftEnd) isBSTPostorder(post, leftEnd1, end-1); }在实际刷题过程中建议将这些验证方法整理成模板便于快速应用到类似问题中。理解其核心思想比死记硬背代码更重要这样才能在面试或考试中灵活变通。

更多文章