笛卡尔树结构由Vuillmin在解决范围搜索的几何数据结构问题时提出的,从数列中构造一棵笛卡尔树可以线性时间完成,需要采用基于栈的算法来找到在该数列中的所有最近小数。由此可知,笛卡尔树是一种特定的二叉树数据结构,可由数列构造,在范围最值查询、范围top k查询(range top k queries)等问题上有广泛应用。它具有堆的有序性,中序遍历可以输出原数列。

目 录

1笛卡尔树简单介绍

2笛卡尔树定义

3笛卡尔树的实现

  1. 3.1 O(N^2)算法实现
  2. 3.2 O(N)算法实现

4相关代码

  1. 4.1 c++代码
  2. 4.2 O(N)算法代码

1笛卡尔树简单介绍

笛卡尔树又称笛卡儿树,在数据结构中属于二叉树的一种。
可以这么说:笛卡尔树是一棵二叉树,树的每个节点有两个值,一个为key,一个为value。光看key的话,笛卡尔树是一棵二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大;光看value的话,笛卡尔树有点类似堆,根节点的value是最小(或者最大)的,每个节点的value都比它的子树要大。

2笛卡尔树定义

无相同元素的数列构造出的笛卡尔树具有下列性质:
1、结点一一对应于数列元素。即数列中的每个元素都对应于树中某个唯一结点,树结点也对应于数列中的某个唯一元素
2、中序遍历(in-order traverse)笛卡尔树即可得到原数列。即任意树结点的左子树结点所对应的数列元素下标比该结点所对应元素的下标小,右子树结点所对应数列元素下标比该结点所对应元素下标大。
3、树结构存在堆序性质,即任意树结点所对应数值大(或小)于其左、右子树内任意结点对应数值(即根节点为其子树的最值)
根据堆序性质,笛卡尔树根结点为数列中的最大/小值,树本身也可以通过这一性质递归地定义:根结点为序列的最大/小值,左、右子树则对应于左右两个子序列,其结点同样为两个子序列的最大/小值。因此,上述三条性质唯一地定义了笛卡尔树。若数列中存在重复值,则可用其它排序原则为数列中相同元素排定序列,例如以下标较小的数为较小,便能为含重复值的数列构造笛卡尔树。

3笛卡尔树的实现

O(N^2)算法实现

①排序之后直接构造笛卡尔树的方法:
首先将节点序列按照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。

O(N)算法实现

我们将要将A的元素依次插入笛卡尔树C。每次插入都可能使树的形态发生变化。为了在O(N)的时间内完成整个插入过程,考虑C的右链,即根结点、根结点的右儿子、根结点的右儿子的右儿子……组成的链。注意这些元素的下标和值都是递增的。下标最大,即将要插入的元素A[i]一定是新树右链的最后一个元素。原来的右链中,值比A[i]大的元素在新树中不再属于右链,这些元素组成的链成为A[i]的左子树的右链;原来右链中的其它元素加上A[i]组成了新的右链。初看起来,寻找分界点的最佳方法是O(logN)时间的二分查找;但是对于整个过程来说,O(NlogN)的时间复杂度不是最优的。关键在于一旦一个元素比A[i]大,它就从右链中被永久地移除了。如果按照从后到前的顺序判断一个元素是否大于A[i],则每次插入的时间复杂度为O(k+1),k为本次插入中移除的右链元素个数。因为每个元素最多进出右链各一次,所以整个过程的时间复杂度为O(N)。
 

构造笛卡尔树的过程:

使用数据结构栈,栈中保存的始终是右链,即根结点、根结点的右儿子、根结点的右儿子的右儿子……组成的链
并且栈中从栈顶到栈底key依次减小

如果按照从后到前的顺序判断一个元素是否大于A[i],则每次插入的时间复杂度为O(k+1)
k为本次插入中移除的右链元素个数。因为每个元素最多进出右链各一次,所以整个过程的时间复杂度为O(N)。

从前往后遍历A[i],
1.对于每一个A[i],从栈中找出(从栈顶往栈底遍历,或者从数组后往前遍历)第一个小于等于A[i]的元素
2.如果找到,i.parent为sta[k],同时sta[k].r=i,即i为sta[k]的右子树,
3.如果栈中存在比A[i]大的元素 这些元素肯定是出栈了,这个问题最后的代码统一表示。
同时,sta[k+1].parent=i; i.l=sta[k+1] 即sta[K+1]为i的左子树
4.最后i入栈,比i大的A[i]都自动出栈了。

例子如下。
0 1 2 3 4 5 6 7 8  9      .....key
3 2 4 5 6 8 1 9 10 7      .....A,value

stack
0 1 2 3 4 5 6 7 8  ...num
0
1 2 3 4 5
6 7 8
6 9
最后sta[0].parent=-1;  为根节点 即 6 为根节点。

这里给出的是索引从0开始的[0,n-1]
如果题目给出的是[1,n],可以减一回到[0,n-1]上。

#include <iostream>
#include <queue>
using namespace std;
const int maxnum=10;int a[maxnum];
struct node
{int key;int parent;int l;int r;
}tree[maxnum];void Init()
{int i;for(i=0;i<maxnum;i++)tree[i].parent=tree[i].l=tree[i].r=-1;  //初始化
}int Build_Tree()
{int i,top,k;int stack[maxnum];top=-1;for(i=0;i<maxnum;i++){k=top;while(k>=0 && a[stack[k]]>a[i])  //栈中比当前元素大的都出栈k--;if(k!=-1)  //find it,栈中元素没有完全出栈,当前元素为栈顶元素的右孩子{tree[i].parent=stack[k];tree[stack[k]].r=i;}if(k<top)    //出栈的元素为当前元素的左孩子{tree[stack[k+1]].parent=i;tree[i].l=stack[k+1];}stack[++k]=i;//当前元素入栈top=k;//top指向栈顶元素}tree[stack[0]].parent=-1;//遍历完成后的栈顶元素就是根return stack[0];
}void inorder(int node)
{if(node!=-1){inorder(tree[node].l);cout<<tree[node].key<<endl;inorder(tree[node].r);}
}void levelorder(int node)
{queue<int> q;q.push(node);while(!q.empty()){int k=q.front();q.pop();cout<<tree[k].key<<endl;if(tree[k].l!=-1)q.push(tree[k].l);if(tree[k].r!=-1)q.push(tree[k].r);}
}int main()
{int i;Init();for(i=0;i<maxnum;i++){cin>>a[i];tree[i].key=a[i];}int root=Build_Tree();//inorder(root);//levelorder(root);return 0;
}/*
3 2 4 5 6 8 1 9 10 7
*/

笛卡尔树简介(分类到treap里面)相关推荐

  1. 牛客 - sequence(笛卡尔树+线段树)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数列 a 和数列 b ,求 题目分析:不算难的题目,对于每个 a[ i ] 求一下贡献然后维护最大值就好,具体思路就是,先找出每个 a[ i ] 左 ...

  2. SGU155(笛卡尔树的构造)

    题目:http://acm.sgu.ru/problem.php?contest=0&problem=155 题意:给出每个点的两个值key和fix,且所有的key值和fix值都是不相同的,要 ...

  3. YbtOJ#752-最优分组【笛卡尔树,线段树】

    正题 题目链接:http://www.ybtoj.com.cn/problem/752 题目大意 nnn个人,每个人有cic_ici​和did_idi​分别表示这个人所在的队伍的最少/最多人数. 然后 ...

  4. 笛卡尔树(Cartesian Tree)

    一.简介 笛卡尔树是一种特定的二叉树数据结构,由数组存储.在范围最值.范围top k查询方面广泛应用. 笛卡尔树的性质: 树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列 树中节点 ...

  5. HDU - 6305 RMQ Similar Sequence(笛卡尔树)

    http://acm.hdu.edu.cn/showproblem.php?pid=6305 题目 对于A,B两个序列,任意的l,r,如果RMQ(A,l,r)=RMQ(B,l,r),B序列里的数为[0 ...

  6. [算法学习] 线段树,树状数组,数堆,笛卡尔树

    都是树的变种,用途不同 [线段树 Interval Tree] 区间管理,是一种平衡树 可看做是对一维数组的索引进行管理.一维数组不需要是排序好的 深度不超过logL 任一个区间(线段)都分成不超过2 ...

  7. 洛谷 - P4755 Beautiful Pair(笛卡尔树+主席树)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数列 a,现在一个数对 ( i , j ) 如果满足 a[ i ] * a[ j ] <=max( a[ i ] ~ a[ j ] ),则称其 ...

  8. 牛客多校3 - Sort the Strings Revision(笛卡尔树+分治)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数字串 s[ 0 ],每个位置的赋值初始时为 s[ i ] = i % 10 ( i ∈ [ 0 , n - 1 ] ),现在有一个长度为 n 的排 ...

  9. POJ - 2559 Largest Rectangle in a Histogram(笛卡尔树,单调栈实现)

    题目链接:点击查看 题目大意:给出一排高度不同,宽度都为 1 的矩形,问拼起来后最大的矩形面积是多少 题目分析:普通做法是用单调栈直接维护,我一直觉得单调栈处理这种矩形问题都比较抽象,也可能是我太菜了 ...

最新文章

  1. 接口学习笔记(2009.11.24)
  2. python中并发编程基础1
  3. mysql的引擎讲解
  4. 模块-from import导入所有工具
  5. 通用权限管理系统组件 (GPM - General Permissions Manager) - 支持请求
  6. 如何升级浏览器_Chrome谷歌浏览器秒变科研神器,让你的效率提升10倍!
  7. Python中 类和对象调用其他类中的变量和方法
  8. solid测序列原理_SOLID原理简介
  9. php root权限执行命令,如何使用PHP执行需要root权限的系统命令
  10. JavaScript 工作必知(九)function 说起 闭包问题
  11. Django【基础篇】
  12. java maven web项目_java maven项目跟web项目区别
  13. 第十四章:【UCHome二次开发】uchome通用方法
  14. 数据产品经理的具象化
  15. 格式工厂怎么将qlv转换成mp4 转换方法最新
  16. HIT-哈工大数据结构-作业3(C++)
  17. 【RK3399 GMAC】Linux Debian9 gmac 主控输出clk ,即output模式
  18. vspy如何在图形面板显示报文_GUI 图形用户界面 [学习笔记]
  19. 报错:<generator object <genexpr> at 0x7fa9adc46eb0>
  20. 解析Linux中的系统安全及应用(二)

热门文章

  1. 对于“条件竞争”的利用
  2. 计算机二级只有上机考试吗,计算机等级考试二级是上机考试吗
  3. 从向量空间的角度来理解方程组有无解的问题
  4. Linux防火墙firewalld只允许特定IP访问
  5. 解决uniapp ios手机端获取时间戳出现NAN
  6. 磁盘坏道的检测及修复
  7. 框架楼是什么意思_什么是框架结构的房子?
  8. APICloud+vue+vant实现二维码扫码功能
  9. 微信会不会封服务器ip,最新微信防封号设置技巧(新微信如何防止封号)
  10. 十六.linux开发之Kernel移植——内核的配置和编译原理