zoukankan      html  css  js  c++  java
  • 【2018.2.2-】你真的会二叉搜索树了吗?(普通版本+普通拓展)

    BST(Binary Search Tree,二叉搜索树)复习开始了>_<

     ps:所有题目如果未特别说明,时限均为1s。

    easy(简单模板):

    1.Falling Leaves(二叉搜索树练习)

    Description

       

     上图给出一个字母二叉树的图的表示。熟悉二叉树的读者可以跳过字母二叉树、二叉树树叶和字母二叉搜索树的定义,直接看问题描述。

     一棵字母二叉树可以是下述两者之一:

       1,它可以是空树;

       2,它可以有一个根结点,每个结点以一个字母作为数据,并且有指向左子树和右子树的指针,左右子树也是字母二叉树。

       字母二叉树可以用图这样表示:空树忽略不计;每个结点这样标识:结点所包含的字母数据;如果左子树非空,向左下方的线段指向左子树;如果右子树非空,向右下方的线指向右子树。

       二叉树的树叶是一个子树都为空的结点。在上图的实例中,有5个树叶结点,数据为B,D,H,P和Y。

       字母树的前序遍历定义如下:

       如果树为空,则前序遍历也是空的;

       如果树不为空,则前序遍历按下述次序组成:访问根结点的数据;前序遍历根的左子树;前序遍历根的右子树。

       上图中树的前序遍历是KGCBDHQMPY。

       在上图中的树也是字母二叉搜索树。字母二叉搜索树是每个结点满足下述条件的字母二叉树:

       按字母序,根结点的数据在左子树的所有结点的数据之后,在右子树的所有结点的数据之前。

       请考虑在一棵字母二叉搜索树上的下述的操作序列:

       删除树叶,并将被删除的树叶列出;

       重复这一过程直到树为空。

       从下图中左边的树开始,产生树的序列如图所示,最后产生空树。

        

       移除的树叶数据为

       BDHPY

       CM

       GQ

       K

       本题给出这样一个字母二叉搜索树的树叶的行的序列,输出树的前序遍历。

    Input

     输入给出一个或多个测试用例。每个测试用例是一个一行或多行大写字母的序列。

       每行给出按上述描述的步骤从二叉搜索树中删除的树叶。每行中给出的字母按字母序升序排列。测试用例之间用一行分隔,该行仅包含一个星号 (‘*’)。 

     在最后一个测试用例后,给出一行,仅给出一个美元标志 (‘$’)。输入中没有空格或空行。

    Output

       对于每个输入的测试用例,有唯一的二叉搜索树,产生树叶的序列。输出一行,给出该树的前序遍历,没有空格。

    Sample Input

    BDHPY
    CM
    GQ
    K
    *
    AC
    B
    $
    

    Sample Output

    KGCBDHQMPY
    BAC

     
    明显模板题,敲个BST就好咯。
     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 struct bst{
     4     char c;
     5     int lchild,rchild;
     6 }num[30];
     7 int N; //节点计数工具 
     8 char str[30][30];
     9 void add(int t,char c){
    10     if(c<num[t].c){ //如果c比当前点字母小,就放在左儿子里
    11         if(num[t].lchild==0){ //该点还没有左儿子,将c设为其左儿子 
    12             num[N].c=c;
    13             num[t].lchild=N++;
    14             return ;
    15         }
    16         else add(num[t].lchild,c);
    17     }
    18     else { //如果c比当前字母大,就放在右儿子里 
    19         if(num[t].rchild==0){ //该点还没有右儿子,将c设为其右儿子 
    20             num[N].c=c;
    21             num[t].rchild=N++;
    22             return ;
    23         }
    24         else add(num[t].rchild,c);
    25     }
    26 }
    27 void print(int t){
    28     printf("%c",num[t].c);
    29     if(num[t].lchild) print(num[t].lchild);
    30     if(num[t].rchild) print(num[t].rchild);
    31 }
    32 
    33 int main(){
    34     int i,j,cont1,len;
    35     while(~scanf("%s",str[0])){
    36         memset(num,0,sizeof(num));
    37         cont1=0;
    38         while(isupper(str[cont1][0])) scanf("%s",str[++cont1]);
    39         cont1--;
    40         
    41         N=0;
    42         num[N++].c=str[cont1][0];
    43         for(i=cont1-1;i>=0;i--){
    44             len=strlen(str[i]);
    45             for(j=0;j<len;j++)
    46                 add(0,str[i][j]); //从根节点开始遍历,将字母加入二叉树 
    47         }
    48         print(0);
    49         putchar('
    ');
    50         if(str[cont1+1][0]=='$') break;
    51     }
    52     return 0;
    53 }
     

    2.笛卡尔树

    Description

    让我们考虑一种特殊的二叉查找树,叫做笛卡尔树。回想一下,二叉查找树是有根有序的二叉树,这样,对于它的每一个节点x满足以下条件:在它的左子树的每个节点的数值小于x的数值,它的右子树的每个节点的数值大于x的数值。也就是说,如果我们用L(x)表示结点x的左子树,用R(x)表示结点x右子树,用kx表示该结点x的数值,那么对每个结点x我们有

    如果y ∈ L(x),那么ky < kx

    如果z ∈ R(x),那么kz > kx

    若一棵二叉查找树被称为笛卡尔树,那么它的每一个结点x除了主要数值kx外还有一个附加数值ax,且这个数值符合堆的条件,即

    如果y是x的父亲,那么ay < ax

    因此,一棵笛卡尔树是一棵有根有序的二叉树,这样,它的每个节点拥有两个数值(k , a)和满足上述的三个条件。

    给出一系列点,构建出它们的笛卡尔树,或检测构建出它们的笛卡尔树是不可能的。

    Input

    第一行包括一个整数N(1 <= N <= 50 000),表示你要构建的笛卡尔树的点的对数。

    接下来N行包括两个数字,k,a,|k|, |a| <= 30 000,保证每行的k和a是不同的。

    输入保证能构建出笛卡尔树的只有一种情况。

    Output

    如果能构建出笛卡尔树则在第一行输出YES否则输出NO。

    如果是YES,则在接下来N行输出这棵树。第i+1行输出第i个结点的父亲,左儿子,右儿子。如果这个结点无父亲或者儿子,则用0代替。

    Sample Input

    7 
    5 4 
    2 2 
    3 9 
    0 5 
    1 3 
    6 6 
    4 11  

    Sample Output

    YES 
    2 3 6 
    0 5 1 
    1 0 7 
    5 0 0 
    2 4 0 
    1 0 0 
    3 0 0  

    额,笛卡尔树的定义都告诉你了,还不照着打一遍?
    给定一个带两种值的序列,一定能构建出一棵笛卡尔树,并且只能建出唯一一棵满足条件的。构建方法已经写了详细注释,请读者慢慢yy。
    #include<bits/stdc++.h>
    using namespace std;
    
    const int BufferSize=1; //由于read中是使用Getchar一位一位地读,数据项的个数为1
    char buffer[BufferSize],*head,*tail;
    int len;
    inline char Getchar(){
        if(head==tail){
            len=fread(buffer,1,BufferSize,stdin);
            tail=(head=buffer)+len;
        }
        return *head++;
    }
    inline int read(){
        int x=0,f=1;char c=Getchar();
        for(;!isdigit(c);c=Getchar()) if(c=='-') f=-1;
        for(;isdigit(c);c=Getchar()) x=x*10+c-'0';
        return x*f;
    }
    
    struct tree{
        int k,a,id,l,r;
    }t[50001];
    int n,root,f[50001],l[50001],r[50001];
    
    bool cmp(tree x,tree y){return x.k<y.k;} //按照k排序(bst顺序) 
    
    /*强行脑补一下笛卡尔树的构建 
    index(k)为二叉搜索树的值,key(a)为堆的值 
    我们按照点的标号index 1-n不断插入节点,因为当前点的index比之前插入的都大,所以根据二叉搜索的性质应该从这棵树的最右边插入。
    又要满足堆的性质,所以我们插入一个点A的时候,沿这棵树的最右节点不断向上找,直到找到第一个权值比它小的点B,根据标号,A的左孩子就是B的右孩子,而A就是B的右孩子了(这实际上似乎就是某种旋转)
    实际实现的时候我们可以用栈维护这棵树的最右子树(一条从根节点出发全部指向右孩子的链,请自行脑补),不断弹出顶部节点以便找到第一个权值key比它小的点,再更改关系即可。由于每个点都只能进出一次栈,所以复杂度为O(n).
    */
    int build(){
        stack<int>st;
        st.push(0); //抽象解释:节点0的右儿子是树根。先插入0是给第38行的while判断的第一条用的,防止队列被弹空。如果队列被弹到这里,说明插入点要放在根节点。 
        for(int i=1;i<=n;i++){
            while(st.top() && t[st.top()].a>t[i].a) st.pop(); //把k和a都比当前点大的点都弹出,最后st.top()就是第一个权值key比当前点小的点了 
            t[i].l=t[st.top()].r; //st.top()的k小于当前k,a小于当前a,因此当前点在st.top()的右子树中。把这个点(B)的右儿子设为当前点(A)的左儿子
            t[st.top()].r=i; //st.top()的右儿子(B)设为当前点(A) 
            st.push(i); //将当前点推入最右子树队列 
        }
        while(st.top()) root=st.top(), st.pop(); //最后栈最底端的元素(st[0])就是根了。因为这个点最早插入树且仍然在右子树链中,那它就是最右子树的根,也就是整棵树的根咯 
    }
    
    void dfs(int now){ //查询一下每个点的父亲、左子树、右子树,最后输出用 
        if(t[now].l){ //左子树 
            f[t[t[now].l].id]=t[now].id; //记录左儿子的父亲为自己 
            l[t[now].id]=t[t[now].l].id; //记录自己的左儿子
            dfs(t[now].l);
        }
        if(t[now].r){ //右子树 
            f[t[t[now].r].id]=t[now].id; //记录右儿子的父亲为自己 
            r[t[now].id]=t[t[now].r].id; //记录自己的左儿子
            dfs(t[now].r);
        }
    }
     
    int main(){
        n=read();
        for(int i=1;i<=n;i++) t[i].k=read(),t[i].a=read(),t[i].id=i; //id用于标记树中的点原来的位置,方便最后按顺序输出 
        sort(t+1,t+1+n,cmp);
        build(); 
        dfs(root);
        printf("YES
    "); //
        for(int i=1;i<=n;i++) printf("%d %d %d
    ",f[i],l[i],r[i]);
    }
    
    

     

    3.Binary Search Heap Construction(treap练习,不过跟笛卡尔树有个毛区别)

    Description

    堆是这样的一种树,每个内结点被赋了一个优先级(一个数值),使得每个内结点的优先级小于其双亲结点的优先级。因此,根结点具有最大的优先级,这也是堆可以用于实现优先级队列和排序的原因。

    在一棵二叉树中的每个内结点有标号和优先级,如果相应于标号它是一棵二叉搜索树,相应于优先级它是一个堆,那么它就被称为treap。给出一个标号-优先级对组成的集合,请构造一个包含了这些数据的treap。

    Input

    输入包含若干测试用例,每个测试用例首先给出整数n,本题设定1<=n<=50000;后面跟着n对的字符串和整数l1/p1, ..., ln/pn,表示每个结点的标号和优先级;字符串是非空的,由小写字母组成;数字是非负的整数。最后的一个测试用例后面以一个0为结束。

    Output

    对每个测试用例在一行中输出一个treap, treap给出顶点的说明。Treap的形式为 (< left sub-treap >< label >/< priority >< right sub-treap >). 子treaps递归书出,如果是树叶就没有输出。

    Sample Input

    7 a/7 b/6 c/5 d/4 e/3 f/2 g/1
    7 a/1 b/2 c/3 d/4 e/5 f/6 g/7
    7 a/3 b/6 c/4 d/7 e/2 f/5 g/1
    0
    

    Sample Output

    (a/7(b/6(c/5(d/4(e/3(f/2(g/1)))))))
    (((((((a/1)b/2)c/3)d/4)e/5)f/6)g/7)
    (((a/3)b/6(c/4))d/7((e/2)f/5(g/1)))

    这里的treap根笛卡尔树有什么区别,于是再来一发。
    #include<iostream>
    #include<cstring>
    #include<stack>
    #include<algorithm>
    using namespace std;#define maxn 50010
    stack<int> st;
    int n;
    char str[10];
    struct node{
      char name[10];
        int num;
        int l,r;
        void clear(){l=r=0;}
    }a[maxn];
    bool cmp(node z1,node z2){
        return strcmp(z1.name,z2.name)<0;
    }
    int build(){
        st.push(0);
        for(int i=1;i<=n;i++){
             while(st.top() && a[st.top()].num<a[i].num)st.pop();
             a[i].l=a[st.top()].r;
             a[st.top()].r=i;
             st.push(i);
        }
        int ans;
        while(st.top()){ans=st.top();st.pop();}
        return ans; //返回树根
    }
    void dfs(int u){
        if(!u) return;
        printf("(");
        if(a[u].l) dfs(a[u].l);
        printf("%s/%d",a[u].name,a[u].num);
        if(a[u].r) dfs(a[u].r);
        printf(")");
    }
    int main(){
        while(scanf("%d",&n)){
            if(n==0) break;
            for(int i=0;i<=n;i++) a[i].clear(); 
            for(int i=1;i<=n;i++){
                scanf("%s",str);
                sscanf(str,"%[^/]/%d",a[i].name,&a[i].num);
            }
            sort(a+1,a+n+1,cmp);
            int root=build();
            dfs(root);
            putchar('
    ');
        }
        return 0;
    }
    
    
    
    
  • 相关阅读:
    yii required 字段去除*号
    shtml用include加载文件,apache开启SSI,及shtml压缩
    门户站点用html或shtml文件格式的原因
    Apache配置shtml
    数据库迁移工具
    SQL SERVER2005 级联删除
    MySql级联删除和更新
    js只允许输入数字和小数点
    表格中的数据为空时,td的边框不显示
    MYSQL: Cannot delete or update a parent row: a foreign key constraint fails
  • 原文地址:https://www.cnblogs.com/scx2015noip-as-php/p/BST.html
Copyright © 2011-2022 走看看