48 求树中两个结点的最低公共祖先,此树不是二叉树,并且没有指向父节点的指针。

一、题目
求树中两个结点的最低公共祖先,此树不是二叉树,并且没有指向父节点的指针。
二、思路
思路一:
  该题首先要和面试官确定是否为二叉树,得到肯定答复后,还要确定是否为二叉搜索树,是否有父指针,或者仅仅是普通二叉树。
解法一:
  树为二叉搜索树时
  最低公共祖先结点的大小在两个树结点大小的中间。
原文链接: https://blog.csdn.net/jingshuigg/article/details/31048141
在这里插入图片描述

复杂度:由于递归调用二叉树,所以时间复杂度是O(logn),空间复杂度是O(1)

public TreeNode getLowestCommonParentBST(TreeNode root,TreeNode node1,TreeNode node2) {
    while(true) {
        if(root==null)
            return root;
        if(root.val<node1.val && root.val<node2.val)
             root=root.right;//  如果p,q都在root右边,则右节点更近
        else if(root.val>node1.val && root.val>node2.val)
            root=root.left;  如果p,q都在root左边,则左节点更近
        else
            return root;// 如果p,q分别在root的左右两边,说明root是最近公共祖先
    }
}

测试:

public class BinaryTreeNode {
	public int value;
	public BinaryTreeNode leftNode;
	public BinaryTreeNode rightNode;
	
	public BinaryTreeNode(int value){
		this.value = value;
		leftNode = null;
		rightNode = null;
	}
}
 
public static void main(String[] args){
		BinaryTreeNode A = new BinaryTreeNode(4);
		BinaryTreeNode B = new BinaryTreeNode(2);
		BinaryTreeNode C = new BinaryTreeNode(6);
		BinaryTreeNode D = new BinaryTreeNode(1);
		BinaryTreeNode E = new BinaryTreeNode(3);
		BinaryTreeNode F = new BinaryTreeNode(5);
		BinaryTreeNode G = new BinaryTreeNode(7);
		A.leftNode = B;
		A.rightNode = C;
		B.leftNode = D;
		B.rightNode = E;
		C.leftNode = F;
		C.rightNode = G;
		BinaryTreeNode res1 = getLastCommonNode( A, E, F);
		BinaryTreeNode res2 = getLastCommonNode( A, D, E);
		BinaryTreeNode res3 = getLastCommonNode( A, B, D);
		System.out.println("The lowest common ancestor of 3 and 5 is " +res1.value);
		System.out.println("The lowest common ancestor of 1 and 3 is " +res2.value);
		System.out.println("The lowest common ancestor of 1 and 2 is " +res3.value);
}
打印结果

结果:
The lowest common ancestor of 3 and5 is 4

The lowest common ancestor of 1 and3 is 2

Thelowest common ancestor of 1 and 2 is 2

2.树为普通树时,需要分情况,看树的节点中有没有指向父节点的指针?
  原文链接: https://blog.csdn.net/jingshuigg/article/details/31048141

为什么需要指向父节点的指针?
答:如果存在parent指针,则分别从输入的p节点和q节点指向root根节点,
其实这就是两个单链表。问题转化为求两个单链表相交的第一个公共节点

解法二:
适用于(树是普通的二叉树,没有指向父节点的指针。 或者二叉搜索树

递归的解法如下:
后序遍历二叉树,如果遍历到的当前节点为pRoot。因为是后序遍历,所以先处理pRoot的两棵子树。假设pRoot左子树时返回left,右子树返回right

  1. 如果pRoot为null或pLeft ,pRight,返回pRoot
  2. 如果left和right都为空,说明pRoot左右子树都没有pLeft ,pRight,返回null
  3. 如果都不为空,说明左子树上发现过pLeft 或pRight,右子树同理,并在pRoot相遇,则返回pRoot
  4. 如果left和right其中一个是null,另一个不为空,直接返回不为空的那个节点即是公共祖先。

(后续遍历,递归) O(n)

样例
二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]如下图所示:
    8
   / \
  12  2
     / \
    6   4

6. 如果输入的树节点为2和12,则输出的最低公共祖先为树节点8。

7. 如果输入的树节点为2和6,则输出的最低公共祖先为树节点2。

public static BTreeNode getLastCommonNode(BTreeNode pRoot, BTreeNode pLeft, BTreeNode pRight){
	//发现目标节点则通过返回值标记该子树发现了某个目标结点
	if(pRoot == null || pRoot == pLeft || pRoot == pRight){
		return pRoot;
	}
	//查看左子树中是否有目标结点,没有为null
	BTreeNode left = getLastCommonNode(pRoot.left, pLeft, pRight);
	//查看右子树是否有目标节点,没有为null
	BTreeNode right = getLastCommonNode(pRoot.right, pLeft, pRight);
	//都不为空,说明做右子树都有目标结点,则公共祖先就是本身
	if(left != null && right != null){
		return pRoot;
	}
	//如果发现了目标节点,则继续向上标记为该目标节点
	return left == null ? right : right;
}
测试:
  /**
     *      4
     *    /  \
     *  2     6
     * / \  /  \
     *1  3 5   7
     *
     *
     */

    private static void test05() {
        BinaryTreeNode A = new BinaryTreeNode(4);
        BinaryTreeNode B = new BinaryTreeNode(2);
        BinaryTreeNode C = new BinaryTreeNode(6);
        BinaryTreeNode D = new BinaryTreeNode(1);
        BinaryTreeNode E = new BinaryTreeNode(3);
        BinaryTreeNode F = new BinaryTreeNode(5);
        BinaryTreeNode G = new BinaryTreeNode(7);
        A.left = B;
        A.right = C;

        B.left = D;
        B.right = E;

        C.left = F;
        C.right = G;

        BinaryTreeNode res1 = getLowestCommonAncestor(A, E, F);
        BinaryTreeNode res2 = getLowestCommonAncestor(A, D, E);
        BinaryTreeNode res3 = getLowestCommonAncestor(A, B, D);
        System.out.println("The lowest common ancestor of 3 and 5 is " + res1.value);
        System.out.println("The lowest common ancestor of 1 and 3 is " + res2.value);
        System.out.println("The lowest common ancestor of 1 and 2 is " + res3.value);
    }
打印结果:
The lowest common ancestor of 3 and 5 is 4
The lowest common ancestor of 1 and 3 is 2
The lowest common ancestor of 1 and 2 is 2

测试2:
    /**
     *     8
     *    / \
     *   12  2
     *      / \
     *     6   4
     */
    private static void test07() {
        //[8, 12, 2, null, null, 6, 4, null, null, null, null]
        BinaryTreeNode A = new BinaryTreeNode(8);
        BinaryTreeNode B = new BinaryTreeNode(12);
        BinaryTreeNode C = new BinaryTreeNode(2);
        BinaryTreeNode D = new BinaryTreeNode(6);
        BinaryTreeNode E = new BinaryTreeNode(4);
        A.left = B;
        A.right = C;


        C.left = D;
        C.right = E;

        BinaryTreeNode res1 = getLowestCommonAncestor(A, B, C);

        BinaryTreeNode res3 = getLowestCommonAncestor(A, C, D);
        System.out.println("The lowest common ancestor of 12 and 2 is " + res1.value);
        System.out.println("The lowest common ancestor of 2 and 6 is " + res3.value);

    }
    打印结果:
    The lowest common ancestor of 12 and 2 is 8
   The lowest common ancestor of 2 and 6 is 2

解法三:
树是普通的二叉树,且树中节点有指向父节点指针。
两个节点如果在两条路径上,那么类似于“求两个链表的第一个公共节点”的算法题

  /**
     *    // 形状普通的树
     *     //             1
     *     //           /   \
     *     //         2      3
     *     //        / \     /\
     *     //      4   5    6  7
     */

    public static NewBinaryTreeNode  getLowestCommonAncestor1(NewBinaryTreeNode root,NewBinaryTreeNode node1,NewBinaryTreeNode node2){
        if(root == null || node1 == null || node2 == null){
            return null;
        }
        int depth1 = findTheDepthOfTheNode(root, node1, node2);
        if(depth1 == -1){
            return node1.parentNode;
        }
        int depth2 = findTheDepthOfTheNode(root, node2, node1);
        if(depth2 == -1){
            return node2.parentNode;
        }
        //p指向较深的节点  q指向较浅的节点
        NewBinaryTreeNode p = depth1 > depth2 ? node1 : node2;
        NewBinaryTreeNode q = depth1 > depth2 ? node2 : node1;
        int depth =  Math.abs(depth1 - depth2);   

        while(depth > 0){
            p = p.parentNode;
            depth --;
        }
        while(p != q){
            p = p.parentNode;
            q = q.parentNode;
        }
        return p;
    }

    //求node1的深度,如果node1和node2在一条路径上,则返回-1,否则返回node1的深度
    public static int findTheDepthOfTheNode(NewBinaryTreeNode root,NewBinaryTreeNode node1,NewBinaryTreeNode node2){
        int depth = 0;
        while(node1.parentNode != null){
            node1 = node1.parentNode;
            depth ++;
            if(node1 == node2){
                return -1;
            }
        }
        return depth;
    }
    
   //含有指向父节点指针的树节点
    public static class NewBinaryTreeNode {
        public int value;
        public NewBinaryTreeNode parentNode;
        public NewBinaryTreeNode leftNode;
        public NewBinaryTreeNode rightNode;

        public NewBinaryTreeNode(int value){
            this.value = value;
            parentNode = null;
            leftNode = null;
            rightNode = null;
        }
    }
测试:
 /**
     *    // 形状普通的树
     *     //             1
     *     //           /   \
     *     //         2      3
     *     //        / \     /\
     *     //      4   5    6  7
     */
    private static void test04() {
        NewBinaryTreeNode A = new NewBinaryTreeNode(1);
        NewBinaryTreeNode B = new NewBinaryTreeNode(2);
        NewBinaryTreeNode C = new NewBinaryTreeNode(3);
        NewBinaryTreeNode D = new NewBinaryTreeNode(4);
        NewBinaryTreeNode E = new NewBinaryTreeNode(5);
        NewBinaryTreeNode F = new NewBinaryTreeNode(6);
        NewBinaryTreeNode G = new NewBinaryTreeNode(7);
        A.leftNode = B;
        A.rightNode = C;

        B.leftNode = D;
        B.rightNode = E;
        B.parentNode = A;

        C.leftNode = F;
        C.rightNode = G;
        C.parentNode = A;

        D.parentNode = B;

        E.parentNode = B;

        F.parentNode = C;

        G.parentNode = C;

        NewBinaryTreeNode res1 = getLowestCommonAncestor1(A,E,F);
        NewBinaryTreeNode res2 = getLowestCommonAncestor1(A,D,E);
        NewBinaryTreeNode res3 = getLowestCommonAncestor1(A,B,D);
        NewBinaryTreeNode res4 = getLowestCommonAncestor1(A,B,F);
        System.out.println("The lowest common ancestor of 5 and 6 is " +res1.value);
        System.out.println("The lowest common ancestor of 4 and 5 is " +res2.value);
        System.out.println("The lowest common ancestor of 2 and 4 is " +res3.value);
        System.out.println("The lowest common ancestor of 2 and 6 is " +res4.value);

    }
    打印结果:
    The lowest common ancestor of 5 and 6 is 1
	The lowest common ancestor of 4 and 5 is 2
	The lowest common ancestor of 2 and 4 is 2
	The lowest common ancestor of 2 and 6 is 1

解法四
此树不是二叉树,并且没有指向父节点的指针。

迭代解法如下:
需要我们保存下由root根节点到p和q节点的路径,并且将路径存入list中,则问题转化为求两个list集合的最后一个共同元素。  在这里插入图片描述
比如我们用前序遍历的方法来得到从根结点到H 的路径的过程是这样的:( 1 )遍历到A,把A 存放到路径中去,路径中只有一个结点A; ( 2 )遍历到B,把B 存到路径中去,此时路径为A->B; ( 3 )遍历到D,把D 存放到路径中去,此,时路径为A->B->D; ( 4 ) 遍历到F,把F 存放到路径中去,此时路径为A->B->D->F;( 5) F 已经没有子结点了,因此这条路径不可能到这结点H. 把F 从路径中删除,变成A->B->D; ( 6 )遍历G. 和结点F 一样,这条路径也不能到达H. 边历完G 之后,路径仍然是A->B->D; ( 7 )由于D 的所有子结点都遍历过了,不可能到这结点H,因此D 不在从A 到H 的路径中,把D 从路径中删除,变成A->B; ( 8 )遥历E,把E 加入到路径中,此时路径变成A->B->E, ( 9 )遍历H,已经到达目标给点, A->B->E 就是从根结点开始到达H 必须经过的路径。

同样,我们也可以得到从根结点开始到达F 必须经过的路径是A->B。接着,我们求出这两个路径的最后公共结点,也就是B. B这个结点也是F 和H 的最低公共祖先.
为了得到从根结点开始到输入的两个结点的两条路径,需要遍历两次树,每边历一次的时间复杂度是O(n)。

public class Demo1 {
		 /*
         * 获取两个节点的最低公共祖先
         */
        public static TreeNode getLastCommonParent(TreeNode root, TreeNode p1, TreeNode p2) {
            //path1和path2分别存储根节点到p1和p2的路径(不包括p1和p2)
            List<TreeNode> path1 = new ArrayList<TreeNode>();
            List<TreeNode> path2 = new ArrayList<TreeNode>();
            List<TreeNode> tmpList = new ArrayList<TreeNode>();

            getNodePath(root, p1, tmpList, path1);
            getNodePath(root, p2, tmpList, path2);
            //如果路径不存在,返回空
            if (path1.size() == 0 || path2.size() == 0)
                return null;

            return getLastCommonParent(path1, path2);
        }

        // 获取根节点到目标节点的路径
        public static void getNodePath(TreeNode root, TreeNode target, List<TreeNode> tmpList, List<TreeNode> path) {
            //鲁棒性
            if (root == null || root == target)
                return;
            tmpList.add(root);
            List<TreeNode> children = root.children;
            for (TreeNode node : children) {
                if (node == target) {
                    path.addAll(tmpList);
                    path.add(node);
                    break;//只是跳出循环,返回上一个递归
                }
                getNodePath(node, target, tmpList, path);
            }

            tmpList.remove(tmpList.size() - 1);
        }

        //将问题转化为求链表最后一个共同节点
        private static TreeNode getLastCommonParent(List<TreeNode> p1, List<TreeNode> p2) {
            TreeNode tmpNode = null;
            for (int i = 0; i < p1.size(); i++) {
                if (p1.get(i) != p2.get(i))
                    break;
                tmpNode = p1.get(i);
            }

            return tmpNode;
        }

        // 节点类
        private static class TreeNode {
            int val;

            List<TreeNode> children = new ArrayList<>();


            public TreeNode(int val) {
                this.val = val;
            }

            @Override
            public String toString() {
                return val + "";
            }
        }


        public static void main(String[] args) {
            test01();
            System.out.println("==========");
            test02();
            System.out.println("==========");
            test03();
        }

        // 形状普通的树
        //             1
        //           /   \
        //         2      3
        //        /         \
        //      4            5
        //     / \        /  |  \
        //    6   7      8   9  10
        public static void test01() {
            TreeNode n1 = new TreeNode(1);
            TreeNode n2 = new TreeNode(2);
            TreeNode n3 = new TreeNode(3);
            TreeNode n4 = new TreeNode(4);
            TreeNode n5 = new TreeNode(5);
            TreeNode n6 = new TreeNode(6);
            TreeNode n7 = new TreeNode(7);
            TreeNode n8 = new TreeNode(8);
            TreeNode n9 = new TreeNode(9);
            TreeNode n10 = new TreeNode(10);

            n1.children.add(n2);
            n1.children.add(n3);

            n2.children.add(n4);

            n4.children.add(n6);
            n4.children.add(n7);

            n3.children.add(n5);

            n5.children.add(n8);
            n5.children.add(n9);
            n5.children.add(n10);

            System.out.println(getLastCommonParent(n1, n9, n10));
           System.out.println("getLastCommonParent1 test01  n4:n6::" + getLastCommonParent1(n1, n4, n6));//

        }
打印结果:
getLastCommonParent1 test01::5
getLastCommonParent1 test01  n4:n6::4

        // 树退化成一个链表
        //               1
        //              /
        //             2
        //            /
        //           3
        //          /
        //         4
        //        /
        //       5
        private static void test02() {
            TreeNode n1 = new TreeNode(1);
            TreeNode n2 = new TreeNode(2);
            TreeNode n3 = new TreeNode(3);
            TreeNode n4 = new TreeNode(4);
            TreeNode n5 = new TreeNode(5);

            n1.children.add(n2);
            n2.children.add(n3);
            n3.children.add(n4);
            n4.children.add(n5);

            System.out.println(getLastCommonParent(n1, n4, n5));
        }
打印结果:

getLastCommonParent1 test02::4
        // 树退化成一个链表,一个结点不在树中
        //               1
        //              /
        //             2
        //            /
        //           3
        //          /
        //         4
        //        /
        //       5
        private static void test03() {
            TreeNode n1 = new TreeNode(1);
            TreeNode n2 = new TreeNode(2);
            TreeNode n3 = new TreeNode(3);
            TreeNode n4 = new TreeNode(4);
            TreeNode n5 = new TreeNode(5);
            TreeNode n6 = new TreeNode(6);

            n1.children.add(n2);
            n2.children.add(n3);
            n3.children.add(n4);
            n4.children.add(n5);

            System.out.println(getLastCommonParent(n1, n5, n6));
        }

    }
   
 
打印结果:

getLastCommonParent1 test03::null


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值