BST | 1064 完全二叉搜索树

OJ:https://www.patest.cn/contests/pat-a-practise/1064

(一)23分(3个case未过)代码

建树的规律是我瞎猜的。首先用样例数据分析。

10
1 2 3 4 5 6 7 8 9 0

对数据排序后:

0 1 2 3 4 5 6 7 8 9

有10个数据,因为是完全二叉树,底层应该有3个叶子,上层有1+2+4=7个结点。用以下代码计算:

    int up_num=1,t=1;
    while(up_num + t*2 < n){
        t*=2;
        up_num+=t;
    }
    int leaves_num=n-up_num;

0 1 2  |  3 4 5 6 7 8 9

将左侧叶子结点分割开,对右侧结点进行分析。

6就是根结点。计算方式是取中间。这个算法中所有的“取中间”都满足以下规律:

奇数个数:直接取中间

偶数个数:取中间靠右

定义以下函数计算:

int get_mid(int a,int b){
    if((a+b)%2){
        return (a+b)/2+1;
    }
    return (a+b)/2;
}

6就是从3到9取中间而来。之后就可以递归建树了。

6的左叶子是0到5取中间的3。

6的右叶子是7到9取中间的8。

……

23分代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>


#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 1010
#define MAX (1<<30)-1
#define V vector<int>

using namespace std;

int a[LEN];
int n;

typedef struct Node{
    int d;
    struct Node* l=NULL;
    struct Node* r=NULL;
    Node(int d=0):d(d){
    }
};

int get_mid(int a,int b){
    if((a+b)%2){
        return (a+b)/2+1;
    }
    return (a+b)/2;
}

Node * build_tree(int s,int e) {
    if(e<s) return NULL;
    if(e==s) return new Node(a[s]);
    int t=get_mid(s,e);
    Node *node=new Node(a[t]);
    node->l=build_tree(s,t-1);
    node->r=build_tree(t+1,e);
    return node;
}

int main(){
//    freopen("1064.txt","r",stdin);
    I("%d",&n);
    int i;
    FF(i,n) I("%d",&a[i]);
    sort(a,a+n);
    int up_num=1,t=1;
    while(up_num + t*2 < n){
        t*=2;
        up_num+=t;
    }
    int leaves_num=n-up_num;
    int root_i=get_mid(leaves_num,n-1);
    Node *root=new Node(a[root_i]);
    root->l=build_tree(0,root_i-1);
    root->r=build_tree(root_i+1,n-1);
    queue<Node*> q;
    q.push(root);
    int cnt=0;
    while(!q.empty()){
        Node* tmp=q.front();
        q.pop();
        cnt++;
        O("%d",tmp->d);
        if(cnt<n)
            O(" ");
        if(tmp->l)
            q.push(tmp->l);
        if(tmp->r)
            q.push(tmp->r);
    }
    return 0;
}

看了大佬的博客之后,才知道自己是多么naive。这题其实很简单,利用BST的性质:中序遍历的序列递增有序,再用dfs(其实深搜的本质就是中序遍历)和一个二叉堆来递归建树。最后都不用队列来模拟层序,直接把二叉堆依次输出就可以了。

AC代码:

#include <stdio.h>
#include <memory.h>
#include <math.h>
#include <string>
#include <vector>
#include <set>
#include <stack>
#include <queue>
#include <algorithm>
#include <map>


#define I scanf
#define OL puts
#define O printf
#define F(a,b,c) for(a=b;a<c;a++)
#define FF(a,b) for(a=0;a<b;a++)
#define FG(a,b) for(a=b-1;a>=0;a--)
#define LEN 10010
#define MAX 0x06FFFFFF
#define V vector<int>

using namespace std;

int n; 
int a[LEN] ,ans[LEN];
int p=-1;        //p是中序遍历序列的索引,初始化为-1,因为在操作中首先会被+1而变成0 

void dfs(int x){    //x是二叉堆(完全二叉树)的索引,根节点是0,叶子节点满足二倍关系 
    if(x>=n){    //超出界限
        p++;    //进行了一轮超出界限的操作,意味着操作序列的更新 
        return; //记得退出,不然会爆栈。找到目标解或者到了递归边界而退出是dfs的必须操作 
    }
    dfs(x*2+1);        //左子树递归 
    ans[x]=a[p];    //ans数组就是二叉堆,a是中序遍历序列。 
    dfs(x*2+2);        //右子树递归
}

int main(){
//    freopen("D:\input\A1064.txt","r",stdin);
    I("%d",&n);
    int i;
    FF(i,n) I("%d",&a[i]);
    sort(a,a+n);
    dfs(0);
    FF(i,n){
        O("%d",ans[i]);
        if(i!=n-1) O(" ");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/TQCAI/p/8530913.html