day13 | 二叉树递归遍历 迭代遍历 层序遍历
二叉树的遍历方式主要有深度优先和广度优先:
- 深度优先有前序、中序、后序
- 广度优先有层序遍历
深度优先就是先遍历到叶子接点再返回,一般通过递归来实现,也可以转为迭代。深度优先的遍历方式主要是以根节点顺序区分,比如前序就是根左右,中序左根右,后续左右根。广度优先一般利用队列来完成。
二叉树结点定义:
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int _val) : val(_val), left(nullptr), right(nullptr) {}
}
递归遍历
首先递归通常三步写:
- 确定返回值和参数
- 确定终止条件
- 确定单层递归逻辑
递归前序遍历:
-
确定返回值和参数
采用一个数组来记录每个结点的val值,返回值为
void
,参数为二叉树根节点和用来存储结果的vector
void traverse(TreeNode* root, vector<int>& vec)
-
确定终止条件
遇到空结点则返回,
if (root == nulltpr) return
-
单层递归逻辑,前序是先处理根节点,然后递归处理左子树,递归处理右子树
vec.push_back(root->val); // 处理根节点
traverse(root->left, vec); // 递归处理左子树
traverse(root->right, vec); // 递归处理右子树
全部代码:
class Solution {
public:
void traverse(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
vec.push_back(root->val);
traverse(root->left, vec);
traverse(root->right, vec);
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traverse(root, result);
return result;
}
};
递归中序遍历:
class Solution {
public:
void traverse(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
traverse(root->left, vec);
vec.push_back(root->val);
traverse(root->right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traverse(root, result);
return result;
}
};
递归后序遍历:
class Solution {
public:
void traverse(TreeNode* root, vector<int>& vec) {
if (root == nullptr) return;
traverse(root->left, vec);
traverse(root->right, vec);
vec.push_back(root->val);
}
vector<int> postorderTraversal(TreeNode* root) {
vector<int> result;
traverse(root, result);
return result;
}
};
迭代遍历
递归遍历可以转为迭代遍历,通过栈来完成,其中前序遍历和后序遍历方式一样,因为这两种遍历方式遍历到的结点都是要处理的结点,可以直接进行处理;而中序遍历遍历到的结点不一定是要处理的结点,需要通过指针找到处理的结点,遍历过的结点用栈存起来,然后处理完一个结点后找到下一个要处理的结点。
前序遍历:
遍历到每一个结点,也就是要处理的结点,将右孩子、左孩子依次入栈就能得到根左右的顺序
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
// 利用栈来存储结点的左右孩子 处理了根节点后然后出栈处理孩子
stack<TreeNode*> st;
vector<int> result;
if (root != nullptr) st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
result.push_back(cur->val);
st.pop();
if (cur->right) st.push(cur->right);
if (cur->left) st.push(cur->left);
}
return result;
}
};
后序遍历:
后序遍历可以通过前序遍历达到,后序是左右根,可以通过逆置根右左来达到,遍历到每一个结点,将左孩子右孩子依次入栈
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
// 后序迭代遍历 左右根 可以通过前序迭代遍历 来完成 需要调整一下入栈顺序 编程 根右左 最后逆置一下
stack<TreeNode*> st;
vector<int> result;
if (root != nullptr) st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
result.push_back(cur->val);
st.pop();
if (cur->left) st.push(cur->left);
if (cur->right) st.push(cur->right);
}
reverse(result.begin(), result.end());
return result;
}
};
中序遍历:
需要通过一个指针来找到需要处理的结点,当指针为空的时候说明遍历到了最右边,这时候应该开始处理栈顶的结点并遍历其右孩子。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
// 还是利用栈 但是不同的是由于遍历到的元素并不一定是要处理的元素,需要存到栈中,也就是当遍历到空时还需要结合栈来
// 进行遍历方向 遍历到空 则说明到了左边结点的孩子,这时候需要访问该结点的父节点然后有兄弟
stack<TreeNode*> st;
vector<int> result;
// 利用指针来找需要处理的结点
TreeNode* cur = root;
while (cur != nullptr || !st.empty()) {
if (cur != nullptr) {
st.push(cur);
cur = cur->left;
} else {
cur = st.top();
st.pop();
result.push_back(cur->val);
cur = cur->right;
}
}
return result;
}
统一迭代(暂时忽略)
层序遍历
每一次处理二叉树的一层,通过队列达到,每次处理纪录一下队列中有多少元素,把这些元素一次性处理完。处理每个元素的时候将左右孩子入队。
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
vector<vector<int>> result;
if (root != nullptr) que.push(root);
while (!que.empty()) {
int size = que.size();
vector<int> layer;
for (int i = 0; i < size; i++) {
TreeNode* cur = que.front();
que.pop();
layer.push_back(cur->val);
if (cur->left) que.push(cur->left);
if (cur->right) que.push(cur->right);
}
result.push_back(layer);
}
return result;
}
};