二叉树的递归遍历:
理解递归算法的三要素:
确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
二叉树的递归遍历问题,深度优先遍历包括:前序遍历、中序遍历和后续遍历。这个理解起来还是很轻松的。 代码如下:
前序遍历:
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
中序遍历:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
vec.push_back(cur->val); // 中
traversal(cur->right, vec); // 右
}
后序遍历:
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
vec.push_back(cur->val); // 中
}
二叉树的迭代遍历:
理解通过栈来解决迭代匹配问题:
1. 前序遍历, 先加入右孩子结点, 再加入左孩子结点 (先进后出). (根左右)
2. 后续遍历, 先加入左孩子结点, 再加入右孩子结点, 然后翻转输出结果 (左右根)
3. 中序遍历, 先遍历加入左孩子结点, 如果左孩子结点为空则弹出栈顶元素, 右孩子结点不为空则加入右孩子结点, 如果右孩子孩子结点为空则其为叶子结点,继续弹出栈顶元素 (左根右)
递归是通过函数栈来进行实现的,迭代使用栈结构来实现的,通过代码能够更好的理解:
代码:
/**
* Definition for a binary tree node.
* 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> preorderTraversal(TreeNode* root) {
vector<int> nums;
stack<TreeNode*> stackin;
stackin.push(root);
while(!stackin.empty()){
root = stackin.top();
stackin.pop();
if(root != nullptr){
nums.push_back(root->val);
if(root->right != nullptr){
stackin.push(root->right);
}
if(root->left != nullptr){
stackin.push(root->left);
}
}
}
return nums;
}
// 后序遍历
vector<int> postorderTraversal(TreeNode* root) {
vector<int> nums;
stack<TreeNode*> stackin;
if(root == nullptr) return nums;
stackin.push(root);
while(!stackin.empty()){
root = stackin.top();
stackin.pop();
nums.push_back(root->val);
if(root->left != nullptr) stackin.push(root->left);
if(root->right != nullptr) stackin.push(root->right);
}
reverse(nums.begin(), nums.end()); // 翻转根右左, 变成左右根
return nums;
}
};
第一次对中序遍历的理解:
因为中序遍历不能满足同时遍历和输出, 所以我们需要一个遍历指针:
第一次根据自己的画图理解写出了如下代码:
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> nums;
stack<TreeNode*> st;
if(root == nullptr) return nums;
TreeNode* cur = root;
st.push(cur);
while(!cur || !st.empty()){
if(cur->left != nullptr){
cur = cur->left;
st.push(cur);
}
else{
//两次删除出现问题
nums.push_back(cur->val);
st.pop();
cur = st.top();
nums.push_back(cur->val);
st.pop();
if(cur->right != nullptr){
cur = cur->right;
st.push(cur);
}
}
}
return nums;
}
};
结果上述代码中在两次删除出现了问题, 最后一个只剩下一个右结点在栈中, 但是却删除了两次, 导致产生了空删除错误!!后续修改也没有解决这个问题.
下面是第二次理解了Carl的代码后写出来的:
class Solution {
public:
// 中序遍历
vector<int> inorderTraversal(TreeNode* root) {
vector<int> nums;
stack<TreeNode*> st;
TreeNode* cur = root;
while(cur != nullptr || !st.empty()){
if(cur != nullptr){
st.push(cur);
cur = cur->left;
}
else{
cur = st.top();
st.pop();
nums.push_back(cur->val);
cur = cur->right;
}
}
return nums;
}
};
非常的巧妙, 非常的简洁明了.
二叉树的统一迭代二刷的时候再来理解吧
二叉树的遍历理解了递归和迭代遍历的方法, 用函数栈来递归遍历, 用栈来迭代遍历, 两种方法都要掌握.