笛卡尔树

      笛卡尔树是一棵二叉树,树的每个节点有两个值,一个为key,一个为value。光看key的话,笛卡尔树是一棵二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大;光看value的话,笛卡尔树有点类似堆,根节点的value是最小(或者最大)的,每个节点的value都比它的子树要大。


       zoj_2243笛卡尔树的构造,开始被topcoder上的教程吸引去看这题。题目中说这种数据结构叫treap然后就按照treap的insert加上左旋右旋去做了,结果果断TLE。然后网上找了一下关于笛卡尔树的资料,发现了一些问题的端倪。

       首先,treap和笛卡尔树从形式上来说是完全一样的,即是一颗二叉搜索树和二叉堆的结合(不是严格意义上的二叉堆,因为二叉堆是一颗完全二叉树,而treap不是)。但是他们的目的也许不太一样。treap更大的意义在于为了维持BST的平衡型而找到的一种随即化构造的方案。在treap中,insert的时候节点的value值是随机生成的。因此在逐个insert节点的时候能够保证treap的统计意义上的平衡。然而,笛卡尔树的目的更多是为了rmq问题和LCA问题的转化。

       其次,我们可以看到,在zoj_2243中,如果使用treap,按照题意一个个插如节点,随即化就被打破了,judge系统的数据也许不随机,从而导致treap的链化,弱化了平衡性,理想的NlogN的复杂度,可能退化到N^2,导致最后的TLE(可能左旋右旋操作的消耗也对应能有所影响).

       最后,找到了一种排序之后直接构造笛卡尔树的方法。首先将节点序列按照key从小到大排序,然后按照顺序插入节点,注意到排序之后,插入的节点的key值一定是树中最大的,所以只需查找最右端的路径,找到一个节点A[i]的value大于待插入节点的value,同时A[i]->right的value小于待插入节点的value。找到之后,只需将A[i]的right指向待插入的节点,A[i]的right原来指向的节点赋值给待插入节点的left指针。注意到查找最右路径的方向,如果从下到上查找,复杂度比较容易分析O(N)(因为查找过的节点必然会旋转到某个节点的左子节点,因此每个查找过的节点只会被查找一次),如果从上倒下,比较复杂(和最右端的最终的路径长度有关吧),会超过N,甚至更高,可能为O(N^2)

       另外,找到一种使用排序加左旋的方法,就是一样先排序,然后使用treap插入节点,可以发现,所有的旋转都为左旋。这种方法也TLE了,这种方法有一个很重要的意义,就是分析了上个方法中从上到下扫描的复杂度。因为这两种方法的效率是等价的,都TLE。

       最后,同样的题ZOJ是5M的,POJ是2M的,我用了从上到下2.5S,从下到上0.6S。


MORE:http://plussai.iteye.com/blog/1118892

http://www.hhanger.com/?p=134

http://hi.baidu.com/baowup/blog/item/bcd31c82808b1ead6c811997.html


你可能感兴趣的:(数据结构,c,insert)