二叉树遍历算法1:前序遍历、中序遍历与后续遍历

二叉树是一种非常常见的数据结构,也是面试中经常问到的数据结构。二叉树常见的遍历方式分为:前序遍历、中序遍历以及后序遍历。其中每种遍历遍历又可以分为递归遍历、以及非递归遍历。
二叉树的基本数据结构如下:

struct TreeNode {
	int val;
	TreeNode* left;
	TreeNode* right;
};

1、前序遍历

递归遍历非常简单:

void PreOrderTraveral(TreeNode* root) {
    if (root) {
        cout << root->val << endl; // 访问根节点
        PreOrderTraveral(root->left); // 遍历左子树
        PreOrderTraveral(root->right); // 遍历右子树
    }
}

前序非递归遍历需要借助stack数据结构:

void PreOrderTraveral(TreeNode* root) {
    stack<TreeNode*> st;
    while (!st.empty() || root) {
        while (root) {
            cout << root->val << endl; // 访问节点
            st.push(root);
            root = root->left;
        }
        root = st.top();
        st.pop();
        root  = root->right; // 转移到右子树遍历
    }
}

2、 中序遍历

递归中序非常简单:

void InOrderTraveral(TreeNode* root) {
    if (root) {
        InOrderTraveral(root->left); // 遍历左子树
        cout << root->val << endl; // 访问根节点
        InOrderTraveral(root->right); // 遍历右子树
    }
}

中序非递归遍历和前序非递归遍历差不多,只是访问节点的时机不同:

void InOrderTraveral(TreeNode* root) {
    stack<TreeNode*> st;
    while (!st.empty() || root) {
        while (root) {
            st.push(root);
            root = root->left;
        }
        root = st.top();
        st.pop();
        cout << root->val << endl; // 访问节点
        root  = root->right; // 转移到右子树遍历
    }
}

3、后序遍历

递归后序遍历:

void PostOrderTraveral(TreeNode* root) {
    if (root) {
        PostOrderTraveral(root->left); // 遍历左子树
        PostOrderTraveral(root->right); // 遍历右子树
        cout << root->val << endl; // 访问根节点
    }
}

非递归后序遍历写法,相对非递归前序遍历和中序遍历来说,稍微麻烦一点:
后续遍历访问的顺序为:左子树 -> 右子树 -> 根节点。因此只有左子树和右子树都遍历了,才能最后访问根节点。一个关键的问题是:如何判断左子树和右子树都遍历了?可以分为下面几种情况来讨论:

  • 左子树和右子树都为空,那么可以直接访问当前节点
  • 右子树为空,左子树不为空,前一个访问的节点为当前节点的左节点,那么可以访问该节点
  • 右子树不为空(左子树可以为空,也可以不为空),并前一个访问的节点为当前节点的右节点

因此,需要判断左右子树的情况以及记录上一个访问的节点:

void PostOrderTraversal(TreeNode* root) {
    stack<TreeNode*> st;
    if (root) {
        st.push(root);
    }
    TreeNode* curNode = nullptr;
    TreeNode* last = root; // 表示上一个访问的节点
    while (!st.empty()) {
        curNode = st.top();   // 观察栈顶节点
        if (curNode->left == nullptr && curNode->right == nullptr  // 左子树和右子树都为空
            || curNode->right == nullptr && last == curNode->left  // 右子树为空,上一个访问的节点为当前节点的左节点
            || last ==  curNode->right  // 上一个访问的节点为当前节点的右节点
        ) {
            cout << curNode->val << endl;
            last = curNode;
            st.pop();
        }
        else {
            // 注意这里的入栈顺序,要先入栈右节点,再入栈左节点
            if (curNode->right) {
                st.push(curNode->right); 
            }
            if (curNode->left) {
                st.push(curNode->left);
            }
        }
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值