【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;
}
posted @ 2019-07-25 20:15  dekeshile  阅读(72)  评论(0编辑  收藏  举报