# 遍历 ## 深度优先遍历(递归法) ```cpp // para_n 用来描述每个节点的状态 // 比如 para1 可以是当前节点的指针,para2 和 para3 可以用来表示当前指针的其它状态信息 // 遍历结果可以用指针放在接收参数保存,也可以通过声明一个 class 的成员来保存 void dfs(int para1, int para2, int para3, std::vector &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 inorderTraversal(TreeNode* root) { // 初始化结果集 vector result; // 初始化栈 stack 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 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> iter(Node *root) { // 讨论边界条件 if (root == nullptr) { return; } // 初始化一个队列 std::queue queue; // 初始化结果向量 vector> result; // 把根节点放进去 if (root) queue.push(root); // 开始迭代,当队列为空时结束迭代 while (!queue.empty()) { // 获得当前层的节点个数 int size = queue.size(); // 创建一个向量用来装当前层的结果 vector 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; } ``` 如果需要找某一层的什么节点的话,考虑用这个版本的层序遍历。