题目描述:
按照树状数组的形式,如图建立尺寸为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;
}