leetcode/notes/src/btree_iter.md

4.6 KiB
Raw Blame History

遍历

深度优先遍历(递归法)

// 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 */);
}

前中后序遍历的区别就在于访问节点的顺序不同。

注意:务必理解和记忆每种遍历的遍历动态图!

前序遍历:

    printf("%d\n", curNode->val);
    dfs(curNode->left, result);
    dfs(curNode->right, result);

begin

中序遍历:

    dfs(curNode->left, result);
    printf("%d\n", curNode->val);
    dfs(curNode->right, result);

medium

后序遍历:

    dfs(curNode->left, result);
    dfs(curNode->right, result);
    printf("%d\n", curNode->val);

end

深度优先遍历(迭代法)

由于递归本质是对栈进行操作,因此也可以用迭代+栈的方式实现。

以中序遍历为例:

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;
}

广度优先遍历(层序遍历)

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);
  }
}

如果需要对每一层进行处理,则修改如下:

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;
}

如果需要找某一层的什么节点的话,考虑用这个版本的层序遍历。