【数据结构】二叉树的非递归遍历与最近公共祖先

目录

二叉树的非递归遍历

1.前序遍历:

2.中序遍历:

3.后序遍历:

二叉树的最近公共祖先

1.递归实现

2.非递归实现


二叉树的非递归遍历

我们一般使用递归遍历二叉树,但借助栈我们也可以通过非递归遍历二叉树。

如上图所示,先遍历根节点和其左子树,将遍历到的节点入栈,之后再取出栈内的节点遍历其右子树

1.前序遍历:

按照上图的步骤来实现,前序遍历(根-->左-->右),在节点入栈的时候打印该节点即可:

    //非递归实现前序遍历
    public void preorderTraversalNor(TreeNode root) {
        //辅助遍历二叉树的栈
        Stack<TreeNode> stack = new Stack<>();
        //根节点入栈
        stack.push(root);
        TreeNode cur = root;
        //栈内取出的元素的右节点不为空且栈不为空
        while (cur != null || !stack.empty()) {
            //根节点或左节点不为空
            while (cur != null) {
                //节点入栈
                stack.push(cur);
                //打印节点的值
                System.out.print(cur.val + " ");
                //cur指向节点的左节点
                cur = cur.left;
            }
            //cur指向栈顶节点的右节点且该节点出栈
            cur = stack.pop().right;
        }
    }

2.中序遍历:

中序遍历(左-->根-->右),故应该在节点的左节点为空或遍历过节点的左子树时,再打印栈顶存放的该节点的父节点:

    //非递归实现中序遍历
    public void inorderTraversalNor(TreeNode root) {
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        while (cur != null && !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.pop();
            System.out.print(top.val + " ");
            cur = top.right;
        }
    }

3.后序遍历:

后续遍历(左-->右-->根),故应该在节点的左右节点都为空或左右节点均遍历过时,再打印栈顶存放的该节点的父节点:

    //非递归的后续遍历
    public void orderTraversalNor(TreeNode root) {
        //用来记录被打印过的子树的根节点
        TreeNode prev = null;
        TreeNode cur = root;
        Stack<TreeNode> stack = new Stack<>();
        while (cur != null || !stack.empty()) {
            while (cur != null) {
                stack.push(cur);
                cur = cur.left;
            }
            TreeNode top = stack.peek();
            //右节点为空或右边已经被打印过了
            if (top.right == null || top.right == prev) {
                System.out.print(top.val + " ");
                stack.pop();
                //记录被打印过的子树的根节点
                prev = top;
            } else {
                cur = top.right;
            }
        }
    }

注意:

1.因为根节点是最后打印的,故获取栈内节点的右节点时并不能将栈内的节点出栈。

2.因为获取栈内节点的右节点时没有将栈内节点出栈,故当遍历完该节点的右节点后才会轮到该节点出栈,此时若该节点的右节点不为空,就需要判断该节点的右节点是否被打印过了,不然就会再遍历一遍该节点的右边。

二叉树的最近公共祖先

上图中B,C的最近公共祖先是A;D,C的最近公共祖先是A;E,F的最近公共祖先是B。

获取二叉树树的最近公共祖先可以通过下面两种方法:

1.递归实现

子问题的思想,分p和q有一个在根节点,p和q都在左子树,p和q都在右子树讨论:

public class Test {
  public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //根节点为空,没有最近公共祖先
        if (root == null || p == null || q == null) {
            return null;
        }
        //p为根节点,最近公共祖先为p;q为根节点,最近公共祖先为q
        if (root == p || root == q) {
            return root;
        }
        //左子树找p,q
        TreeNode leftTree = lowestCommonAncestor2(root.left, p, q);
        //右子树找p,q
        TreeNode rightTree = lowestCommonAncestor(root.right, p, q);
        if (leftTree != null && rightTree != null) {
            //一个在左,一个在右,最近公共祖先为该子树的根节点
            return root;
        } else if (leftTree == null) {
            //都在右子树,最近公共祖先为p或q中辈分大的那个
            return rightTree;
        } else if (rightTree == null) {
            //都在左子树,最近公共祖先为p,q中辈分大的那个
            return leftTree;
        } else {
            //都没找到,返回null
            return null;
        }
    }
}

2.非递归实现

用两个栈分别存放两个节点到根节点路径上的所有节点,将较长栈出栈直达两个栈的大小相等,最后同时出栈比较出栈元素,两个栈第一个相同的出栈元素即为最近公共祖先:

.

public class Test {
   //最近公共祖先
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null || p == null || q == null) {
            return null;
        }
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        //stack1存放root到p路径上的所有节点
        getPath(root, p, stack1);
        //stack2存放root到q路径上的所有节点
        getPath(root, q, stack2);
        int size1 = stack1.size();
        int size2 = stack2.size();
        //将路径比较长的去掉长出来的那段尾巴
        if (size1 > size2) {
            int size = size1 - size2;
            while (size > 0) {
                stack1.pop();
                size--;
            }
        } else {
            int size = size2 - size1;
            while (size > 0) {
                stack2.pop();
                size--;
            }
            stack2.pop();
        }
        //此时同时比较出栈的元素就能得到最近公共祖先
        while (stack1.peek() != stack2.peek()) {
            stack1.pop();
            stack2.pop();
        }
        return stack1.peek();
    }
    private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
        if (root == null || node == null) {
            return false;
        }
        stack.push(root);
        if (root == node) {
            return true;
        }
        boolean flg1 = getPath(root.left, node, stack);
        if (flg1) {
            return true;
        }
        boolean flg2 = getPath(root.right, node, stack);
        if (flg2) {
            return true;
        }
        stack.pop();
        return false;
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
以下是两种C语言实现二叉树共同祖先的方法: 方法一: ```c typedef struct BiTNode { int data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree; // 查找p或q结点是否在以root为根的二叉树中 bool findNode(BiTree root, BiTNode *p) { if (root == NULL) { return false; } if (root == p) { return true; } if (findNode(root->lchild, p) || findNode(root->rchild, p)) { return true; } return false; } // 查找p和q的最近公共祖先 BiTNode *nearest_ancestor(BiTree root, BiTNode *p, BiTNode *q) { if (root == NULL || p == NULL || q == NULL) { return NULL; } if (!findNode(root, p) || !findNode(root, q)) { return NULL; } if (root == p || root == q) { return root; } BiTNode *left = nearest_ancestor(root->lchild, p, q); BiTNode *right = nearest_ancestor(root->rchild, p, q); if (left != NULL && right != NULL) { return root; } else if (left != NULL) { return left; } else { return right; } } ``` 方法二: ```c typedef struct BiTNode { int data; struct BiTNode *lchild, *rchild; } BiTNode, *BiTree; typedef struct { BiTNode *elem[100]; int top; } Stack; // 初始化栈 void init_stack(Stack *s) { s->top = -1; } // 入栈 void push(Stack *s, BiTNode *node) { s->top++; s->elem[s->top] = node; } // 出栈 void pop(Stack *s) { s->top--; } // 获取栈顶元素 BiTNode *get_top(Stack *s) { return s->elem[s->top]; } // 查找从根结点到指定结点的路径 bool path(BiTree root, BiTNode *node, Stack *s) { if (root == NULL) { return false; } push(s, root); if (root == node) { return true; } if (path(root->lchild, node, s) || path(root->rchild, node, s)) { return true; } pop(s); return false; } // 查找p和q的最近公共祖先 BiTNode *nearest_ancestor(BiTree root, BiTNode *p, BiTNode *q) { Stack s1, s2; BiTNode *Node1; init_stack(&s1); init_stack(&s2); path(root, p, &s1); path(root, q, &s2); Node1 = get_top(&s1); if (get_top(&s1) != get_top(&s2)) { return NULL; } for (int i = 1; i < s1.top && i < s2.top; i++) { if (s1.elem[i] != s2.elem[i]) { return Node1; } Node1 = s1.elem[i]; } return Node1; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

记得开心一点啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值