二叉树的子树
Time Limit:3000MS Memory Limit:65536K
Description
如上图所示,由正整数1,2,3……组成了一颗二叉树。我们已知这个二叉树的最后一个结点是n。现在的问题是,结点m所在的子树中一共包括多少个结点。
比如,n = 12,m = 3那么上图中的结点13,14,15以及后面的结点都是不存在的,结点m所在子树中包括的结点有3,6,7,12,因此结点m的所在子树中共有4个结点。
Input
输入数据包括多行,每行给出一组测试数据,包括两个整数m,n (1 <= m <= n <= 1000000000)。最后一组测试数据中包括两个0,表示输入的结束,这组数据不用处理。
Output
对于每一组测试数据,输出一行,该行包含一个整数,给出结点m所在子树中包括的结点的数目。
Sample Input
3 12
0 0
Sample Output
4
Hint
这一题如果从查询的点出发往下遍历它的左子树和右子树,是肯定会超时的,因此不应遍历每个点,而是逐层遍历。
以样例为例,从3出发逐层向下遍历,每次找它的右儿子结点,即3*2+1,以此类推找到每个当前点编号i的编号i*2+1点,直到i*2+1>n;这样就相当于把所有满结点的层遍历完了。
那么这些层包含了多少点呢?排除最后一层抽出来看其实它是一棵完全二叉树,假如有i层,它的点数为2的i-1次方-1,而层数可以通过遍历时求出;至于最后一层的结点,可以再回到m进行遍历,找到它最左方的叶子结点k,那么最后一层的结点数就为n-k+1.当然,如果n<k,最后一层就没有结点了。所以子树的总点数就显而易见了。
代码:
#include
#include
using namespace std;
int n,m;
int main()
{
while (true)
{
scanf("%d%d",&m,&n);
if (n==0&&m==0) break;
int ans=0;
int i=0;
int m1=m; //m1用来找左子树叶子结点,而m遍历每层右子树叶子结点
while (m<=n)
{
m=m*2+1;
ans+=int(pow(double(2),i));
i++;
}
m1*=int(pow(double(2),i-1));
if (m1*2>n) printf("%d\n",ans); else printf("%d\n",ans+n-m1*2+1);
}
}
#include
using namespace std;
int n,m;
int main()
{
while (true)
{
scanf("%d%d",&m,&n);
if (n==0&&m==0) break;
int ans=0;
int i=0;
int m1=m; //m1用来找左子树叶子结点,而m遍历每层右子树叶子结点
while (m<=n)
{
m=m*2+1;
ans+=int(pow(double(2),i));
i++;
}
m1*=int(pow(double(2),i-1));
if (m1*2>n) printf("%d\n",ans); else printf("%d\n",ans+n-m1*2+1);
}
}