笛卡尔树模板

17 篇文章 1 订阅
13 篇文章 0 订阅
本文介绍了笛卡尔树的基本概念,它是一种结合了二叉树和堆特性的数据结构。文章详细阐述了笛卡尔树的构造过程,通过顺序插入数组并维护堆性质来构建,同时提供了整体思想和代码实现,复杂度为O(N)。此外,文章还提及了笛卡尔树在HDU 1506题目中的应用,并讨论了如何求解笛卡尔树的根节点。
摘要由CSDN通过智能技术生成

如果学过treap(一种平衡二叉树),笛卡尔树应该很好理解

本文只介绍基础的建树,没有深入的东西。(你懂我意思吧)

笛卡尔树也是二叉树+堆


一、二叉树与堆的概念

可以看看我这篇,顺便把treap学了也可以,简单哒

 


二、笛卡尔树的结构

笛卡尔树可以按顺序插入一个数组,我们将下标当值插入二叉树,然后数组的值作为“优先级”,维护堆的性质。

盗一份网上的图你们参考一下,结构就是这个样子


三、笛卡尔树的构造

先给整块的代码和整体的思想,再对代码仔细讲一下

整体的思想:

由于数组按顺序插入,下标一定递增,因此每个点根据二叉树一定会插入当前树的最右子节点,接下去维护堆的性质进行一些操作,我们用栈维护右链,每次插入都在栈中找到第一个比当前的数值小的,那个点就是当前的父节点,弹出栈的链就作为当前节点的左儿子,然后当前点入栈。

由于每个点入栈出栈各一次,构造复杂度为O(N)

///ch[][]子节点,0为左儿子,1为右儿子
///val[]记录当前节点的值
///sta[]为栈
///sz单纯用来记录插入了几个点(可以不要)    
void insert(int id,int v){
    int st_tot = tot;
    ch[id][0] = ch[id][1] = 0;
    while (tot>=0){
        if (val[sta[tot]] <= v){
            ch[sta[tot]][1] = id;
            break;
        }
        else tot--;
    }
    if (tot < st_tot) {ch[id][0] = sta[tot+1];if (sta[tot+1]==root) root = id;}
    val[id] = v;
    sta[++tot] = id;
    sz++;
}

这个板子是自己打的,可能有些地方不够简洁,如果可以更简洁,请评论留言告诉我。

那么首先看第一行

int st_tot = tot;

st_tot,称之为本轮开始时的tot,这个变量的用处是判断本次插入栈中是否有元素弹出,如果有,那么当前节点左儿子就有啦,即下面这一行

if (tot < st_tot) {ch[id][0] = sta[tot+1];if (sta[tot+1]==root) root = id;}

这里还有一个操作就是更新笛卡尔树的根节点,很明显,根节点发生改变只有在当前插入节点把原先根节点作为儿子时,因此此时根节点转移。具体为什么会让当前节点的左儿子连最后一个出栈的元素,请看下面的讲解

ch[id][0] = ch[id][1] = 0;
        while (tot>=0){
            if (val[sta[tot]] <= v){
                ch[sta[tot]][1] = id;
                break;
            }
            else tot--;
        }

首先我们明确:栈内存的是当前笛卡尔树的右链,即ch[根]->r_son->r_son->r_son...

上面这一片代码首先初始化了一下当前节点的两个儿子

然后弹出栈内元素,弹出来的节点,都是对应数值大于当前节点的,他们应该作为当前节点的儿子,否则堆的性质不符合。

我们弹出元素直到栈内维护的剩余的右链末的那个点对应数值比当前点小,此时,当前节点连作右链末的右儿子,此时的树符合笛卡尔树的性质。

那么丢掉的链咋办?丢掉的链头连到当前节点的左儿子。

因为这一串链下标比当前小且值比当前节点大。

下面的代码就不讲了。


四、笛卡尔树的基基基础功能

我们回想一下维护的结构:左儿子下标更小,右儿子下标更大,两个儿子的对应数值都比当前节点大

所以HDU 1506了解一下?顺便检验一下你学会了没有

②笛卡尔树是无旋treap实现build函数的思想。

③其他不会,有缘回来补充(咕咕咕)


五、HDU 1506的题解

>>>疯狂自我推销


六、模板的一些修改

笛卡尔树的根节点求法:

root不用像上述的代码一样每次更新,我们知道栈实时维护右链,所有sta[0]就是根节点

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值