叫先根遍历、中根遍历、后根遍历,更好些,先中后是表达根的位置。
- 先根遍历:先访问
根节点
,然后访问左子树, 最后访问右子树。 - 中根遍历:先访问左子树,然后访问
根节点
, 最后访问右子树。 - 后根遍历:先访问左子树,然后访问右子树, 最后访问
根节点
。 - 层次遍历:按照树的深度从根节点开始一层一层的访问。
先根、中根、后根遍历,有三种解法,递归方式、非递归方式,以及Morris遍历。
二叉树数据结构定义如下:
#include <vector>
#include <stack>
#include <algorithm>
#include <stdio.h>
#include <queue>
using namespace std;
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode() : val(0), left(nullptr), right(nullptr) {}
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
};
递归实现
递归遍历实现最简单:
// 先根遍历递归写法
void preorderTraversal(TreeNode* root) {
if (root != nullptr) {
printf("%d\t", root->val);
preorderTraversal(root->left);
preorderTraversal(root->right);
}
}
// 中根遍历递归写法
void inorderTraversal(TreeNode* root) {
if (root != nullptr) {
inorderTraversal(root->left);
printf("%d\t", root->val);
inorderTraversal(root->right);
}
}
// 后根遍历递归写法
void postorderTraversal(TreeNode* root) {
if (root != nullptr) {
postorderTraversal(root->left);
postorderTraversal(root->right);
printf("%d\t", root->val);
}
}
```对于对于对于
### 非递归实现
非递归遍历实现需要用到栈,实际上递归本质也是栈,应为递归函数调用,也是将参数和变量入栈的过程。将其过程模拟出来:开始时,我们只知道树的根节点,因此从上往下进行访问。每次将根节点入栈。进入左节点时 root=root->left;进入右节点时root=root->right。到达叶子节点,则进行退栈操作,再进入另一分支, 当栈为空,root也为nullptr,遍历完毕。
##### 先根遍历和中根遍历
以前序遍历为例子,首先遍历根节点,打印根节点;然后进入递归,将根节点入栈;先序遍历是根左右,因此,先遍历左子树,root = root->left。打印root;然后依旧继续遍历左子树,直到root为空,此时开始退栈,遍历右子树,root = root->right。进入右子树后,先打印root,然后入栈,又是继续先往左递进...
中根遍历和先根遍历只有一点区别,先根遍历是先打印根;因此先打印再入栈;中根遍历时先打印左节点然后打印根,因此放到退栈时再打印。
```cpp
void preorderTraversal(TreeNode* root) {
stack<TreeNode*> stk;
while (!stk.empty() || root != nullptr) {
while (root != nullptr) {
printf("%d\t", root->val);
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
root = root->right;
}
}
void inorderTraversal(TreeNode* root) {
stack<TreeNode*> stk;
while (root != nullptr || !stk.empty()) {
while (root != nullptr) {
stk.push(root);
root = root->left;
}
root = stk.top();
stk.pop();
printf("%d\t",root->val);
root = root->right;
}
}
对于后根遍历,比较麻烦一些,先根和中根,根节点和递进节点时是连着的,而对后根来说却不是这样。 我们得运用下逆向思维:后根遍历顺序是左、右、根,我们反过来变成根、右、左,这样不就和先根遍历完全对称了么?然后将结果逆序一下,不就是得到后根遍历的答案了么?
void postorderTraversal(TreeNode *root) {
stack<TreeNode *> stk;
vector<int> rev;
while (root != nullptr || !stk.empty()) {
while (root != nullptr) {
stk.push(root);
rev.push_back(root->val);
root = root->right;
}
root = stk.top();
stk.pop();
root = root->left;
}
for(auto it = rev.rbegin(); it != rev.end(); it++) {
print("%d\t", *it);
}
}
层次遍历
二叉树的层次遍历要用到队列,出队时,将左右子节点都入队。
void levelTraversal(TreeNode* root) {
queue<TreeNode*> q;
if(root != nullptr) {
q.push(root);
while(!q.empty()) {
root = q.front();
q.pop();
printf("%d\t", root->val);
if(root->left != nullptr) {
q.push(root->left);
}
if(root->right != nullptr) {
q.push(root->right);
}
}
}
}