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