打印二叉树特定节点x的所有祖先节点

算法思路

二叉树的后序遍历顺序为左、右、根,递归写法为

postOrderTraversal(root->left);
postOrderTraversal(root->right);
cout<<root->val; 

递归的边界为

if(!root){
	return;
}

可以很清楚的看出递归栈的变化,即

树从根结点向左到叶子结点,沿途结点全部入栈,从栈中出栈访问结点p->left后回溯到其父结点p,再对以p->right为根结点的子树重复上述过程,当p结点的左右子树遍历完成后p出栈再回溯到p节点的父节点......以此类推直至最后root->right遍历完成后回溯到root节点递归结束。

把递归栈的变化模拟出来就可以知道:当访问到结点x时,要么x的右子树还未开始遍历入栈,要么x的所有子结点都已经访问出栈完成。无论哪种情况,当访问到x时的栈内剩余结点均为x的祖先结点。

维护一个栈,显示地将递归过程中栈的变化模拟出来。

这里我们要注意后序遍历跟前中序遍历的区别。由于前中序遍历根结点的访问都是在右子结点之前的,因此前中序遍历在入栈子树右子结点时根结点是直接出栈的。后序遍历的根结点的访问次序在最后,因此对于每棵子树根结点只有在以下两种情况才会出栈

  • 无右子结点
  • 右子结点已访问完成

因此每次向上回溯时要判断根结点是否应该访问出栈,使用指针prev记录上次访问结点,以此来判断子树根结点的右子结点是否已经访问。即

root->right==nullptr || root->right == prev

算法代码

// 打印x节点的所有祖先节点
void PrintAncestor(TreeNode* root, int x) {
    stack<TreeNode*> stk;
    TreeNode* prev;
    while(root || !stk.empty()) {
        while(root && root->val != x) {
            stk.push(root);
            root = root->left;
        }
        if(root==nullptr) {  
            root = stk.top();
            stk.pop();
        }
        // 找到x后直接输出当前栈中所有节点算法结束
        if(root->val == x) {
            while(!stk.empty()) {
                cout<<stk.top()->val<<" ";
                stk.pop();
            }
	    return;
        }
        if(root->right == nullptr || root->right == prev) {
            prev = root;
            root = nullptr;
        }
        else {
            stk.push(root);
            root = root->right;
        }
    }
}

ps:以上仅为个人理解,如有错误之处还请批评指正

posted @ 2021-11-22 12:08  能再说一次晚安吗  阅读(187)  评论(0)    收藏  举报