#数据结构与算法学习笔记#PTA11:先序遍历+中序遍历转后序遍历/二叉树非递归遍历/二叉树栈遍历(JAVA)

利用栈模拟二叉树,结合先序遍历和中序遍历,实现非递归后序遍历。关键点在于理解二叉树非递归遍历,通过先序和中序得到后序遍历序列。通过特定思路,避免递归构建二叉树,直接输出后序遍历。在实现过程中注意字符串处理细节,如使用equals而非==比较,以及处理两位数节点编号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2018.4.18

这道题说的是利用栈来模拟二叉树,通过栈的出入情况,模拟出这棵二叉树,并后序遍历打印这棵二叉树。

这题关键点在于理解二叉树的非递归遍历,能够看得出入栈顺序就是二叉树的先序遍历序列,出栈顺序就是二叉树的中序遍历序列。理解了这点这道题就转换成已知先序遍历+中序遍历,打印二叉树的后序遍历的问题了。

有两个思路:1.根据先序遍历和中序遍历先构建一棵二叉树,再后序遍历打印这棵树;2.直接通过先序遍历和中序遍历得出后序遍历序列。其实能够做出思路1,想想肯定能做出思路2。本题我采用的是思路2。

思路概述:先序遍历的第一个结点,在中序遍历中的位置i,i对应根节点,中序遍历中i左半部分的元素即为根节点左子树,右半部分元素即为根节点右子树。通过先序遍历先递归左子树的根节点,再递归右子树的根节点,直到递归到叶节点时打印,在递归函数出栈时依次打印根节点,就可以输出该二叉树的后序遍历。

这题其实很简单,但是我碰上了两个大坑,1.在验证输入字符串时,不能用==,要用equals;2.注意N大于10时字符串为2位数,不能用int data = str[1].charAt(0) - '0';,要用int data = Integer.parseInt(str[1]);,否则N>10输出结果不正确。为了查出第二个大坑花了两个晚上,之前因为十位以内的输入都正确了,输入push等又太麻烦,因此调试的时候都直接跳过了输入步骤直接给前序遍历和中序遍历赋值查输出结果。查到错之后感慨万分,debug这种事还是得踏踏实实地来啊。(代码中和文后附一个自己手撕的N=30的测试样例)

 

An inorder binary tree traversal can be implemented in a non-recursive way with a stack. For example, suppose that when a 6-node binary tree (with the keys numbered from 1 to 6) is traversed, the stack operations are: push(1); push(2); push(3);

### 关于二叉树非递归遍历PTA题目解题思路 #### 创建二叉树并实现非递归遍历框架 为了完成二叉树非递归遍历,在编写代码之前应当构建一棵用于测试的二叉树。对于非递归遍历而言,通常借助来模拟函数调用的过程。 ```c++ struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; ``` #### 实现序遍历(根-左-右) 在序遍历中,访问顺是从根结点开始,接着是左子树最后才是右子树。通过显式的使用可以很容易地换成迭代版本[^1]: ```cpp void preOrderTraversal(TreeNode* root, vector<int>& result){ stack<TreeNode*> s; while (root || !s.empty()){ while(root){ result.push_back(root->val); // 访问当前节点 s.push(root); root = root->left; // 移动到左侧孩子 } if(!s.empty()){ root = s.top(); s.pop(); root = root->right; // 向右侧孩子 } } } ``` ####序遍历(左-根-右)的非递归方法 中序遍历时,会优处理所有的左边分支直到最底部,再回溯过程中依次输出各个节点的数据值,并向右边继续相同的操作: ```cpp void inorderTraversal(TreeNode* root,vector<int> &result){ std::stack<TreeNode*> stk; while (!stk.empty() || root != nullptr) { if (root != nullptr) { stk.push(root); root = root->left; } else { root = stk.top(); stk.pop(); result.push_back(root->val); // 输出中间节点 root = root->right; } } } ``` #### 后续遍历(左-右-根)采用标记法 由于后序遍历需要等到左右两个子树都已经被访问过后才能打印根节点的信息,因此这里引入了一个额外的状态变量`flag`用来区分是否已经完成了两侧孩子的访问[^4]: ```cpp struct Info{ TreeNode *node; bool flag=false; }; void postorderTraversal(TreeNode* root, vector<int>& res){ if (!root) return; stack<Info> st; st.emplace(Info{root}); while (!st.empty()) { auto& top = st.top(); if(top.flag){ res.push_back(top.node->val); st.pop(); }else{ top.flag=true; if(top.node->right!=nullptr) st.emplace(Info{top.node->right}); if(top.node->left!=nullptr) st.emplace(Info{top.node->left}); } } } ``` 上述三种方式分别展示了如何基于不同的遍历策略设计对应的算法逻辑,这些技巧不仅适用于解决特定平台上的编程挑战问题,也能够帮助理解更复杂的图论或者其他高级主题中的应用实例。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值