【OI赛第五场】T1树

笛卡尔树是一种二叉树数据结构,用于线性时间构造并解决范围搜索和最值查询问题。其特点结合了二叉搜索树的有序性和堆的特性。构造笛卡尔树可以通过栈操作实现,过程中保持右链顺序,使得中序遍历输出原始数列。在给定的例子中,展示了如何根据给定数组构建笛卡尔树,并强调了数组索引的不同处理方式。
摘要由CSDN通过智能技术生成
题意
一颗二叉树,知道中序遍历是1~n,告诉你层次遍历,求字典序最小的先序遍历。
样例输入
5
4 2 5 1 3
样例输出
4 2 1 3 5
数据范围:n<=3*10^5

题解
题目的关键是把这颗树建出来,怎么建呢?就是找根,根就是一段区间在层次遍历中出现最早的,然后递归左右子树,现在的关键是求一段区间的最早出现的值。用一个B数组记录1~n中每个数出现的时间,用ST表,或线段树就可以A掉这题。复杂度O(nlogn);但这不是本题的重点。重点是用笛卡尔树O(n)的求。下面介绍一下笛卡尔树。

笛卡尔树

笛卡尔树又称笛卡儿树,在数据结构中属于二叉树的一种。

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

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

构造笛卡尔树的过程:

使用数据结构栈,栈中保存的始终是右链,即根结点、根结点的右儿子、根结点的右儿子的右儿子……组成的链
并且栈中从栈顶到栈底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]上。

这就是笛卡尔树的概念及建树过程。此题就可以把中序遍历1~n当成第一维权值出现的时间(满足中序遍历是1~n),把每个点的出现的时间(两个时间不要混)当做第二位权值建小根堆(因为在层次遍历出现时间越早越可能是根)。就可以O(n)建树然后遍历一遍求先序遍历了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=300010;
int s[maxn],a[maxn],b[maxn],x,n;
struct tree{
	int fa,l,r,val;
}t[maxn];
inline int build(){
	int top=-1,k;
	for(int i=1;i<=n;i++){
		k=top;
		while(k>=0&&b[s[k]]>b[i])//栈中比当前元素大的都出栈 
		k--;
		if(k!=-1){//find it,栈中元素没有完全出栈,当前元素为栈顶元素的右孩子
			t[i].fa=s[k];
			t[s[k]].r=i;
		}
		if(k<top){//出栈的元素为当前元素的左孩子 
			t[s[k+1]].fa=i;
			t[i].l=s[k+1];
		}
		s[++k]=i;
		top=k;
	}
	t[s[0]].fa=-1;//遍历完成后的栈顶元素就是根
	return s[0];
}
inline void out(int x){
	if(x==-1) return ;
	printf("%d ",x);
	out(t[x].l);
	out(t[x].r);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		b[a[i]]=i;
	}
	for(int i=1;i<=n;i++){
		t[i].val=b[i];
	}
	for(int i=1;i<=n;i++)
	t[i].l=t[i].r=-1;
	x=build();
	out(x);

	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值