二叉树的遍历规则:
上树中,前中后序遍历顺序如下:
- 前序遍历(中左右):5 4 1 2 6 7 8
- 中序遍历(左中右):1 4 2 5 7 6 8
- 后序遍历(左右中):1 2 4 7 8 6 5
递归,对于递归,要明确三要素:
-
确定递归函数的参数和返回值:
确定哪些参数是递归的过程中需要处理的,那么就在递归函数里加上这个参数, 并且还要明确每次递归的返回值是什么进而确定递归函数的返回类型。 -
确定终止条件(base case):
写完了递归算法, 运行的时候,经常会遇到栈溢出的错误,就是没写终止条件或者终止条件写的不对,操作系统也是用一个栈的结构来保存每一层递归的信息,如果递归没有终止,操作系统的内存栈必然就会溢出。 -
确定单层递归逻辑:
确定每一层递归需要处理的信息。在这里也就会重复调用自己来实现递归的过程。
1. 二叉树深度优先(DFS)遍历
1.1 前序遍历
遍历顺序为中左右,leetcode传送门:144. 二叉树的前序遍历
- 递归算法:
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
if(root == nullptr) return ret;
ret.push_back(root->val); //中
preorderTraversal(root->left); //左
preorderTraversal(root->right); //右
//preorderTraversal(root->left);
return ret;
}
private:
vector<int> ret;
};
- 迭代算法:
/*非模板算法*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if(root == nullptr) return ret;
stack<TreeNode*> stk;
stk.push(root);
while(!stk.empty()) {
TreeNode* root = stk.top(); //中
stk.pop();
ret.push_back(root->val);
if(root->right != nullptr) stk.push(root->right); //右(空子节点不入栈)
if(root->left != nullptr) stk.push(root->left); //左(空子节点不入栈)
}
}
return ret;
};
/*模板算法*/
/*也称为标记法,就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。*/
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> ret;
if(root == nullptr) return ret;
stack<TreeNode*> stk;
stk.push(root);
while(!stk.empty()) {
TreeNode* node = stk.top();
stk.pop(); //为了避免重复操作,将此时的栈顶元素(非空/空)弹出,若有需要再次压栈
if(node != nullptr) {
if(node->right != nullptr) stk.push(node->right); //添加右节点,空节点不入栈
if(node->left != nullptr) stk.push(node->left); //添加左节点,空节点不入栈
stk.push(node); //中节点访问过,但是未作处理,压栈并在下一步中压入空指针标记
stk.push(nullptr); //放入空指针作为标记
} else { //只有遇到空节点时,才将数据放入结果集中
node = stk.top(); //从栈中重新取出元素
stk.pop(); //弹出取出的元素
ret.push_back(node->val); //加入到结果集
}
}
return ret;
}
};
1.2 中序遍历
遍历顺序为左中右,leetcode传送门:94. 二叉树的中序遍历
- 递归算法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
if(root == nullptr) return ret;
preorderTraversal(root->left); //左
ret.push_back(root->val); //中
preorderTraversal(root->right); //右
return ret;
}
private:
vector<int> ret;
};
- 迭代算法
/*非模板,第二版效率比第一版要高*/
/*第二版减少了中间变量cur和中间的空指针比较*/
/*version1*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
stack<TreeNode*> stk;
TreeNode* cur = root;
while(!stk.empty() || cur != nullptr) {
if(cur != nullptr) { //若cur非空,则说明还有左子树没有遍历完,将当前节点压栈,并指向左节点
stk.push(cur);
cur = cur->left;
} else { //若cur为空,则左子树遍历完了,开始回溯,左节点,父节点,并将值加入结果集,再指向右节点
cur = stk.top();
stk.pop();
ret.push_back(cur->val);
cur = cur->right;
}
}
return ret;
}
};
/*version2*/
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
stack<TreeNode*> stk;
while(!stk.empty() || root != nullptr) {
while(root != nullptr) {
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
ret.push_back(root->val);
root = root->right;
}
return ret;
}
};
/*模板算法*/
class Solution {
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
if(root == nullptr) return ret;
stack<TreeNode*> stk;
stk.push(root);
while(!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
if(node != nullptr) {
if(node->right != nullptr) stk.push(node->right); //右
stk.push(node); //中
stk.push(nullptr);
if(node->left != nullptr) stk.push(node->left); //左
} else {
node = stk.top();
stk.pop();
ret.push_back(node->val);
}
}
return ret;
}
};
1.3 后序遍历
遍历顺序为左右中,leetcode传送门:145. 二叉树的后序遍历
- 递归算法
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
if(root == nullptr) return ret;
postorderTraversal(root->left);
postorderTraversal(root->right);
ret.push_back(root->val);
}
private:
vector<int> ret;
}
- 迭代算法
①前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。
②模板算法。
/*①*/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
stack<TreeNode*> stk;
stk.push(root);
while(!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
if(node == nullptr) continue;
ret.push_back(node->val);
stk.push(node->left);
stk.push(node->right);
}
//return ret;
return reverseVector(ret);
}
private:
vector<int> reverseVector(vector<int>& vec) {
vector<int> ret;
for(vector<int>::reverse_iterator iter = vec.rbegin(); iter != vec.rend(); iter++)
ret.push_back(*iter);
return ret;
}
};
/*模板算法*/
class Solution {
vector<int> inorderTraversal(TreeNode* root) {
vector<int> ret;
if(root == nullptr) return ret;
stack<TreeNode*> stk;
stk.push(root);
while(!stk.empty()) {
TreeNode* node = stk.top();
stk.pop();
if(node != nullptr) {
stk.push(node); //中
stk.push(nullptr);
if(node->right != nullptr) stk.push(node->right); //右
if(node->left != nullptr) stk.push(node->left); //左
} else {
node = stk.top();
stk.pop();
ret.push_back(node->val);
}
}
return ret;
}
};
2. 二叉树广度优先(BFS)遍历
2.1 层序遍历
leetcode传送门:102. 二叉树的层序遍历
- 递归实现:
①利用depth变量记录当前在第几层(从0开始),进入下层时depth + 1;
②如果depth >= vector.size()说明这一层还没来过,这是第一次来,所以得扩容;
③因为是前序遍历,中-左-右,对于每一层来说,左边的肯定比右边先被遍历到,实际上后序中序都是一样的
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
pre(root, 0, ret);
return ret;
}
private:
void pre(TreeNode* root, int depth, vector<vector<int>>& ret) {
if(root == nullptr) return;
if(depth >= ret.size())
ret.push_back(vector<int> {});
ret[depth].push_back(root->val);
pre(root->left, depth+1, ret);
pre(root->right, depth+1, ret);
}
};
- 迭代实现:
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> ret;
if(root == nullptr) return ret;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()) {
int eleNums = q.size();
vector<int> midRet;
for(int i=0; i<eleNums; ++i) {
TreeNode* node = q.front();
q.pop();
midRet.push_back(node->val);
if(node->left != nullptr) q.push(node->left);
if(node->right != nullptr) q.push(node->right);
}
ret.push_back(midRet);
}
return ret;
}
};