二叉树的递归遍历非常简单,其基本模型如下
前序遍历
前序遍历非常适用于由上往下进行访问的情景,比如求根节点到某一个结点沿途的权重和,那么用前序遍历是很好的。既可以带着一个变量进行遍历(利用引用或者外部变量),也可以通过设置返回值,然后最后进行综合,从而达到统一的结果。当然也可以在访问完,遍历完左右子树后,再根据结果来进行重新调整。(如第三个例子)
有时候为了记录上一个结点的信息对下一个结点的影响,我们会构造一个辅助函数,增加一个接口来提供信息,从而改变下一个结点,比如visit(Tree T,int x);
/*普通的前序遍历*/
void visit(TreeNode* T) {
}
void trans(TreeNode* root){
visit(root);
trans(root->left);
trans(root->right);
}
/*带返回值的前序遍历,返回值会对结果产生效果*/
void visit(TreeNode* T) {
}
int trans(TreeNode* root){
visit(root);
int res1=trans(root->left);
int res2=trans(root->right);
return res1+res2;
}
/*例3*/
void visit(TreeNode* T) {
}
int trans(TreeNode* root){
visit(root);
int res1=trans(root->left);
int res2=trans(root->right);
visit2(root,res1,res2);//遍历完之后再重新调整
return root->val;
}
中序遍历
中序遍历由于它的顺序的特殊性,在搜索树的遍历中,如果对它进行中序遍历,得到的就是一个从小到大的序列。而如果进行逆中序遍历,得到的就是一个从大到小的顺序。另外,我的体会是,中序遍历是一种左右分割式的遍历方式,就是如果当它访问一个点的时候,那么它的左边的所有结点都已经遍历完了(这看起来像废话,但是非常重要)
简单来说,中序遍历就是一种从左往右的遍历方式,将整棵树分成了左右两半。
void visit(TreeNode* T) {
}
void trans(TreeNode* root){
trans(root->left);
visit(root);
trans(root->right);
}
后序遍历
后序遍历与前序遍历相反,它更多的是先遍历左右子树,然后再对当前结点进行处理。典型的应用比如将二叉树改造为所有的子树结点的和 这个时候,根结点的值必须要先确定了子结点的值,然后再确定根的值,这种逻辑是一层一层往上发展的。
void visit(TreeNode* T) {
}
void trans(TreeNode* root){
trans(root->left);
trans(root->right);
visit(root);
}
另外补充一张leetcode上的图
非递归后序遍历
下面的代码来自于Knox
https://leetcode-cn.com/problems/binary-tree-postorder-traversal/solution/er-cha-shu-de-hou-xu-bian-li-by-leetcode/
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> ret;
stack<TreeNode *> path;
unordered_set<TreeNode *> _set; // 记录已经访问的结点
if (root) path.push(root);
while (!path.empty()) {
auto node = path.top();
bool leftVisited = true, rightVisited = true;
// 左右结点判断先后顺序不能互换,因为需要先把右结点放进 stack中
if (node->right && _set.find(node->right) == _set.end()) {
rightVisited = false;
path.push(node->right);
}
if (node->left && _set.find(node->left) == _set.end()) {
leftVisited = false;
path.push(node->left);
}
if (leftVisited && rightVisited) { // 左右结点已经访问过了,才可以访问当前结点
ret.push_back(node->val);
_set.insert(node);
path.pop(); // 访问过了,从path中移除
}
}
return ret;
}
};