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('\n'); 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\n"); //额 for(int i=1;i<=n;i++) printf("%d %d %d\n",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
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('\n'); } return 0; }