二叉树的最近公共祖先

求二叉树中两个节点 的最近公共祖先。

二叉树
注意,图中的所有节点唯一。如节点5和4的公共祖先节点为5,1和5的公共祖先节点为3。

在做了一些二叉树的题后,本能的使用了递归的做法写出了代码并调试通过了。虽然通过了,但是还是对其细节不太清楚。 此处再通过代码来理解其中的思想并记录。如下

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (root == null) {
        return root;
    }

	// root 等于p 或 q中的任一个,那么公共的祖先就是此节点。
    if (root == p) {
        return p;
    }

    if (root == q) {
        return q;
    }
	// 搞懂原理后,以上三个if条件可以合并成一个。 均返回root即可。 
	
	// 递归在左子树中找祖先。当p与q不同在root.left的子树中时,返回的必然是 p 或 q 中的一个(由上面的if条件可知)。 注意,要理解这个。 
    TreeNode left = lowestCommonAncestor(root.left, p, q);
    // 同理,在右子树中找祖先。
    TreeNode right = lowestCommonAncestor(root.right, p, q);
    
    // 而如果p 或 q 同在root.left上,那么right 即为null。反之亦然。

	// left与right不等,说明 p 与 q 既不同在root.left,也不同在root.right。而是分布在root的左右两棵子树中。所以root是公共祖先。
	// 如果相等(相等的时候,left与right都为null,因为公共祖先不可能同时在左子树和右子树中找到), 此时没有公共祖先,返回null。
    return left != right ? root : left;
}

在上面基础上简化出另一种形式的代码如下:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
	   if (root == null || root == q || root == p) {
	       return root;
	   }
	
	   TreeNode left = lowestCommonAncestor(root.left, p, q);
	   TreeNode right = lowestCommonAncestor(root.right, p, q);
	
		// 分布在root的左右子树两侧。 
	   if (left != null && right != null) {
	       return root;
	   }
	   
	   return left == null ? right : left;
}

如果在修改此题为查找二叉搜索树中两节点的公共祖先,则可以通过二叉搜索树的性质简化代码如下:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (p.val > root.val && q.val > root.val) {
    	// 如果 p 和 q 均处在右子树,那么只在右子树中找
        return lowestCommonAncestor(root.right, p, q);
    } else if (p.val < root.val && q.val < root.val) {
    	// 同理,只在左子树中查找。 
        return lowestCommonAncestor(root.left, p, q);
    } else {
    	// 当p,q分别在root节点的两侧,那么此时root就是公共祖先了,故此时返回结果。 
    }
    return root;
}

用迭代的方式如下:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    while (root != null) {
        // 在右子树上
        if (root.val < p.val && root.val < q.val) {
            root = root.right;
        } else if (root.val > q.val && root.val > p.val) {
            // 同理
            root = root.left;
        } else {
            break;
        }
    }

    return root;
}

如果事先确定p与q的大小,则可以减少while中的条件比较。 如下:

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    if (p.val > q.val) {
        TreeNode tmp = p;
        p = q;
        q = tmp;
    }
    // 保证节点q的值大

    while (root != null) {
        // 小的比root大,则大的一定比root大
        if (root.val < p.val) {
            root = root.right;
        } else if (root.val > q.val) {
            // 同理
            root = root.left;
        } else {
            break;
        }
    }

    return root;
}

因为极端条件下要遍历所有的节点,故以上算法时间复杂度为O(N)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值