目录
LeetCode 102. 107. 199. 637. 429. 515. 116. 117. 104. 111. 二叉树的层序遍历
今天主要是了解了二叉树的主要遍历方式,广度和深度遍历方式。
LeetCode 144. 二叉树的前序遍历
LeetCode 145. 二叉树的后序遍历
LeetCode 94. 二叉树的中序遍历
题目链接:LeetCode 94. 二叉树的中序遍历、LeetCode 145. 二叉树的后序遍历、LeetCode 144. 二叉树的前序遍历
递归思想:首先需要确定递归函数的形参,第一个就是目前节点,其次需要一个能存储节点数值的数组;如果这个节点是空节点的话,就直接return掉;如果节点不为空的话,就要进行遍历了,前中后序的要求如下:
- 前序遍历:先存储这个节点的值,再递归这个节点的左节点,最后是右节点;
- 中序遍历:先递归这个节点的左节点,再存储这个节点的值,最后是右节点;
- 后序遍历:先递归这个节点的左右皆,再递归右节点,最后村粗这个节点值。
代码如下:
// 前序遍历
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
vec.push_back(cur->val); // 中
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
// 中序遍历
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
vec.push_back(cur->val); // 中
traversal(cur->right, vec); // 右
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
// 后序遍历
void traversal(TreeNode* cur, vector<int>& vec) {
if (cur == NULL) return;
traversal(cur->left, vec); // 左
traversal(cur->right, vec); // 右
vec.push_back(cur->val); // 中
}
vector<int> preorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
迭代思想:前中后序的迭代方法基本上完全不一样,所以只能分别介绍了。
首先就是前序遍历了,前序遍历是中左右,每次都是先处理的中间节点,那么就将根节点先放入一个栈里面,然后右节点入栈,再左节点入栈。这样出栈的时候就是左节点再右节点了。
代码如下:
vector<int> preorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top(); // 中
st.pop();
result.push_back(node->val);
if (node->right) st.push(node->right); // 右(空节点不入栈)
if (node->left) st.push(node->left); // 左(空节点不入栈)
}
return result;
}
后序遍历和前序遍历有丶像,所以先把后序遍历讲了。后序遍历是左右中,前序遍历是中左右,那么可以把前序遍历的顺序稍微改那么一下下,变成中右左,到最后的时候把给出来的数组反转一下不就是左右中了吗。
代码如下:
vector<int> postorderTraversal(TreeNode* root) {
stack<TreeNode*> st;
vector<int> result;
if (root == NULL) return result;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
result.push_back(node->val);
if (node->left) st.push(node->left); // 相对于前序遍历,这更改一下入栈顺序 (空节点不入栈)
if (node->right) st.push(node->right); // 空节点不入栈
}
reverse(result.begin(), result.end()); // 将结果反转之后就是左右中的顺序了
return result;
}
最后就是中序遍历了,中序遍历是左中右。因为根节点在中间,但是最先访问的节点要根据根节点一步一步向下访问,直到达到数最左边的最底部,再开始处理节点,处理的顺序与访问顺序不一致。
在使用迭代法写中序遍历,就需要借用指针的遍历来帮助访问节点,栈则用来处理节点上的元素。
代码如下:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while (cur != NULL || !st.empty()) {
if (cur != NULL) { // 指针来访问节点,访问到最底层
st.push(cur); // 将访问的节点放进栈
cur = cur->left; // 左
} else {
cur = st.top(); // 从栈里弹出的数据,就是要处理的数据(放进result数组里的数据)
st.pop();
result.push_back(cur->val); // 中
cur = cur->right; // 右
}
}
return result;
}
LeetCode 102. 107. 199. 637. 429. 515. 116. 117. 104. 111. 二叉树的层序遍历
题目链接:LeetCode 102. 二叉树的层序遍历、LeetCode 107. 二叉树的层序遍历 II、LeetCode 199. 二叉树的右视图、LeetCode 637. 二叉树的层平均值、LeetCode 429. N叉树的层序遍历、LeetCode 515. 在每个树行中找最大值、LeetCode 116. 填充每个节点的下一个右侧节点指针、LeetCode 117. 填充每个节点的下一个右侧节点指针 II
LeetCode 104. 二叉树的最大深度、LeetCode 111. 二叉树的最小深度
思想:这十个题都可以采用层序遍历的方式来解决,所以只要掌握了层序遍历,这十个题根本不在话下。层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。需要借用一个辅助数据结构即队列来实现,队列先进先出,符合一层一层遍历的逻辑,而用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。而这种层序遍历方式就是图论中的广度优先遍历,只不过我们应用在二叉树上。
代码如下:
vector<vector<int>> levelOrder(TreeNode* root) {
queue<TreeNode*> que;
if (root != NULL) que.push(root);
vector<vector<int>> result;
while (!que.empty()) {
int size = que.size();
vector<int> vec;
// 这里一定要使用固定大小size,不要使用que.size(),因为que.size是不断变化的
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
vec.push_back(node->val);
if (node->left) que.push(node->left);
if (node->right) que.push(node->right);
}
result.push_back(vec);
}
return result;
}
总结
遍历是数据结构的基本,掌握遍历才能完成后序操作。