2023牛客寒假算法基础集训营4 F-清楚姐姐学树状数组

题目描述:

按照树状数组的形式,如图建立尺寸为N=2^k的二叉树。

计算对于树状数组生成的二叉树上编号为x的节点,分别在前序、中序、后序遍历中是第几个被遍历到的节点?

输入:

第一行输入两个整数k,q(0≤k≤60,1≤q≤1e5)表示树状数组的尺寸为N=2^k,查询的次数为q。

接下来q行每行输入一个正整数x(1≤x≤N)表示查询的节点编号。

输出:

对于节点x,输出其在该二叉树中前序,中序,后序的位置。

Solution:

我们先假设n是当前遍历到的节点值,d是当前层节点与左右节点的差值,cnt表示前/后序遍历当前点的起码位置。

例如上图中对于以6为节点的树,6即为n,而d=1。

对于d我们可以简单的观察得出即使N/(2^当前深度)(假定起始深度为1)

故我们只需初始时将d=n在每次遍历时d/=2即可得到所需值。

对于前序遍历:

特判一下2^k,若是则输出1。

然后从n=2^(k-1)出发,判断x和n的大小关系:

如果x==n,便输出cnt+1(需加上当前点自身)

如果x<n,那便是向左子树遍历,有前序遍历根左右规则,cnt只需+1,n=n-d,d/=2。

如果x>n,那便是向右子树遍历,有前序遍历根左右规则,此时cnt便得加上左子树所有节点的个数以及根节点,而又由树状数组的构造原理可知,此数值即为lowbit(n),故此时cnt+lowbit(n),n=n+d,d/=2.

对于中序遍历:

由题意可知,x本身即为答案。

对于后序遍历:

特判一下2^k,若是则输出N。

然后从n=2^(k-1)出发,判断x和n的大小关系:

如果x==n,由后序遍历左右根的规则可知,cnt需加上x所在子树的所有节点个数,即cnt+2*lowbit(x)-1。

如果x<n,那便是向左子树遍历,有后序遍历左右根规则,cnt不变,n=n-d,d/=2。

如果x>n,那便是向右子树遍历,有后序遍历左右根规则,此时cnt便得加上左子树所有节点的个数但不包含根节点,即cnt+lowbit(n)-1,n=n+d,d/=2.

C++code:

#include <bits/stdc++.h>
#define lowbit(x) (x & -x)
using namespace std;
typedef long long ll;
ll N;
ll k, q;
//bsh和esh函数参数表示分别为x为查询点,n是当前查询的根节点值,d表示当前层根节点到左右节点的差值,cnt表示前/后序遍历当前点的起码位置
ll bsh(ll x, ll n, ll d, ll cnt) {//计算前序遍历
    if (x == n)return cnt + 1;//输出时应加上x点自身
    else if (x < n) return bsh(x, n - d, d / 2, cnt + 1);
    else return bsh(x, n + d, d / 2, cnt + lowbit(n));
}
ll esh(ll x, ll n, ll d, ll cnt) {//计算后序遍历
    if (x == n)return cnt + 2 * lowbit(n) - 1;//输出时加上所有子节点的个数和自身
    else if (x < n) return esh(x, n - d, d / 2, cnt);
    else return esh(x, n + d, d / 2, cnt + lowbit(n) - 1);
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    cin >> k >> q;
    N = 1;
    for (int i = 1; i <= k; i++)N *= 2;
    while (q--) {
        ll x;
        cin >> x;
        if (x == N) cout << 1 << ' ' << x << ' ' << N << endl;//将起点特判
        else {
            cout << bsh(x, N / 2, N / 4, 1) << ' ' << x << ' ' << esh(x, N / 2, N / 4, 0) << endl;
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值