leetcode/notes/src/btree_iter.md
Sainnhe Park a5dd365883
All checks were successful
ci/woodpecker/push/test Pipeline was successful
Update notes
2023-02-01 13:11:44 +08:00

172 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 遍历
## 深度优先遍历(递归法)
```cpp
// para_n 用来描述每个节点的状态
// 比如 para1 可以是当前节点的指针para2 和 para3 可以用来表示当前指针的其它状态信息
// 遍历结果可以用指针放在接收参数保存,也可以通过声明一个 class 的成员来保存
void dfs(int para1, int para2, int para3, std::vector<std::string> &result) {
// 讨论边界条件
// 只需要在这里讨论结束条件即可,初始化的工作会在 dfs 外完成
if (/* end condition */) {
/* statement */
}
// 当当前节点状态越界或不合法时,剪枝
if (/* invalid */) {
return;
}
// 当当前节点状态合法时,遍历当前节点的所有子节点
dfs(/* state of child node 1 */, result);
dfs(/* state of child node 2 */, result);
dfs(/* state of child node 3 */, result);
}
void main(void) {
dfs(/* state of root node */, /* initial result */);
}
```
前中后序遍历的区别就在于访问节点的顺序不同。
**注意**:务必理解和记忆每种遍历的遍历动态图!
前序遍历:
```cpp
printf("%d\n", curNode->val);
dfs(curNode->left, result);
dfs(curNode->right, result);
```
![begin](https://paste.sainnhe.dev/cSaJ.gif)
中序遍历:
```cpp
dfs(curNode->left, result);
printf("%d\n", curNode->val);
dfs(curNode->right, result);
```
![medium](https://paste.sainnhe.dev/KJMD.gif)
后序遍历:
```cpp
dfs(curNode->left, result);
dfs(curNode->right, result);
printf("%d\n", curNode->val);
```
![end](https://paste.sainnhe.dev/PHbJ.gif)
## 深度优先遍历(迭代法)
由于递归本质是对栈进行操作,因此也可以用迭代+栈的方式实现。
以中序遍历为例:
```cpp
vector<int> inorderTraversal(TreeNode* root) {
// 初始化结果集
vector<int> result;
// 初始化栈
stack<TreeNode*> st;
// 当根节点不为空时将根节点入栈
if (root != NULL) st.push(root);
// 当栈为空时停止迭代
while (!st.empty()) {
// 先获取栈顶元素
TreeNode* node = st.top();
// 栈顶元素出栈
st.pop();
// 如果栈顶元素不为空指针,则将节点按顺序入栈
if (node != NULL) {
// 注意是右中左,和左中右反着,因为栈是先进后出
// 右
if (node->right) st.push(node->right);
// 中
st.push(node);
st.push(NULL);
// 左
if (node->left) st.push(node->left);
} else { // 只有遇到空节点的时候,才将下一个节点放进结果集
node = st.top(); // 重新取出栈中元素
st.pop();
result.push_back(node->val); // 加入到结果集
}
}
return result;
}
```
## 广度优先遍历(层序遍历)
```cpp
void iter(Node *root) {
// 讨论边界条件
if (root == nullptr) {
return;
}
// 初始化一个队列
std::queue<Node *> queue;
// 把根节点放进去
// 这里要检查一下是否为空,也就是先检查边界条件再操作
// DFS 不需要检查边界条件就可以直接操作,这是因为边界条件在下一层迭代中检查
if (root) queue.push(root);
// 开始迭代,当队列为空时结束迭代
while (!queue.empty()) {
// 取队首
Node *node = queue.front();
// 弹出队首
queue.pop();
// 将队首的值放进向量中
vec.push_back(node->val);
// 遍历队首的所有子节点并把它们放到队尾
if (node->left) queue.push(node->left);
if (node->right) queue.push(node->right);
}
}
```
如果需要对每一层进行处理,则修改如下:
```cpp
vector<vector<int>> iter(Node *root) {
// 讨论边界条件
if (root == nullptr) {
return;
}
// 初始化一个队列
std::queue<Node *> queue;
// 初始化结果向量
vector<vector<int>> result;
// 把根节点放进去
if (root) queue.push(root);
// 开始迭代,当队列为空时结束迭代
while (!queue.empty()) {
// 获得当前层的节点个数
int size = queue.size();
// 创建一个向量用来装当前层的结果
vector<int> vec;
// 开始迭代当前层
for (int i{0}; i < size; ++i) {
// 取队首
Node *node = queue.front();
// 弹出队首
queue.pop();
// 将队首的值放进向量中
vec.push_back(node->val);
// 遍历队首的所有子节点并把它们放到队尾
if (node->left) queue.push(node->left);
if (node->right) queue.push(node->right);
}
result.push_back(vec);
}
return result;
}
```
如果需要找某一层的什么节点的话,考虑用这个版本的层序遍历。