笛卡尔树:
每个节点有2个关键字key、value。从key的角度看,这是一颗二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大;从value的角度看,这是一个堆。
题意:以字符串为关键字key,数字为关键字value,构造一个二叉搜索大堆,最后按要求中序遍历 笛卡尔树的构造。
建立笛卡尔树的O(n)的算法:
从别人博客里拷贝过来的,这里给出链接:http://hi.baidu.com/yy17yy/item/cd4edcf963944f6a3d148553
首先按key关键字进行排序,这样建树的时候从左下角往右下角建。根据定义可知,每个数组的末尾节点必是位于树的最右路上且不可能有右孩子。 在建立树的过程中每次加入一个元素A[X],则该节点一定位于此时的树的最右路上且无右孩子。所以只要沿着A[X-1]节点向上走, 找到比A[X]大的第一个节点A[K],则A[X]是A[K]的右孩子,且A[K]原来的右孩子此时将成为A[X]的左孩子。 若找不到A[K]则此时的树的根将作为A[X]的左孩子,A[X]将成为树的新的根节点。
可以模拟一个虚根,其priority设为无穷,这样最后构造出来的树即为虚根的右子树,方便编写程序。
我原来排序时,是自己写了比较器,传给sort方法,969MS。后来在Node结构体中重载运算符<,625MS。
代码中scanf的用法,详见:http://blog.csdn.net/wesweeky/article/details/6439777
#include <iostream> #include <cstdio> #include <string.h> #include <queue> #include <algorithm> #include <string> using namespace std; const int INF=0x3f3f3f3f; const int maxn=1<<16; int fa[maxn]; //存储父节点的编号 int son[maxn][2]; //son[i][0]存储i的左儿子编号,son[i][1]存储i的右儿子编号 int n; struct Node{ char s[110]; //字符串 int p; //优先级 bool operator<(const Node tmp)const{ return strcmp(s,tmp.s)<0; } }node[maxn]; /* bool cmp(const Node tmp1,const Node tmp2){ if(strcmp(tmp1.s,tmp2.s)<0) return 1; else return 0; } */ void treap(int i){ int tmp=i-1; //直到找到一个tmp,使得tmp的优先级大于i while(node[tmp].p<node[i].p){ tmp=fa[tmp]; } //tmp的原先的右儿子即为i的左儿子,i成为tmp的右儿子 son[i][0]=son[tmp][1]; son[tmp][1]=i; fa[i]=tmp; } //中序遍历 void dfs(int i){ if(i==0) return; printf("("); //递归左儿子 dfs(son[i][0]); printf("%s/%d",node[i].s,node[i].p); //递归右儿子 dfs(son[i][1]); printf(")"); } int main() { //这里为方便起见,设立了一个优先级很大的虚根,之后建的树为虚根的右子树 node[0].p=INF; while(scanf("%d",&n),n){ memset(son,0,sizeof(son)); memset(fa,-1,sizeof(fa)); for(int i=1;i<=n;i++){ //[a-z]表示读取的字符串由a-z中的字符组成,其余的字符为定界符 scanf(" %[a-z]/%d",node[i].s,&node[i].p); } //sort(node+1,node+n+1,cmp); sort(node+1,node+n+1); for(int i=1;i<=n;i++){ treap(i); } //从虚根的右儿子开始dfs dfs(son[0][1]); printf(" "); } return 0; }