顺序存储的二叉树的最近的公共祖先问题

题目概述

给定一棵顺序存储的二叉树和编号为i和j的两个节点,求它们的最近公共祖先。

输入格式

第1行给出正整数n(≤1000),即顺序存储的最大容量;第2行给出n个非负整数,其间以空格分隔。其中0代表二叉树中的空结点(如果第1个结点为0,则代表一棵空树);第3行给出一对结点编号i和j。

输出格式

如果i或j对应的是空结点,则输出ERROR: T[x] is NULL,其中x是i或j中先发现错误的那个编号;否则在一行中输出编号为i和j的两个结点最近的公共祖先结点的编号和值,其间以1个空格分隔。

输入样例

15
4 3 5 1 10 0 7 0 2 0 9 0 0 6 8
11 4

输出样例

2 3

解题思路

考虑到顺序二叉树的特殊性质,我们可以通过二叉树节点编号的大小关系来简化问题。对于节点i,其左子节点为2i,右子节点为2i+1,而其父节点为i/2。因此,我们可以通过循环判断它们的大小关系来找到最近公共祖先。

具体思路为:

  1. 首先,我们输入顺序二叉树和节点编号i、j。

  2. 判断i和j是否代表错误节点,如果其中一个是错误节点,则输出相应信息。

  3. 如果i和j相等,则i即为节点的最近公共祖先,输出节点编号i和它的值即可。

  4. 如果以上判断不成立,我们可以通过循环判断i和j节点的大小关系来找到它们的最近公共祖先。具体地,假设i>j,我们将i变为它的父节点i/2。如果i<=j,则将j变为它的父节点j/2。如果i==j,则i即为它们的最近公共祖先。

参考代码

下面给出一种基于循环的实现方式。
首先,我们定义了一个整型数组 binTree,用于存储顺序二叉树的节点值。根据题意,输入顺序二叉树节点数 n 和节点值,并存储在数组 binTree 中。

然后,我们输入第二行两个节点编号 num1 和 num2。根据题意,我们需要输出节点 num1 和 num2 的最近公共祖先。我们调用了 findFather 函数来计算最近公共祖先节点编号 result,并将其存储在 result 变量中。

接着,我们使用条件判断语句对 result 进行判断,判断节点 num1 和 num2 是否是错误节点,或者它们是否代表同一个节点。如果以上判断不成立,则输出节点 num1 和 num2 的最近公共祖先节点编号和值。

int findFather(int a,int b)
{
    while(1)
    {
        if(a>b)
            a=a/2;
        else if(a<b)
            b=b/2;
        if(a==b)
            break;
    }
    return a;
}

findFather 函数的主体思路为,通过 while 循环依次判断节点编号的大小关系,从而找到它们的最近公共祖先节点编号。具体地:

  1. 如果 a > b,则将 a 变为 a/2,即将 a 变为其父节点。
  2. 如果 a < b,则将 b 变为 b/2,即将 b 变为其父节点。
  3. 如果 a = b,则 a 即为节点的最近公共祖先节点。

因为树的高度为 \(\log n\) 级别,所以在最坏情况下,该算法的时间复杂度为 \(O(\log n)\)

最后,根据题目要求输出答案即可。如果其中一个节点是错误节点,则输出相应的错误信息。

#include <stdio.h>

// 计算两个节点的最近公共祖先
int findFather(int a, int b);

int main() {
    int binTree[1001] = {0}; // 顺序存储二叉树,初始化所有节点值为 0

    // 输入顺序二叉树节点数 N 和节点值,存储在数组 binTree 中
    int N, i;
    scanf("%d", &N);
    for (i = 0; i < N; i++) {
        scanf("%d", &binTree[i]);
    }

    // 输入需要查询的两个节点编号 num1 和 num2
    int num1, num2, result = 0; // i 用在循环了,不用 i
    scanf("%d %d", &num1, &num2);

    // 计算 num1 和 num2 的最近公共祖先,存储在 result 中
    result = findFather(num1, num2);

    // 根据题目要求输出结果
    if (num1 == num2) { // 如果两个节点编号相同,则它们即为最近公共祖先,直接输出节点编号和节点值
        printf("%d %d\n", num1, binTree[num1 - 1]);
    } else if (binTree[num1 - 1] == 0) { // 如果 num1 代表一个空节点,则输出错误信息
        printf("ERROR: T[%d] is NULL\n", num1);
    } else if (binTree[num2 - 1] == 0) { // 如果 num2 代表一个空节点,则输出错误信息
        printf("ERROR: T[%d] is NULL\n", num2);
    } else { // 输出最近公共祖先的节点编号和节点值
        printf("%d %d\n", result, binTree[result - 1]);
    }

    return 0;
}

// 计算 num1 和 num2 的最近公共祖先
int findFather(int num1, int num2) {
    int ancestor = 0; // 最近公共祖先节点编号

    // 循环判断 num1 和 num2 的父节点是否相同,直到找到它们的最近公共祖先节点编号
    while (1) {
        if (num1 > num2) { // 如果 num1 的编号大于 num2,则将 num1 变为它的父节点
            num1 = num1 / 2;
        } else if (num1 < num2) { // 如果 num1 的编号小于 num2,则将 num2 变为它的父节点
            num2 = num2 / 2;
        } else { // 如果 num1 和 num2 的编号相同,则它们的最近公共祖先即为它们的父节点
            ancestor = num1;
            break;
        }
    }

    return ancestor;
}

posted @ 2023-04-11 19:25  许悠  阅读(203)  评论(0)    收藏  举报
浏览器标题切换
浏览器标题切换end
1 2 3
4