笛卡尔树学习笔记

Posted wwwaax

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了笛卡尔树学习笔记相关的知识,希望对你有一定的参考价值。

笛卡尔树

下文的资料多摘自OI Wiki

性质

笛卡尔树是一种二叉树,每一个节点都由一个键值二元组 \\((k,w)\\) 构成。要求 \\(k\\) 满足二叉搜索树的性质,而 \\(w\\) 满足堆的性质。如果笛卡尔树的 \\(k\\)\\(w\\) 键值确定的话,且 \\(k\\) 互不相同,\\(w\\) 互不相同,那么这个笛卡尔树的结构是唯一的。

例如 OI Wiki 上的这张图:

上面的这棵树是按每一个点内的值为键值 \\(w\\),把数组下标当作键值 \\(k\\),来建立的。仔细观察可以发现,这棵树的 \\(k\\) 满足二叉搜索树的性质,而键值 \\(w\\) 是满足小根堆的性质的。

像上面这棵树一样键值 \\(k\\) 恰好对应数组下标的笛卡尔树有一个性质:一棵子树内的下标是连续的一个区间(满足二叉搜索树的性质)。

谈到笛卡尔树,很容易让人想到一种家喻户晓的结构—— Treap。没错,Treap 是笛卡尔树的一种,只不过 \\(w\\) 的值完全随机。Treap 也有线性的构建算法,如果提前将元素排好序,显然可以使用上述单调栈算法完成构建过程,只不过很少会这么用。

构建

过程

我们考虑将元素的键值 \\(k\\) 进行排序,然后一个一个地插入到当前的笛卡尔树中,那么我们每一次插入的元素必在这个树的右链(右链:从根节点一直往右子树走,经过的节点)的末端。于是我们可以执行这样一个过程,从下往上比较右链节点与当前节点的 \\(u\\),和\\(w\\),如果找到了一个右链上的节点满足 \\(x_w<u_w\\),就把 \\(u\\) 接到 \\(x\\) 的有儿子上,而 \\(x\\) 原来的右子树就变成了 \\(u\\) 的左子树。

显然每个数最多进出右链一次(或者说每个点在右链中存在的是一段连续的时间)。这个过程我们可以用栈维护,栈中维护当前笛卡尔树的右链上的结点。一个点不在右链上了就把它弹掉。这样每个点最多进出一次,复杂度 \\(O(n)\\)

实现

for (int i = 1; i <= n; i++) 
  int k = top;
  while (k > 0 && h[stk[k]] > h[i]) k--;
  if (k) rs[stk[k]] = i;  // rs代表笛卡尔树每个节点的右儿子
  if (k < top) ls[i] = stk[k + 1];  // ls代表笛卡尔树每个节点的左儿子
  stk[++k] = i;
  top = k;

以上是关于笛卡尔树学习笔记的主要内容,如果未能解决你的问题,请参考以下文章

笔记:笛卡尔树

数据结构学习笔记——图的应用1(最小生成树最短路径)

数据结构学习笔记——图的应用1(最小生成树最短路径)

数据结构学习笔记——图的应用1(最小生成树最短路径)

学习笔记:最小生成树算法

学习数据结构笔记(21) --- [克鲁斯卡尔算法(Kruskal Algorithm) 由公交车站连接问题引入]