笛卡尔树

笛卡尔树[编辑]

维基百科,自由的百科全书

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

定义[编辑]

无相同元素的数列构造出的笛卡尔树具有下列性质:

  1. 结点一一对应于数列元素。即数列中的每个元素都对应于树中某个唯一结点,树结点也对应于数列中的某个唯一元素
  2. 中序遍历(in-order traverse)笛卡尔树即可得到原数列。即任意树结点的左子树结点所对应的数列元素下标比该结点所对应元素的下标小,右子树结点所对应数列元素下标比该结点所对应元素下标大。
  3. 树结构存在堆序性质,即任意树结点所对应数值大/小于其左、右子树内任意结点对应数值

根据堆序性质,笛卡尔树根结点为数列中的最大/小值,树本身也可以通过这一性质递归地定义:根结点为序列的最大/小值,左、右子树则对应于左右两个子序列,其结点同样为两个子序列的最大/小值。因此,上述三条性质唯一地定义了笛卡尔树。若数列中存在重复值,则可用其它排序原则为数列中相同元素排定序列,例如以下标较小的数为较小,便能为含重复值的数列构造笛卡尔树。

笛卡尔树应用[编辑]

范围最值查询与最低公共祖先[编辑]

笛卡尔树可以有效地处理范围最值查询(range minimum queries),通过将定义在数列上的RMQ问题转化为定义在树结构上的最低公共祖先(lowest common ancestor)问题。数列以线性时间构造出笛卡尔树,笛卡尔树则能以常数时间处理最低公共祖先查询,因此在线性时间的预处理后,范围最值查询能以常数时间完成。

Bender & Farach-Colton (2000)[2]则提出了RMQ与LCA问题的新联系,他们通过不基于树的算法处理RMQ问题从而有效地解决LCA问题。其使用欧拉路径的技巧将树结构转化为数列,此数列具有特定性质(相邻数值代表树中的相邻顶点,即在树中高度差为1的顶点),利用这一性质RMQ问题可以很高效地得到解决。通常的数列则不具备此性质,为了将一般的数列转化为具有上述性质的数列,需要应用到笛卡尔树,具体过程为在普通数列上构造笛卡尔树,在笛卡尔树上使用欧拉路径转化的方法将树转化为具有上述性质的新数列。

范围最值查询问题也可以解释为二维范围查询问题,或者三边范围查询问题(three sided range queries),笛卡尔平面上的有限点集可以用来构造笛卡尔树,首先将这些点按照x取值排序,然后将y值作为数列中元素的值,以此数列建立笛卡尔树。若 S 为有限点集中满足  L \le x \le R 条件的点集,设 p 是 S 中x值最小的点,q 是 S 中 x 值最大的点,则笛卡尔树中 p 与 q的最低公共祖先即为该点集中处于该x值范围内y值最高/低的点 b。三边范围查询问题,即给定条件  L \le x \le R, y \le T ,取出所有满足条件的点。其解决是以笛卡尔树找到b 点,若b点的y 值满足条件,则递归地在 p, b 所约束的子树以及b, q 所约束的子树内重复这一过程,这一查询可以使每个被报告的点都在常数时间内找到,总体的时间复杂度为  O(k) ,k即为满足条件的点数。

笛卡尔树同样可以应用于以常数时间查询超度量空间内点对的距离。超度量空间内距离的定义与最宽路径问题中的权重相同。从最小生成树上可以构造一个笛卡尔树,根结点表示最小生成树中的权值最大的边,撤去此边会将最小生成树分割为两个子树,笛卡尔树递归地从这两棵子树上构造。笛卡尔树的叶结点表示度量空间内的点,两个叶结点的最低公共祖先则是这两个点在最小生成树中最重的边,代表这两点间的距离。获得了最小生成树及将边按照权值排序后,笛卡尔树即可在线性时间内构造出来。

treap[编辑]

笛卡尔树是二叉树,对于数列而言将其作为二叉搜索树是自然的。若将二叉搜索树结点关联上一个权值,并且保证此权值在树结构中遵循堆中的序关系,即父结点权值比子结点权值大,则此二叉搜索树又被称为Treap. 其名称来源于树与堆两英文词的组合(tree + heap -> treap)。Treap与笛卡尔树在结构上是相同的,只是两者的应用不同。


POJ-2201 Cartesian Tree【笛卡尔树】

分类: 数据结构 557人阅读 评论(0) 收藏 举报

题目链接:http://poj.org/problem?id=2201

题目大意:

让你构造一棵笛卡尔树。

笛卡尔树的节点含有2个值,1个key,一个value,其中key是主键,value是辅键。一棵笛卡尔树就是:key升序,value升序或者降序。类似堆。

与treap的区别是:treap的value是随机值,是为了使树更加平衡引进的,而笛卡尔树的value是一个确定的值。

结构完全相同,功能不一样。理解这一点就知道什么时候用treap,什么时候用笛卡尔树了。


笛卡尔树相关链接:

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

http://zh.wikipedia.org/wiki/%E7%AC%9B%E5%8D%A1%E5%B0%94%E6%A0%91

http://en.wikipedia.org/wiki/Cartesian_tree


代码如下:

  1. #include<iostream>
  2. #include<cstring>
  3. #include<cstdio>
  4. #include<algorithm>
  5. #include<cmath>
  6. using namespace std;
  7. const int N = 50010;
  8. int stack[N];
  9. int pre[N], lson[N], rson[N];
  10. struct node{
  11. int key, value, id;
  12. }st[N];
  13. bool operator <(const node &a, const node &b){
  14. return a.key < b.key;
  15. }
  16. void init(int n)
  17. {
  18. memset(stack, -1, sizeof(stack));
  19. for(int i = 1; i <= n; ++i)
  20. pre[i] = lson[i] = rson[i] = 0;
  21. }
  22. void Build_Cartesian_Tree(int n)
  23. {
  24. int k, top = -1;
  25. for(int i = 0; i < n; ++i)
  26. {
  27. k = top;
  28. while(k >= 0 && st[stack[k] + 1].value > st[i + 1].value) //第一个小于i的位置
  29. --k;
  30. if(k != -1)
  31. {
  32. pre[st[i + 1].id] = st[stack[k] + 1].id;
  33. rson[st[stack[k] + 1].id] = st[i + 1].id;
  34. }
  35. if(k < top) //左旋
  36. {
  37. pre[st[stack[k + 1] + 1].id] = st[i + 1].id;
  38. lson[st[i + 1].id] = st[stack[k + 1] + 1].id;
  39. }
  40. stack[++k] = i;
  41. top = k;
  42. }
  43. pre[st[stack[0] + 1].id]= 0; //根节点
  44. }
  45. int main()
  46. {
  47. int num;
  48. while(scanf("%d", &num) != EOF)
  49. {
  50. init(num);
  51. for(int i = 1; i <= num; ++i)
  52. {
  53. scanf("%d%d", &st[i].key, &st[i].value);
  54. st[i].id = i;
  55. }
  56. sort(st + 1, st + num + 1); //按key排序
  57. Build_Cartesian_Tree(num);
  58. printf("YES\n");
  59. for(int i = 1; i <= num; ++i)
  60. printf("%d %d %d\n", pre[i], lson[i], rson[i]);
  61. }
  62. return 0;

poj2201Cartesian Tree(笛卡尔树)

分类: 数据结构 笛卡尔树 55人阅读 评论(0) 收藏 举报
Cartesian Tree
Time Limit: 10000MS Memory Limit: 65536K
Total Submissions: 2950 Accepted: 1122
Case Time Limit: 2000MS

Description

Let us consider a special type of a binary search tree, called a cartesian tree. Recall that a binary search tree is a rooted ordered binary tree, such that for its every node x the following condition is satisfied: each node in its left subtree has the key less then the key of x, and each node in its right subtree has the key greater then the key of x.
That is, if we denote left subtree of the node x by L(x), its right subtree by R(x) and its key by kx then for each node x we have
  • if y ∈ L(x) then ky < kx
  • if z ∈ R(x) then kz > kx

The binary search tree is called cartesian if its every node x in addition to the main key kx also has an auxiliary key that we will denote by ax, and for these keys the heap condition is satisfied, that is
  • if y is the parent of x then ay < ax

Thus a cartesian tree is a binary rooted ordered tree, such that each of its nodes has a pair of two keys (k, a) and three conditions described are satisfied.
Given a set of pairs, construct a cartesian tree out of them, or detect that it is not possible.

Input

The first line of the input file contains an integer number N -- the number of pairs you should build cartesian tree out of (1 <= N <= 50 000). The following N lines contain two numbers each -- given pairs (ki, ai). For each pair |ki|, |ai| <= 30 000. All main keys and all auxiliary keys are different, i.e. ki != kj and ai != aj for each i != j.

Output

On the first line of the output file print YES if it is possible to build a cartesian tree out of given pairs or NO if it is not. If the answer is positive, on the following N lines output the tree. Let nodes be numbered from 1 to N corresponding to pairs they contain as they are given in the input file. For each node output three numbers -- its parent, its left child and its right child. If the node has no parent or no corresponding child, output 0 instead.
The input ensure these is only one possible tree.

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

Source

Northeastern Europe 2002, Northern Subregion

题目大意:裸的笛卡尔树,建树输出每个点的父节点和儿子节点编号,没有为0。
题目分析:注意这题肯定要输出YES,因为给的每一对关键字都不相同,所以能建唯一的笛卡尔树,不存在NO的情况。还有排序后序号就乱了,注意一下就行了,其他的没什么了。
详情请见代码:
  1. #include <iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. using namespace std;
  6. const int N = 50005;
  7. struct node
  8. {
  9. int key,val;
  10. int l,r,pa,id;
  11. }lcm[N];
  12. int pp[N],ll[N],rr[N];
  13. int n;
  14. int stack[N];
  15. int cmp(struct node a,struct node b)
  16. {
  17. return a.key < b.key;
  18. }
  19. int cmp2(struct node a,struct node b)
  20. {
  21. return a.id < b.id;
  22. }
  23. void build()
  24. {
  25. int i,j,top;
  26. top = -1;
  27. for(i = 1;i <= n;i ++)
  28. {
  29. j = top;
  30. while(j >= 0 && lcm[stack[j]].val > lcm[i].val)
  31. {
  32. j --;
  33. }
  34. if(j != -1)
  35. {
  36. pp[lcm[i].id] = lcm[stack[j]].id;
  37. rr[lcm[stack[j]].id] = lcm[i].id;
  38. }
  39. if(j < top)
  40. {
  41. pp[lcm[stack[j + 1]].id] = lcm[i].id;
  42. ll[lcm[i].id] = lcm[stack[j + 1]].id;
  43. }
  44. stack[++ j] = i;
  45. top = j;
  46. }
  47. }
  48. int main()
  49. {
  50. int i;
  51. while(scanf("%d",&n) != EOF)
  52. {
  53. for(i = 1;i <= n;i ++)
  54. {
  55. scanf("%d%d",&lcm[i].key,&lcm[i].val);
  56. lcm[i].l = lcm[i].r = lcm[i].pa = 0;
  57. lcm[i].id = i;
  58. }
  59. lcm[0].id = 0;
  60. sort(lcm + 1,lcm + 1 + n,cmp);
  61. memset(pp,0,sizeof(pp));
  62. memset(ll,0,sizeof(ll));
  63. memset(rr,0,sizeof(rr));
  64. build();
  65. printf("YES\n");
  66. for(i = 1;i <= n;i ++)
  67. printf("%d %d %d\n",pp[i],ll[i],rr[i]);
  68. }
  69. return 0;
  70. }
  71. //2124K 1016MS

poj2559 & zoj1985 &hdu1506 Largest Rectangle in a Histogram(笛卡尔树)

分类: 笛卡尔树 数据结构 64人阅读 评论(0) 收藏 举报
Largest Rectangle in a Histogram
Time Limit: 1000MS Memory Limit: 65536K
Total Submissions: 12205 Accepted: 3957

Description

A histogram is a polygon composed of a sequence of rectangles aligned at a common base line. The rectangles have equal widths but may have different heights. For example, the figure on the left shows the histogram that consists of rectangles with the heights 2, 1, 4, 5, 1, 3, 3, measured in units where 1 is the width of the rectangles:

Usually, histograms are used to represent discrete distributions, e.g., the frequencies of characters in texts. Note that the order of the rectangles, i.e., their heights, is important. Calculate the area of the largest rectangle in a histogram that is aligned at the common base line, too. The figure on the right shows the largest aligned rectangle for the depicted histogram.

Input

The input contains several test cases. Each test case describes a histogram and starts with an integer n, denoting the number of rectangles it is composed of. You may assume that 1<=n<=100000. Then follow n integers h1,...,hn, where 0<=hi<=1000000000. These numbers denote the heights of the rectangles of the histogram in left-to-right order. The width of each rectangle is 1. A zero follows the input for the last test case.

Output

For each test case output on a single line the area of the largest rectangle in the specified histogram. Remember that this rectangle must be aligned at the common base line.

Sample Input

7 2 1 4 5 1 3 3
4 1000 1000 1000 1000
0

Sample Output

8
4000

Hint

Huge input, scanf is recommended.

Source


题目大意:给定一个统计直方图,n个矩形条,每个矩形条宽1,高0-10^9,求最大矩形面积。
题目分析:这题数据好像比较水,当年各种乱搞也能过,今天看了一下笛卡尔树,发现这题用笛卡尔树就能过了。与前面2题不同的是,这题没那么裸,要抽象出笛卡尔树的模型。
做法是这样的,以输入的顺序为第一关键字,矩形条的高为第二关键字建立一颗笛卡尔树。并且以高为关键字是一个小堆。那么从树根开始后序遍历整颗树,在节点处结算一次保存最大值。建树O(n),遍历树也是O(n),所以总的时间复杂度也是O(n),而且常数非常小,所以比较高效,在3个oj上提交跑的都还可以,不在第一版就在第二版。
下面简单分析一下笛卡尔树解这题的原理。首先笛卡尔树是一个二叉排序树,那么以输入顺序作为二叉排序树的关键字,所建出来的树保证以任意节点为根的子树的所有节点是连续的(想一想,为什么),然后笛卡尔树以矩形条的高度为关键字又有堆的性质,而这题的关键不就正是要找连续的矩形条,使总面积最大,而决定大矩形高度的就是每个小矩形条的最小高度!那么我们维护一个小堆即可,这样在每个节点处就保证以该节点为根的子树所能构成的最大矩形就是该节点的高度*该子树的大小!!
因此本题非常巧妙的运用了笛卡尔树的2个性质,此题就是为笛卡尔树而生的啊有木有!!
详情请见代码:
  1. #include <iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. using namespace std;
  5. const int N = 100005;
  6. typedef __int64 ll;
  7. ll ans;
  8. struct node
  9. {
  10. ll val;
  11. int l,r,pa;
  12. }lcm[N];
  13. int n;
  14. int stack[N];
  15. int nextint()
  16. {
  17. char c;
  18. int ret;
  19. while(isspace(c = getchar()))
  20. ;
  21. ret = c - '0';
  22. while((c = getchar()) >= '0' && c <= '9')
  23. ret = ret * 10 + c - '0';
  24. return ret;
  25. }
  26. int build()
  27. {
  28. int i,j,top;
  29. top = -1;
  30. for(i = 1;i <= n;i ++)
  31. {
  32. j = top;
  33. while(j >= 0 && lcm[stack[j]].val > lcm[i].val)
  34. j --;
  35. if(j != -1)
  36. {
  37. lcm[i].pa = stack[j];
  38. lcm[stack[j]].r = i;
  39. }
  40. if(j < top)
  41. {
  42. lcm[stack[j + 1]].pa = i;
  43. lcm[i].l = stack[j + 1];
  44. }
  45. stack[++ j] = i;
  46. top = j;
  47. }
  48. return stack[0];
  49. }
  50. int dfs(int cur)
  51. {
  52. if(cur == -1)
  53. return 0;
  54. int num = dfs(lcm[cur].l) + dfs(lcm[cur].r) + 1;
  55. //printf("%I64d:%d\n",lcm[cur].val,num);
  56. ll tmp = num * lcm[cur].val;
  57. if(tmp > ans)
  58. ans = tmp;
  59. return num;
  60. }
  61. void print(int cur)
  62. {
  63. if(cur == -1)
  64. return;
  65. printf("%d\n",lcm[cur].val);
  66. print(lcm[cur].l);
  67. print(lcm[cur].r);
  68. }
  69. int main()
  70. {
  71. int i;
  72. while(n = nextint(),n)
  73. {
  74. for(i = 1;i <= n;i ++)
  75. {
  76. lcm[i].val = (ll)nextint();
  77. lcm[i].l = lcm[i].r = lcm[i].pa = -1;
  78. }
  79. int root = build();
  80. //print(root);
  81. ans = 0;
  82. dfs(root);
  83. printf("%I64d\n",ans);
  84. }
  85. return 0;
  86. }
  87. //poj:6216K 47MS
  88. //zoj:70ms 2532k
  89. //hdu:31MS 3548K
 
   

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于用 C 语言建立笛卡尔树求解区间最值问题,我可以给你一些基本的思路和代码实现。 首先,笛卡尔树是带权值的树,其性质由下面的定理给出: 定理1:一个序列可以对应唯一的一棵笛卡尔树。 定理2:设在笛卡尔树上节点 u 和 v 的先后顺序为“按下标增大的顺序,在这个前提下按权值减小的顺序”,则 u 到 v(包括 u 和 v)路径上的点在原序列中构成了一个区间。 基于这两个定理,我们就可以使用笛卡尔树来解决区间最值问题,其基本思路如下: 1. 输入一个序列 a[N],构建对应的笛卡尔树。 2. 对于每个节点 u,记录其子树中最小值或最大值(根据具体问题而定)。 3. 对于每个询问,找到笛卡尔树上 u 和 v 两个节点,使得它们对应的区间包含询问区间 [L,R],那么 [L,R] 中的最值就是从 u 到 v 路径上的最小值或最大值(可以使用 st 表等数据结构进行维护)。 下面是用 C 语言实现笛卡尔树的基本代码,可以参考一下: struct Node { int val, idx; int fa, ch[2]; } tree[N]; int root, idx; int build(int l, int r) { // 构建笛卡尔树并返回根节点编号 if (l > r) return -1; int x = l; for (int i = l + 1; i <= r; i++) if (tree[i].val < tree[x].val) x = i; tree[x].ch[0] = build(l, x - 1); tree[x].ch[1] = build(x + 1, r); if (tree[x].ch[0] != -1) tree[tree[x].ch[0]].fa = x; if (tree[x].ch[1] != -1) tree[tree[x].ch[1]].fa = x; return x; } 现在我回答了你的问题,如果您有任何其他问题,可以随时问我。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值