递归遍历三要素
-
确定递归函数的参数和返回值: 确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。
-
确定终止条件: 写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。
-
确定单层递归的逻辑: 确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
迭代遍历
这里说的迭代遍历本质上来看和递归没有区别,都是利用栈来是实现的。递归就是不停调用自己,直到满足条件,每次函数调用都会将局部变量、参数等压入栈中,每次返回的时候又从栈中弹出数据,所以迭代遍历就是模拟压栈和弹栈的过程。对于不同的遍历顺序,压栈和弹栈的顺序是不一样的,所以用栈也可以是实现二叉树的前后中序遍历了。
LeetCode 144.二叉树的前序遍历
链接:link
递归遍历
- 确定递归函数的参数和返回值:因为要打印出前序遍历节点的数值,所以参数里需要传入vector在放节点的数值,除了这一点就不需要在处理什么数据了也不需要有返回值,所以递归函数返回类型就是void,代码如下:
void preOrder(TreeNode* root, vector<int>& v)
- 确定终止条件:在递归的过程中,如何算是递归结束了呢,当然是当前遍历的节点是空了,那么本层递归就要要结束了,所以如果当前遍历的这个节点是空,就直接return,代码如下:
if(root == nullptr) return;
- 确定单层递归的逻辑:前序遍历是中左右的循序,所以在单层递归的逻辑,是要先取中节点的数值,代码如下:
v.push_back(root->val);
preOrder(root->left, v);
preOrder(root->right, v);
完整代码如下:
class Solution {
public:
void preOrder(TreeNode* root, vector<int>& v){
if(root == nullptr) return;
v.push_back(root->val);
preOrder(root->left, v);
preOrder(root->right, v);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> v;
preOrder(root, v);
return v;
}
};
迭代遍历
前序遍历是中左右,每次先处理的是中间节点,那么先将根节点放入栈中,然后将右孩子加入栈,再加入左孩子。为什么要先加入 右孩子,再加入左孩子呢? 因为这样出栈的时候才是中左右的顺序。
完整代码如下:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> sk;
sk.push(root);
vector<int> v;
if(root == nullptr) return v;
while(!sk.empty()){
TreeNode* node = sk.top();
sk.pop();
v.push_back(node->val);
if(node->right) sk.push(node->right);
if(node->left) sk.push(node->left);
}
return v;
}
};
LeetCode 94.二叉树的中序遍历
链接:link
递归遍历
中序遍历遍历的实现思路和前序遍历一致,只是遍历顺序为左中右。代码如下:
class Solution {
public:
void inOrder(TreeNode* root, vector<int>& v){
if(root == nullptr) return;
inOrder(root->left, v);
v.push_back(root->val);
inOrder(root->right, v);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> v;
inOrder(root, v);
return v;
}
};
迭代遍历
中序遍历是左中右,先访问的是二叉树顶部的节点,然后一层一层向下访问,直到到达树左面的最底部,再开始处理节点(也就是在把节点的数值放进v数组中)。那么在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
stack<TreeNode*> sk;
vector<int> v;
if(root == nullptr) return v;
TreeNode* cur = root;
while(!sk.empty() || cur){
while(cur){
sk.push(cur);
cur = cur->left;
}
cur = sk.top();
sk.pop();
v.push_back(cur->val);
cur = cur->right;
}
return v;
}
};
LeetCode 145.二叉树的后序遍历
链接:link
递归遍历
后序遍历遍历的遍历顺序为左右中。代码如下:
class Solution {
public:
void postOrder(TreeNode* root, vector<int>& v){
if(root == nullptr) return;
postOrder(root->left, v);
postOrder(root->right, v);
v.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> v;
postOrder(root, v);
return v;
}
};
迭代遍历
先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了。代码如下:
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> sk;
vector<int> v;
if(root == nullptr) return v;
sk.push(root);
while(!sk.empty()){
TreeNode* node = sk.top();
sk.pop();
v.push_back(node->val);
if(node->left) sk.push(node->left);
if(node->right) sk.push(node->right);
}
reverse(v.begin(), v.end());
return v;
}
};