【PAT A1064】Complete Binary Search Tree
题目大意:给你一个序列,要求用这个序列建一个完全二叉搜索树,并输出这棵完全二叉搜索树的层次遍历序列。(注:给定的序列里的数各不相同,能保证建出来的完全二叉搜索树唯一)
Sample Input:
10 1 2 3 4 5 6 7 8 9 0
Sample Output:
6 3 8 1 5 7 9 0 2 4
方法思路:对给定的序列先进行排序形成有序序列,在有序序列里找到划分点(即树根),划分左右子树,然后再分别左右递归建树。
找划分点的方法:
一棵树如果是满二叉树,则树高和结点个数的关系为 2h+1 - 1 = N (其中h为树的高度,规定根结点的高度为0,N为满二叉树的结点个数)。也即 h = log2(N+1) - 1 。
考察题目当前给我们的 n 个结点(n不一定是满二叉树个结点),这里假设 n 不足满二叉树,即最底层的结点个数不满。
如:
graph TD
C[6]
C-->D[3]
C-->E[8]
D-->F[1]
D-->G[5]
F-->F1[0]
F-->F2[2]
G-->H[4]
E-->E1[7]
E-->E2[9]
1.计算树高当前 int h = log2(n+1)
2.计算除最底层之外,以上的结点个数 2^h - 1
3.计算最底层结点个数 bottomNodes = n - (2^h - 1)
4.计算 最底层若是满的情况应有结点个数 fullBottomNodes = 2^h
5.分情况:
- 若 ( bottomNodes <= fullBottomNodes /2 ) 即最底层左边没满,则此时整棵树的左边结点总数为
leftNodes = upNodes/2 + bottomNodes 即 上面的一半加最底层实际所含结点个数
- 若( bottomNodes > fullBottomNodes /2 ) 即最底层左边满了,那么此时整棵树的左边结点总数为
leftNodes = upNodes/2 + fullBottomNodes /2 即 上面的一半加最底层满的一半
6.找到树的左边结点个数就可以用 left + leftNodes 来找到划分结点对应下标了 (序列区间 [ left,right ] )
实际上上述划分方法对满二叉树也同样试用,可亲自模拟。
示例代码:
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<queue>
using namespace std;
const int MAXN = 1010;
int seq[MAXN];
struct node{
int data;
node* lchild;
node* rchild;
};
int N;
int squrt(int a){
return a*a;
}
//2的快速幂,n为指数,即2^n
int pow2Q(int n){
if(n == 0)
return 1;
return (n&1==1)?squrt(pow2Q(n>>1))<<1:squrt(pow2Q(n>>1));
}
/*
在有序序列的闭区间[left,right]递归寻找划分点
并创建树
返回树根结点指针
*/
node* create(int left,int right){
if(left > right)
return NULL;
int allNum = right - left + 1;
double depth = log(allNum+1)/log(2);
int in_depth = (int)depth;//当前树高,规定根结点高度为0
int upNodes = pow2Q(in_depth) - 1;//除去最底层,上面的结点总数
int bottomNodes = allNum - upNodes;//最底层的结点总数
int fullBottomNodes = upNodes + 1;//如果最底层是满的应有的结点总数
int leftNodes;//根结点的左子树结点个数
if(bottomNodes <= fullBottomNodes/2){//不足一半
leftNodes = upNodes/2 + bottomNodes;
}else{
leftNodes = upNodes/2 + fullBottomNodes/2;
}
int divData = seq[left+leftNodes];//划分结点,将有序数组划分为左右子树
// printf("divData%d,leftNodes:%d\n",divData,leftNodes);
node* root = new node;
root->data = divData;
root->lchild = create(left,left+leftNodes-1);//继续往划分出来的左边递归创建左子树
root->rchild = create(left+leftNodes+1,right);//继续往划分出来的右边递归创建左子树
return root;
}
//层次遍历
int layPrNum = 0;
void layerTravel(node* root){
queue<node*> Q;
Q.push(root);
while(!Q.empty()){
node* aNode = Q.front();
Q.pop();
layPrNum++;
printf("%d",aNode->data);
if(layPrNum < N)
printf(" ");
else
printf("\n");
if(aNode->lchild != NULL){
Q.push(aNode->lchild);
}
if(aNode->rchild != NULL){
Q.push(aNode->rchild);
}
}
}
int main(){
scanf("%d",&N);
for(int i=0;i<N;i++){
scanf("%d",&seq[i]);
}
sort(seq,seq+N);
node*root = create(0,N-1);
layerTravel(root);
return 0;
}