查找两个二叉树节点的最近公共祖先Java

前言

本来想偷懒看看别人的代码,收藏一下完事了,但找了半天也没找到想要的非递归的Java版本,所以本文着重讲下非递归方式的实现,供自己学习的同时分享给大家。

 

递归方式实现

递归方式实现直接点传送门,这个小姐姐已经解释的及其通俗易懂了。

 

非递归方式实现

问题:

如图所示,若要查找节点4和节点5的最近公共祖先。从图中我们可以看到最近的公共祖先为节点2,那么怎样才能找到这个节点2呢?

思路:

从根节点1到节点4和节点5的路径分别为{1,2,4}和{1,2,5},可以发现2节点是两个路径集合相同下标3不同元素4和5的上一个相同元素,这个元素就是最近的公共祖先。用非递归的后序遍历的方式对整棵树从根节点进行查找,再加上若干对查找状态的判断,净版的非递归后序遍历在之前的文章有,传送门在此。我们可以用双向链表来存储路径的节点,在双向链表末尾插入和移除路径节点,类似于栈,但在最后对两个路径集合的比较中,我们又需要从前向后读取的队列结构。

代码如下:

感觉麻烦的同学,建议看看净版的非递归后序遍历,再来看下面的代码,就会清晰跟多。

import java.util.LinkedList;
import java.util.Stack;

/**
 * @author yinglala
 */
public class Ancestor {
    //求两个节点的最近公共祖先
    private static BinaryTree ancestorMethod(BinaryTree t,BinaryTree a,BinaryTree b) {
        if (t == null) {
            return null;
        }
        if (t == a || t == b) {
            return t;
        }
        Stack<BinaryTree> stack = new Stack();
        //采用双向链表来存放节点a和节点b存放的路径
        LinkedList<BinaryTree> listA = new LinkedList();
        LinkedList<BinaryTree> listB = new LinkedList();

        stack.push(t);
        //将根元素压栈
        listA.addLast(t);
        listB.addLast(t);
        //设置当前节点用来保证左子树不会出现死循环
        BinaryTree p = t;
        //设置访问过的节点用来保证右子树不会出现死循环
        BinaryTree tVisit = null;

        //设置a节点和b节点访问的状态 初始状态为false
        Boolean aFind = false;
        Boolean bFind = false;

        while (stack != null && stack.size() != 0) {
            //如果左孩子不为空 先压栈
            if (p != null && p.getLchild() != null) {
                stack.push(p.getLchild());
                //如果a节点还没有找到就继续记录找寻a节点经过的路径
                if(!aFind){
                    listA.addLast(p.getLchild());
                }
                //如果b节点还没有找到就继续记录找寻b节点经过的路径
                if(!bFind){
                    listB.addLast(p.getLchild());
                }
                p = p.getLchild();
            } else {
                p = stack.peek();//获取栈顶元素 并保证p不为空

                //若没有右孩子,或者右孩子已经被访问过
                if (p.getRchild() == null || p.getRchild() == tVisit) {
                    //若访问的节点为a,则更改a节点的访问状态
                    if(p == a){
                        aFind = true;
                    }
                    //若访问的节点为b,则更改b节点的访问状态
                    if(p == b){
                        bFind = true;
                    }
                    //若节点a没有访问到 则在a的访问路径链中移除本次访问的记录
                    if(!aFind){
                        listA.removeLast();
                    }
                    //若节点b没有访问到 则在b的访问路径链中移除本次访问的记录
                    if(!bFind){
                        listB.removeLast();
                    }
                    //节点a和节点b都访问过了则跳出循环
                    if(aFind && bFind)
                        break;
                    //标明上个被访问的节点 防止在回溯的过程中无限访问右孩子节点
                    tVisit = p;
                    p = null;
                    stack.pop();
                } else {
                    stack.push(p.getRchild());
                    //同样的 如果a节点还没有找到就继续记录找寻a节点经过的路径
                    if(!aFind){
                        listA.addLast(p.getRchild());
                    }
                    //如果b节点还没有找到就继续记录找寻b节点经过的路径
                    if(!bFind){
                        listB.addLast(p.getRchild());
                    }
                    p = p.getRchild();
                }
            }
        }

        //如果listA或者listB为空说明a节点或b节点不在以t为根的子树中 直接返回null
        if(listA.isEmpty() || listB.isEmpty()){
            return null;
        }
        int i;
        int j;
        for(i=0, j=0; i< listA.size() && j < listB.size();i++,j++){
            if(listA.get(i) != listB.get(j)){
                return listA.get(i - 1);
            }
        }
        return listA.get(i - 1);
    }

    public static void main(String[] args){
        BinaryTree root = new BinaryTree(1);
        BinaryTree second = new BinaryTree(2);
        BinaryTree three = new BinaryTree(3);
        BinaryTree four = new BinaryTree(4);
        BinaryTree five = new BinaryTree(5);
        BinaryTree six = new BinaryTree(6);
        BinaryTree seven = new BinaryTree(7);
        BinaryTree eight = new BinaryTree(8);
        root.setLchild(second);
        root.setRchild(three);
        second.setLchild(four);
        second.setRchild(five);
        three.setLchild(six);
        three.setRchild(seven);

        System.out.println("以root为根,节点4和5的最近公共祖先为:" +ancestorMethod(root,four,five).getData());
        System.out.println("以root为根,节点6和5的最近公共祖先为:" +ancestorMethod(root,six,five).getData());
        System.out.println("以root为根,节点5和2的最近公共祖先为:" +ancestorMethod(root,five,second).getData());
        System.out.println("以root为根,节点5和8的最近公共祖先为:" +ancestorMethod(root,five,eight));
        System.out.println("以root为根,节点root和5的最近公共祖先为:" +ancestorMethod(root,root,five).getData());
    }
}

 

参考连接:https://www.cnblogs.com/neuzk/p/9487301.html


转载请注明出处。

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值