当前位置: 首页 > 文档资料 > LeetCode 题解 >

Tree - Binary Tree Depth Order Traversal

优质
小牛编辑
140浏览
2023-12-01

前面我们解决了tree的level order遍历问题,这里我们需要来处理tree的depth order,也就是前序,中序和后序遍历。

Binary Tree Preorder Traversal

Given a binary tree, return the preorder traversal of its nodes’ values.

For example:
Given binary tree {1,#,2,3},

  1. 1
  2. \
  3. 2
  4. /
  5. 3

return [1,2,3].

Note: Recursive solution is trivial, could you do it iteratively?

给定一颗二叉树,使用迭代的方式进行前序遍历。

因为不能递归,所以我们只能使用stack来保存迭代状态。

对于前序遍历,根节点是最先访问的,然后是左子树,最后才是右子树。当访问到根节点的时候,我们需要将右子树压栈,这样访问左子树之后,才能正确地找到对应的右子树。

代码如下:

  1. class Solution {
  2. public:
  3. vector<int> preorderTraversal(TreeNode *root) {
  4. vector<int> vals;
  5. if(root == NULL) {
  6. return vals;
  7. }
  8. vector<TreeNode*> nodes;
  9. //首先将root压栈
  10. nodes.push_back(root);
  11. while(!nodes.empty()) {
  12. TreeNode* n = nodes.back();
  13. vals.push_back(n->val);
  14. //访问了该节点,出栈
  15. nodes.pop_back();
  16. //如果有右子树,压栈保存
  17. if(n->right) {
  18. nodes.push_back(n->right);
  19. }
  20. //如果有左子树,压栈保存
  21. if(n->left) {
  22. nodes.push_back(n->left);
  23. }
  24. }
  25. return vals;
  26. }
  27. };

Binary Tree Inorder Traversal

给定一颗二叉树,使用迭代的方式进行中序遍历。

对于中序遍历,首先遍历左子树,然后是根节点,最后才是右子树,所以我们需要用stack记录每次遍历的根节点,当左子树遍历完成之后,从stack弹出根节点,得到其右子树,开始新的遍历。

代码如下:

  1. class Solution {
  2. public:
  3. vector<int> inorderTraversal(TreeNode *root) {
  4. vector<int> vals;
  5. if(root == NULL) {
  6. return vals;
  7. }
  8. vector<TreeNode*> nodes;
  9. TreeNode* p = root;
  10. while(p || !nodes.empty()) {
  11. //这里一直遍历左子树,将根节点压栈
  12. while(p) {
  13. nodes.push_back(p);
  14. p = p->left;
  15. }
  16. if(!nodes.empty()) {
  17. p = nodes.back();
  18. vals.push_back(p->val);
  19. //将根节点弹出,获取右子树
  20. nodes.pop_back();
  21. p = p->right;
  22. }
  23. }
  24. return vals;
  25. }
  26. };

Binary Tree Postorder Traversal

给定一颗二叉树,使用迭代的方式进行后序遍历。

对于后序遍历,首先遍历左子树,然后是右子树,最后才是根节点。当遍历到一个节点的时候,首先我们将右子树压栈,然后将左子树压栈。这里需要注意一下出栈的规则,对于叶子节点来说,直接可以出栈,但是对于根节点来说,我们需要一个变量记录上一次出栈的节点,如果上一次出栈的节点是该根节点的左子树或者右子树,那么该根节点可以出栈,否则这个根节点是新访问的节点,将右和左子树分别压栈。

代码如下:

  1. class Solution {
  2. public:
  3. vector<int> postorderTraversal(TreeNode *root) {
  4. vector<int> vals;
  5. if(root == NULL) {
  6. return vals;
  7. }
  8. vector<TreeNode*> nodes;
  9. TreeNode* pre = NULL;
  10. nodes.push_back(root);
  11. while(!nodes.empty()) {
  12. TreeNode* p = nodes.back();
  13. //如果不判断pre,我们就没法正确地出栈了
  14. if((p->left == NULL && p->right == NULL) ||
  15. (pre != NULL && (pre == p->left || pre == p->right))) {
  16. vals.push_back(p->val);
  17. nodes.pop_back();
  18. pre = p;
  19. } else {
  20. //右子树压栈
  21. if(p->right != NULL) {
  22. nodes.push_back(p->right);
  23. }
  24. //左子树压栈
  25. if(p->left != NULL) {
  26. nodes.push_back(p->left);
  27. }
  28. }
  29. }
  30. return vals;
  31. }
  32. };

总结

可以看到,树的遍历通过递归或者堆栈的方式都是比较容易的,网上还有更牛的不用栈的方法,只是我没理解,就不做过多说明了。