无穷大二叉树的最近公共祖先

[题目链接](https://www.nowcoder.com/practice/7db533ff46704fd3922c1b26d2cf6fd5)

思路

有一棵无穷大的满二叉树,结点按层序从 1 开始编号。给定两个结点 ,求它们的最近公共祖先(LCA)的编号。

满二叉树的性质

在按层序编号的满二叉树中,结点 的父结点编号为 。这是因为第 层有 个结点,编号从 ,左孩子编号为 ,右孩子编号为

求 LCA 的方法

利用父结点 = 这一性质,不断将编号较大的结点向上移动(除以 2),直到两个结点相遇:

  • ,令
  • ,令
  • 时,即为 LCA。

这个过程的正确性在于:编号大的结点一定在更深的层或更靠右的位置,每次除以 2 就是向上走一层。两个结点最终一定会在它们的公共祖先处汇合。

样例演示

输入

  • ,输出 1

复杂度分析

  • 时间复杂度:,每次操作将较大值至少减半。
  • 空间复杂度:,只用常数额外空间。

代码

#include <iostream>
using namespace std;
int main() {
    long long a, b;
    cin >> a >> b;
    while (a != b) {
        if (a > b) a /= 2;
        else b /= 2;
    }
    cout << a << endl;
    return 0;
}
import java.util.Scanner;
public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        long a = sc.nextLong();
        long b = sc.nextLong();
        while (a != b) {
            if (a > b) a /= 2;
            else b /= 2;
        }
        System.out.println(a);
    }
}
a, b = map(int, input().split())
while a != b:
    if a > b:
        a //= 2
    else:
        b //= 2
print(a)
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
rl.on('line', function(line) {
    const [a, b] = line.trim().split(' ').map(Number);
    let x = a, y = b;
    while (x !== y) {
        if (x > y) x = Math.floor(x / 2);
        else y = Math.floor(y / 2);
    }
    console.log(x);
    rl.close();
});