LogoCSP Wiki By Yundou
图论

树的遍历

树的遍历

树上 DFS

在树上 DFS 是这样的一个过程:先访问根节点,然后分别访问根节点每个儿子的子树。

可以用来求出每个节点的深度、父亲等信息。

二叉树 DFS 遍历

先序遍历

图片描述

按照 根,左,右 的顺序遍历二叉树。

示例代码

void preorder(BiTree* root) {
  if (root) {
    cout << root->key << " ";
    preorder(root->left);
    preorder(root->right);
  }
}

中序遍历

图片描述

按照 左,根,右 的顺序遍历二叉树。

示例代码

void inorder(BiTree* root) {
  if (root) {
    inorder(root->left);
    cout << root->key << " ";
    inorder(root->right);
  }
}

后序遍历

图片描述

按照 左,右,根 的顺序遍历二叉树。

示例代码

void postorder(BiTree* root) {
  if (root) {
    postorder(root->left);
    postorder(root->right);
    cout << root->key << " ";
  }
}

反推

已知中序遍历序列和另外一个序列可以求第三个序列。

图片描述
  1. 前序的第一个是 root,后序的最后一个是 root
  2. 先确定根节点,然后根据中序遍历,在根左边的为左子树,根右边的为右子树。
  3. 对于每一个子树可以看成一个全新的树,仍然遵循上面的规律。

树上 BFS

从树根开始,严格按照层次来访问节点。

BFS 过程中也可以顺便求出各个节点的深度和父亲节点。

树的层序遍历

树层序遍历是指按照从根节点到叶子节点的层次关系,一层一层的横向遍历各个节点。根据 BFS 的定义可以知道,BFS 所得到的遍历顺序就是一种层序遍历。但层序遍历要求将不同的层次区分开来,所以其结果通常以二维数组的形式表示。

例如,下图的树的层序遍历的结果是 [[1], [2, 3, 4], [5, 6]](每一层从左向右)。

图片描述

示例代码

vector<vector<int>> levelOrder(Node* root) {
  if (!root) {
    return {};
  }
  vector<vector<int>> res;
  queue<Node*> q;
  q.push(root);
  while (!q.empty()) {
    int currentLevelSize = q.size();  // 当前层的节点个数
    res.push_back(vector<int>());
    for (int i = 0; i < currentLevelSize; ++i) {
      Node* cur = q.front();
      q.pop();
      res.back().push_back(cur->val);
      for (Node* child : cur->children) {  // 把子节点都加入
        q.push(child);
      }
    }
  }
  return res;
}

无根树

过程

树的遍历一般为深度优先遍历,这个过程中最需要注意的是避免重复访问结点。

由于树是无环图,因此只需记录当前结点是由哪个结点访问而来,此后进入除该结点外的所有相邻结点,即可避免重复访问。

示例代码

void dfs(int u, int from) {
  // 递归进入除了 from 之外的所有子结点
  // 对于出发结点,from 为空,故会访问所有相邻结点,这与期望一致
  for (int v : adj[u])
    if (v != from) {
      dfs(v, u);
    }
}
 
// 开始遍历时
int EMPTY_NODE = -1;  // 一个不存在的编号
int root = 0;         // 任取一个结点作为出发点
dfs(root, EMPTY_NODE);

有根树

对于有根树,需要区分结点的上下关系。

考察上面的遍历过程,若从根开始遍历,则访问到一个结点时 from 的值,就是其父结点的编号。

通过这个方式,可以对于无向的输入求出所有结点的父结点,以及子结点列表。

On this page