二叉树遍历总结:DFS+BFS【C++】

二叉树遍历总结(C++实现)

二叉树遍历分类

  1. DFS(depth-first-search) 深度优先搜索
    1. 前序遍历(根-左-右)
    2. 中序遍历(左-根-右)
    3. 后序遍历(左-右-根)
  2. BFS(breadth-first-search) 广度优先搜索
    1. 层序遍历(从上到下,从左到右访问)

DFS 深度优先搜索:前序、中序、后序遍历

  • 深搜的三种遍历算法,实际上通过的是同一条路径,其不同之处仅仅是对结点执行操作的时机不同

  • 相同:在DFS过程中,每个结点都会作为其子树的根结点,被函数经过三次(根-左-根-右-根)
  • 不同:前序遍历在第一次经过时对结点执行操作,中序遍历在第二次执行操作,后序遍历在第三次进行操作

如上图所示,左右子树可为空。圈叉表示第一次访问,星标表示第二次,三角形表示第三次。

实现方式

前序、中序和后序均有递归和非递归两种实现方式

  • 递归版本中,三种遍历的实现大同小异
  • 非递归版本中,前中序相似,后序较为复杂,相同点在于三种遍历的非递归方式都会用到栈

结点结构:

struct TreeNode {
	int val; //结点值
	TreeNode *left; //左孩子
	TreeNode *right; //右孩子
	TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

前序遍历的实现

递归版本

void preOrderRecur(TreeNode *root) {
      if (!root) return;

      // do something e.g. print
      cout << root->val << " ";
      preOrderRecur(root->left);
      preOrderRecur(root->right);
}

非递归版本

最左结点:一直往左,直到碰到无左孩子的结点

边界判断:需要当前结点r为空且栈为空,循环才会结束。

  1. 栈空但r不为空:当前根结点的左子树处理完毕,右子树没有处理
  2. r空但栈不空:正在处理某一个子树的过程中,且其根结点无右儿子
void preOrderNocur(TreeNode *root) {
      if (!root) return;

      // 初始化栈
      stack<TreeNode*> s;
      TreeNode *r = root;

      // 循环开始
      while (r || !s.empty()) {
              // 一次循环处理一个最小子树
              // 从根开始一直往左入栈,直到最左结点入栈结束
              while (r) {
                     s.push(r);
                     // 这里是第一次经过结点,前序遍历在这里进行操作
                     cout << r->val << " ";
                     r = r->left;
              }
              // 处理栈内结点,访问其右子树
              if (!s.empty()) {
                     r = s.top(); //第二次经过结点,中序遍历在这里进行操作
                     s.pop(); //这里结点出栈了,所以没有第三次经过结点,无法用这个方法实现后序遍历
                     r = r->right; //转到右子树
              }
      }
}

中序遍历的实现

递归版本

void inOrderRecur(TreeNode *root) {
      if (!root) return;

      inOrderRecur(root->left);
      // do something
      cout << root->val << " ";
      inOrderRecur(root->right);
}

非递归版本(与前序遍历类似)

void inOrderNocur(TreeNode *root) {
      if (!root) return;

      // 初始化
      stack<TreeNode*> s;
      TreeNode* r = root;

      // 循环开始
      while (r || !s.empty()) {
              // 一直往左全部入栈
              while (r) {
                     s.push(r);
                     r = r->left;
              }
              // 从最左儿子开始由左根右的顺序处理
              if (!s.empty()) {
                     r = s.top();
                     // 中序遍历在这里do something
                     cout << r->val << " ";
                     s.pop();
                     r = r->right;
              }
      }
}

后序遍历的实现

递归版本

void postOrderRecur(TreeNode *root) {
      if (!root) return;

      postOrderRecur(root->left);
      postOrderRecur(root->right);
      // do something
      cout << root->val << " ";
}

非递归版本

思路:

  • 前序遍历的顺序是根左右,反过来就是右左根
  • 因此想到,将前序遍历变为根右左,反过来就是左右根,即后序遍历
  • 实现反向输出的方法就是增加一个栈(p)
void postOrderNocur(TreeNode *root) {
      if (!root) return;

      stack<TreeNode*> s; // 第一个栈,保证前序遍历(根-右-左)的执行
      stack<TreeNode*> p; // 第二个栈,实现反向输出左右根
      TreeNode *r = root;

      while (r || !s.empty()) {
              while (r) {
                     s.push(r);
                     p.push(r); // p与s同步push
                     r = r->right;
              }
              if (!s.empty()) {
                     r = s.top();
                     s.pop(); // p不需要pop;s执行pop的原因是需要将前序遍历执行下去
                     r = r->left;
              }
      }
      while (!p.empty()) {
              cout << p.top()->val << " ";
              p.pop();
      }
}

BFS 广度优先搜索:层序遍历

层序遍历

思想:使用队列实现

  1. 根入队;
  2. 循环开始:队头出队,其左右儿子按序入队
void levelOrder(TreeNode *root) {
      if (!root) return;

      // 初始化队列
      queue<TreeNode*> q;
      TreeNode *r = root;
      q.push(r);

      while (!q.empty()) {
              r = q.front();
              q.pop();
              // do something
              cout << r->val << " ";

              if (r->left) q.push(r->left);
              if (r->right) q.push(r->right);
      }
}

将队列改为堆栈,并且将左右儿子入队顺序交换,可以实现另一个非递归版本的前序遍历

void preOrderStack(TreeNode* root) {
      if (!root) return;

      // 初始化
      stack<TreeNode*> s;
      TreeNode* r = root;
      s.push(r);

      while (!s.empty()) {
              r = s.top();
              s.pop();

              // do something
              cout << r->val << " ";

              if (r->right) s.push(r->right);
              if (r->left) s.push(r->left);
      }
}
posted @ 2020-09-15 14:10  Ueeei  阅读(788)  评论(0编辑  收藏  举报