阿里巴巴2016年秋季校园招聘C++研发岗在线笔试附加题第一题

本文介绍了一种特殊的二叉树构建方法,该树左子树的a值小于根节点,右子树的a值大于根节点,同时整体构成一个按b值建立的大顶堆。文章详细阐述了构建流程及插入新数据的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一次参加在线笔试,没适应节奏,也忘记对题目截图或复制了。下面大概描述一下题目意思:

题目给出了两个结构定义:

struct pair_t {
    int a, b;
};

struct node_t {
    int a, b;
    node_t *left, *right;
};

其中 pair_t 结构表示输入的数据对,node_t 结构表示需要实现的二叉树结构。

1,题目的输入是若干 pair_t 结构的数据,然后要求建立一棵二叉树,要求二叉树的左子树的 a 值小于根节点的 a 值,右子树的 a 值大于根节点的 a 值,且二叉树是一个按 b 值建立的大顶堆。

题目还有个提示:(1)二叉树存在且唯一; (2)先确定根节点。

2,第二问是对于新输入的 pair_t 结构数据,如何插入使其仍然满足该二叉树的性质,因为算法比较复杂,所以只要求给出思路。

(题目大概意思是这样)

 

我解题的思路:

1,

首先按照 pair_t 结构数据的 a 值排序;

然后根据 pair_t 的 b 值找到根节点;

再根据根节点找到左右子树(pair_t 结构数组中,排序之后,在根节点左边的就是左子树,在右边的就是右子树);

然后重复这一过程即可完成建树。

 

第一问的代码:

#include <iostream>

using namespace std;

//输入的数据结构
struct pair_t {
    int a, b;
};

//二叉树节点结构
struct node_t {
    int a, b;
    node_t *left, *right;
};

/*
*   因为要求大顶堆的左子树的 a 值小于根节点的 a 值,且右子树的 a 值大于根节点的 a 值,所以按 a 排序;
*   下面是快排函数
*/
void sort_pair_a(pair_t* pair, int start, int end)
{
    if(start >= end)
        return ;

    int begin = start;
    int finish = end;

    pair_t value;
    value = pair[start];

    while(start < end) {
        for( ; start < end; --end)
            if(pair[end].a < value.a) {
                pair[start++] = pair[end];
                break;
            }

        for( ; start < end; ++start)
            if(pair[start].a > value.a) {
                pair[end--] = pair[start];
                break;
            }
    }

    pair[start] = value;

    sort_pair_a(pair, begin, start);
    sort_pair_a(pair, start+1, finish);
}

/*
*   下面是根据数据对的 b 值建大顶堆;
*   试题中其实已经提示大家要先找根节点,所谓根节点,也就是大顶堆的顶,即 b 值最大的数据对;
*   由于通过排序将数据对按 a 的升序排列,所以根据找到的根节点就可以很轻松的确定左右子树了,然后重复这一过程建树;
*
*   按理说对 pair[] 只需要一次排序即可,但是试题只要求写出 build() 函数,所以我这里的快排是放在 build() 函数中的;
*   我在想,其实不排序也可以的,只要先遍历一遍找到根节点,然后再遍历找到左子树和右子树的节点,再建树就可以了;
*   话说回来,排序后理解起来更容易,操作也简单,反正排序函数不难敲对吧 ^.^
*/
node_t* build(pair_t* pair, int n)
{
    sort_pair_a(pair, 0, n-1);    //对数组 pair 按 a 的值做升序排序。这个排序函数其实可以放在 main() 函数中的,只是题目只要求给出 build() 函数的实现,所以我就放在这里了。

    node_t* T = new node_t;
    pair_t temp;
    temp = pair[0];
    int i, loc=0;

    //找根节点
    for(i=1; i < n; i++)
        if(temp.b < pair[i].b) {
            temp = pair[i];
            loc = i;
        }

    T->a = temp.a;
    T->b = temp.b;

    //左子树
    if(loc > 0)
        T->left = build(pair, loc); //数组pair中的数从位置 0 开始存,所以这里的长度是 loc 而不是 loc-1;
    else
        T->left = NULL;

    //右子树
    if(n-loc-1 > 0)
        T->right = build(pair+loc+1, n-loc-1);    //和上一条语句一样,要注意一下数组起始位置和长度中的 +1 和 -1;
    else
        T->right = NULL;

    return T;   //返回根节点
}


//先序遍历
void print_prior(node_t* p)
{
    if(p == NULL)
        return ;
    if(p != NULL)
        cout<<"("<<p->a<<", "<<p->b<<") ";//<<endl;
    print_prior(p->left);
    print_prior(p->right);
}

//中序遍历
void print_mid(node_t* p)
{
    if(p == NULL)
        return ;

    print_mid(p->left);
    if(p != NULL)
        cout<<"("<<p->a<<", "<<p->b<<") ";//<<endl;
    print_mid(p->right);
}

//后序遍历
void print_last(node_t* p)
{
    if(p == NULL)
        return ;
    print_last(p->left);
    print_last(p->right);
    if(p != NULL)
        cout<<"("<<p->a<<", "<<p->b<<") ";//<<endl;
}

#define N 1000
int main()
{
    pair_t pair[N];
    int n;
    cin>>n;
    for(int i=0; i < n; i++)
        cin>>pair[i].a>>pair[i].b;

    node_t* T = build(pair, n);

    print_prior(T);
    cout<<endl;
    print_mid(T);
    cout<<endl;
    print_last(T);
    cout<<endl;

    return 0;
}
/*
测试样例:
6
5 8
2 10
4 3
1 5
0 2
9 1
*/

 

 

2,第二问我之前的思路:

1,首先将要插入的节点的 b 值与根节点的 b 值比较:

              1)如果小于根节点的 b 值,转步骤 2;

              2)如果大于根节点的 b 值,则将该节点与根节点交换,让它成为新的根节点,然后用原来的根节点作为要插入的节点,重复步骤 1;

2,将该节点的 a 值与根节点的 a 值进行比较:

              1)如果小于根节点的 a 值,则将其插入左子树,转步骤 1;

              2)如果大于根节点的 a 值,则将其插入右子树,转步骤 1;  

//题目已经说明,输入的数据满足要求,二叉树存在且唯一,所以不会存在 a 和 b 值都相等的情况,即不会出现完全一样的节点。

 

不过后来发现第二问的思路肯定是不对的,因为这个思路可能会破坏 a 值的性质,所以正确的思路应该要先根据 a 值找到合适的位置,然后再进行调整;不过暂时还没完全理清头绪,等确定了再更新。

这里给出第 2 问的错误思路,是告诉和我有过这种思路的同学再思考一下。

 

 

上面就是我对该题的理解和解答。这篇博客并非给出标准的答案,只是跟大家交流,如果博客中存在错误,欢迎大家指出,谢谢!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值