二叉树的遍历——递归和迭代
许久没有用到二叉树,也是有些生疏了,虽然写递归没问题,但迭代还是有点难度。
统一节点定义:
struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode() : val(0), left(nullptr), right(nullptr) {} TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} };
分递归和迭代排版吧。
【递归】
前序遍历:根左右
class Solution { public: vector<int> ans; vector<int> preorderTraversal(TreeNode *root) { if (!root) return ans; ans.emplace_back(root->val); if (root->left) preorderTraversal(root->left); if (root->right) preorderTraversal(root->right); return ans; } };
中序遍历:左根右
class Solution { public: vector<int> ans; vector<int> inorderTraversal(TreeNode *root) { if (!root) return ans; if (root->left) inorderTraversal(root->left); ans.emplace_back(root->val); if (root->right) inorderTraversal(root->right); return ans; } };
后序遍历:左右根
class Solution { public: vector<int> ans; vector<int> postorderTraversal(TreeNode *root) { if (!root) return ans; if (root->left) postorderTraversal(root->left); if (root->right) postorderTraversal(root->right); ans.emplace_back(root->val); return ans; } };
----------------------------------------------分割线------------------------------------------
【迭代】
前序:写了两个版本,一个是自己直接写出来的(更好理解),一个是基于二叉树迭代模板
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { stack<TreeNode *> st; vector<int> ans; st.push(root); //刚开始用的是vector想用下标,但写着变成了层序遍历 //前序遍历还是得用栈来模拟 while (st.size() && st.top()) { TreeNode *n = st.top(); st.pop(); ans.emplace_back(n->val); //后进先出,所以得是右左 if (n->right) st.push(n->right); if (n->left) st.push(n->left); } return ans; } };
class Solution { public: vector<int> preorderTraversal(TreeNode *root) { vector<int> ans; stack<TreeNode *> st; TreeNode *n = root; while (!st.empty() || n != nullptr) { while (n != nullptr) { ans.emplace_back(n->val); st.emplace(n); n = n->left; } n = st.top()->right; st.pop(); } return ans; } };
中序:
tips:前序是在 循环左子节点 时候就直接放入,中序是 循环左子节点 后 的每一次找右子节点 时候放入。
class Solution { public: vector<int> inorderTraversal(TreeNode *root) { stack<TreeNode *> st; vector<int> ans; TreeNode *n = root; while (!st.empty() || n != nullptr) { while (n != nullptr) { st.emplace(n); n = n->left; } ans.emplace_back(st.top()->val); n = st.top()->right; st.pop(); } return ans; } };
后序:
也是最难的,我不熟练的情况下空想做了一个小时,最好还是打个草稿画一画二叉树,更方便构思逻辑
重点理解顺序是“左右根”,
在循环左子节点入栈后,在对当前(根)节点是否出栈,需要考虑右子节点的情况
1.没有右子节点,那么直接出栈
2.有右子节点,分是否已经遍历过右子节点:
(先说逻辑)
a.如果已经遍历,那么也是直接出栈
b.如果还未遍历,那么将 当前节点 转移到 右子节点 即可,不用入栈一次的原因是,在转移到 该节点 的 右子节点 以后,所执行的操作是 先 循环左子节点 入栈,是会将 当前节点(也就是右子节点) 一并入栈的。
那么问题变成了,如何考虑是否已经遍历的问题,answer——添加一个pre标记变量:在每一次出栈的时候,用pre存一下当前的出栈元素即可。
class Solution { public: vector<int> postorderTraversal(TreeNode *root) { vector<int> ans; stack<TreeNode *> st; TreeNode *n = root, *pre = nullptr; while (!st.empty() || n != nullptr) { while (n != nullptr) { st.emplace(n); n = n->left; } n = st.top()->right; if (n == nullptr || pre == n) { n = nullptr; pre = st.top(); ans.emplace_back(st.top()->val); st.pop(); } } return ans; } };
over,虽然是很基础且很早就学过的数据结构,但是还是生疏了,记录一下。
另外补充一个层序遍历:(这种处理在图论里也有用到)
class Solution { public: vector<vector<int>> levelOrder(TreeNode *root) { vector<vector<int>> ans(0); queue<TreeNode *> q; if (root == nullptr) return ans; q.push(root); int n, id = -1; while (q.size()) { n = q.size(); id++; ans.emplace_back(vector<int>(0)); for (int i = 0; i < n; ++i) { TreeNode *n = q.front(); q.pop(); ans[id].emplace_back(n->val); if (n->left) q.push(n->left); if (n->right) q.push(n->right); } } return ans; } };

浙公网安备 33010602011771号