LeetCode 二叉树的遍历:前中后(链表实现)
一、前序遍历(Preorder Traversal)
- 先根节点 -> 左节点 -> 右节点
- 思路:每次出栈栈顶元素(一定是某一棵子树的根节点),再将其右孩子和左孩子依次入栈
- 步骤如下:
-
- 先将
root
压栈 while
循环(退出条件:栈非空stack.empty()==0
)- 将栈顶元素出栈
pop(stack.top())
- 将栈顶元素储存到
vector<int>
中,vector<int>.push_back()
- 依次将出栈元素的右孩子和左孩子节点压栈
p->right != nullptr -> stack.push(p->right)
p->left != nullptr -> stack.push(p->left)
- 先将
代码如下:
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
// 首先定义二叉树结构体
struct Binary_Tree {
Binary_Tree* left_child;
Binary_Tree* right_child;
int data;
// 构造函数
Binary_Tree(int x) :data(x), left_child(nullptr), right_child(nullptr) {};
};
class Binary_Tree_Preorder_Traversal {
vector<int> Preorder_Traserval(Binary_Tree* root) {
vector<int> result; // 定义向量接收遍历结果
stack<const Binary_Tree*> s; // 定义栈进行二叉树的遍历
if(root != nullptr)s.push(root);
while (!s.empty()) {
const Binary_Tree* p = s.top();
s.pop();
result.push_back(p->data);
if (p->right_child != nullptr) s.push(p->right_child);
if (p->left_child != nullptr) s.push(p->left_child);
}
return result;
}
};
二、中序遍历
- 先左孩子 -> 根节点 -> 右孩子
- 先沿着左结点一直遍历到底,再不断出栈
- 如果有右孩子,则在沿着右孩子的左孩子继续遍历
- 如果没有右孩子,则继续出栈
- 步骤如下:
- 首先从
root
结点开始一路遍历左孩子left_child
,条件判断if p != nullptr
- 并将途经的所有结点压栈:
s.push(p);
且p = p -> left;
- 遍历到不能遍历了,跳出条件判断
- 并将途经的所有结点压栈:
else
开始出栈,并且开始对每个结点的右孩子right_child
进行考察- 出栈并且将数据存储到
vector
中:p = s.top(); s.pop(); result.push_back(p->val);
- 进一步考察结点的右孩子
p = p -> right
- 出栈并且将数据存储到
代码如下:
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
struct TreeNode {
TreeNode* left; // 左孩子
TreeNode* right; // 右孩子
int val; // 数据
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
class Binary_Tree_Inorder_Traversal {
vector<int> Inorder_Traversal(TreeNode* root) {
vector<int> result;
stack<const TreeNode*> s;
const TreeNode* p = root; // 一开始不能直接将root结点push到栈中,否则就push了两次,结果有问题
while (!s.empty() || p != nullptr) {
if (p != nullptr) {
s.push(p); // 将一路向左途经的每一个结点都压栈
p = p->left; // 继续向左遍历
}
else { // 向左遍历到头了,开始出栈+向右遍历
p = s.top();
s.pop();
result.push_back(p->val);
p = p->right; // 准备向右遍历,向右遍历之后再向左一路遍历
}
}
return result;
}
};
三、后序遍历
- 先左节点 -> 右节点 -> 根节点
- 思路:每次在出栈栈顶之前都需要判断此元素的右孩子是否呗访问&存在
- 关键点:将
nullptr
也看成一个结点(空结点)
- 步骤如下:
- 首先,用两个指针分别记录此刻和上一刻访问的结点
const TreeNode* p = root,q = nullptr;
- 注意:在进行前向遍历的时候,是不需要用到
q
指针的 - 在反向出栈的时候,才需要记录上一时刻出栈的结点
q = last_push
- 注意:在进行前向遍历的时候,是不需要用到
- 从
root
结点开始,一直向左遍历结点到底,判断条件if(p != nullptr)
- 需要将q赋值为空指针
- 进入
while(!=s.empty())
循环开始出栈,先不进行判断将p = s.top(); s.pop();
- 在判断出栈是否合理(即右节点是否存在且没有被访问过):
if(p->right == q)
说明右结点不存在(为nullptr
)或者右结点已经被访问,此时出栈合理,可以保存p->val
,即result.push_back(p->val); q = p;
else
即p->right != q
说明右结点存在且未被访问(即使是nullptr
也认为存在),此时出栈不合理,应该重新将p入栈,再分析其右孩子,s.push(p); p = p->right; break;
代码如下:
#include<iostream>
#include<vector>
#include<stack>
using namespace std;
struct TreeNode {
TreeNode* left;
TreeNode* right;
int val;
TreeNode(int x) :val(x), left(nullptr), right(nullptr) {};
};
class Binary_Tree_PostOrder_Traserval {
vector<int> PostOrder_Traserval(TreeNode* root) {
vector<int> result;
stack<const TreeNode*> s;
const TreeNode* p = root;
const TreeNode* q = nullptr;
do {
while (p != nullptr) { // 当p还指向结点的时候,一直向下探索
s.push(p);
p = p->left;
}
q = nullptr;
while (!s.empty()) {
p = s.top();
s.pop();
// 若q指向与p->right指向相同,说明右结点已经完成访问,此时根节点可以直接出栈
if (p->right == q) {
result.push_back(p->val);
q = p;
} // 若q的指向与p->right的指向不同,说明右节点还没有完成访问,不能直接将根节点出栈
else {
s.push(p);
p = p->right;
break;
}
}
} while (!s.empty());
return result;
}
};