首页 > 其他分享 >二叉查找树和笛卡尔树

二叉查找树和笛卡尔树

时间:2024-10-18 21:22:50浏览次数:1  
标签:笛卡尔 BST 二叉 查找 权值 节点

目录

二叉查找树

定义

二叉查找树(Binary Search Tree,BST),又名二叉搜索树或二叉排序树。

​ 它是一类特殊规定的二叉树,它应当满足以下条件:

  1. 每个节点有唯一确定的权值
  2. 非叶子节点的权值比其左子树中所有节点权值大
  3. 非叶子节点的权值比其右子树中所有节点权值小

​ 由于上述特性,易知BST的中序遍历是一个有序排列。

​ 特别地,空树也是一棵BST(觉得有意思就记下来了?)

作用

​ 顾名思义,它用于快速地查找数据,同时它也支持快速地插入删除数据。

​ 因为在BST上查找一个数,所需的查找次数不超过树的深度。查找过程类似于二分,运气好的话,它的时间复杂度能和二分相同。

​ 实际上,由于原序列本身无序,所以每次查找的时间复杂度会在\(O(\log n)\)到\(O(n)\)之间浮动。(具体原因后面再写)

操作

查找

​ 通过递归实现。

​ 二分思想中,每一次会在区间[l,r]中取mid,将区间均分为两半。并对mid进行检查,以此决定接下来是查找左区间还是右区间。

(说起来,初中数学有教过二分吧?)

​ 对应的,在BST中,当前查找到的节点相当于区间的mid,将区间分为左子树和右子树两部分(遗憾的是,并非均分)。对当前节点进行“判断”,就可以决定接下来的查找范围是枣子树还是柚子树。

(好耶,是枣子柚子二选一!)

(试图把左子树称为枣子树,把右子树称为柚子树)

插入

​ 在查找操作的基础上加一点点东西。

​ 如果按照给定顺序和值(即给定序列)插入,最终建成的BST一定是唯一的。

​ 对于需要插入的数据,我们可以通过查找得到它“应该在的位置”。如果这个位置是空位,就在这个位置插入新节点。

(如果不是空位,说明之前已经有同样的数据,可以按照需要进行记录。例如在每个节点用int cnt记录数据出现次数)

删除

​ 有删除操作时,BST可能不唯一。

​ 好在有两种轻松愉悦的情况:

​ 1.如果要删除的节点孤苦伶仃,无儿无女,无依无靠,那直接把它删了就行,反正也没节点给它收尸不是吗。

​ 2.如果要删除的节点只有枣子树或只有柚子树(总之就是只有一个儿子),那么直接删除它,并将子树衔接上来,代替它的位置。

​ 其余情况比较麻烦(儿孙们要争夺祖父的地位(?)

​ 这里列举两种常见的解决方法,同样是递归操作:

​ 其一是:用左子树中的最大节点,顶替删除节点。但是对于左子树来说,这相当于删除了“最大节点”,所以继续递归,直到前面两种情况之一。

​ 其二是:用右子树中的最小节点,顶替删除节点。同理,继续递归。

​ 由于删除时的操作,BST可能变成奇奇怪怪的形状。

缺点

形态不稳定。

​ 前面有说过,它每次操作的时间复杂度最好是\(O(\log n)\),最差会到\(O(n)\)。这是因为插入序列的顺序不一定,按照它建出的BST可能恰好平衡,也可能退化成一条链。

​ 比如序列4213657是一棵满二叉树:Annotation 2023-03-25 105753

​ 而序列1234567就会退化成一条链:Annotation 2023-03-25 110026

​ 此外,在删除的过程中,还会改变树的形态,也可能会使它退化。

​ BST存在的问题即“如何保持平衡的形态”。后续的替罪羊树、Treap树、Splay树、红黑树等等都是基于“维护平衡”这一问题的BST树优化算法。

笛卡尔树

定义

笛卡尔树是一类特殊的二叉查找树,其和一般BST的区别在于每个节点包括两个权值信息。

一棵笛卡尔树应当满足如下条件:

  1. 每个节点包括两个唯一确定的权值\((x_u,y_u)\)
  2. 只考虑权值\(x_u\)的情况下,树的形态应当符合一棵二叉查找树的性质。
  3. 只考虑权值\(y_u\)的情况下,树的形态应当符合大根堆或小根堆的性质。

根据OI Wiki的说明,如果一棵笛卡尔树的\((x_u,y_u)\)唯一确定,那么这棵笛卡尔树的形态唯一

关于唯一确定:即\((x_u,y_u)\)均已知,且\(x_u\)互不相同,\(y_u\)互不相同。

操作

构造

首先将节点按照\(x_u\)从小到大的顺序排序,现在假设我们要构造的笛卡尔树符合小根堆的性质。

由于二叉查找树的性质,且\(x_u\)递增,显然新节点的位置应该尽可能靠右。可以通过维护从根开始的一条极长链,满足每个节点都是其父节点的右儿子。

这条链满足链上节点的权值\(x,y\)均单调递增,可以看作一个单调栈,或者说可以用单调栈来维护。加入新节点\(u\)的时候,在链上找到深度最大的节点\(v\)满足\(y_v<y_u\),将其作为\(u\)的父节点。显然\(x_u>x_v\),故令\(u\)为\(v\)的右儿子。

如果\(v\)有右儿子,则将\(v\)的右子树拆下来,接在\(u\)的左子树下。由于\(v\)是深度最大的\(y<y_u\),所以\(y_v<y_u<y_{rs(v)}\),且\(x_u>x_{rs(v)}\),所以这样操作不会破坏笛卡尔树的性质。

洛谷P5854【模板】笛卡尔树为例,代码如下:

#include <bits/stdc++.h>
using namespace std;
#define lld long long

const int N = 1e7+5;
int n, q[N], tl = 0;
struct Tree{
  int val, ls, rs;
}t[N];

void read(int &x){
  char input = getchar(); x = 0;
  while(input<'0'||input>'9')
  	input = getchar();
  while(input>='0'&&input<='9'){
  	x = x*10+(input-'0');
  	input = getchar();
  }
}

int main(){
  scanf("%d", &n);
  for(int i = 1; i <= n; i++){
  	int T = tl; read(t[i].val);
  	while(tl && t[q[tl]].val > t[i].val) tl--;
  	if(tl) t[q[tl]].rs = i;
  	if(tl < T) t[i].ls = q[tl+1];
  	q[++tl] = i;
  }
  lld ans1 = 0, ans2 = 0;
  for(int i = 1; i <= n; i++){
  	ans1 ^= (lld)i*(t[i].ls+1);
  	ans2 ^= (lld)i*(t[i].rs+1);
  } printf("%lld %lld\n", ans1, ans2);
  return 0;
} // 此题的恶心之处在于必须快读

标签:笛卡尔,BST,二叉,查找,权值,节点
From: https://www.cnblogs.com/meteor2008/p/18474364

相关文章

  • 二叉树和度为二的有序树的区别
    一、定义与结构度为二的有序树:在这种树结构中,每个节点最多有两个子节点。子节点的顺序是重要的,即使两个子节点的值相同,只要他们的位置不同,他们就被视为是不同的子节点。当一个节点只有一个子节点时,该子节点的位置(左或右)并无特定要求,也即无需区分其左右次序。二叉树:二叉树......
  • 机器学习中的海量数据查找—倒排索引查找
    原文链接:机器学习中的海量数据查找—倒排索引查找–每天进步一点点(longkui.site)索引是一种用于数据快速查找的数据结构,哈希表、二分查找、分块查找也可以视为一种索引,这类索引的价值在于在较短的时间内获得最相关、最全、最深的数据集合。在通常使用的索引中,大多是基于顺序......
  • 推断二叉树(进阶)
    Description给出一棵二叉树的中序遍历和每个节点的父节点,求这棵二叉树的先序和后序遍历。Input输入第一行为一个正整数n表示二叉树的节点数目,节点编号从1到n,其中1为根节点。第2行有n个数字,第i个数字表示i的父亲节点。(1的父亲节点为0,表示无)第3行为中......
  • B+树、红黑树、平衡二叉树
    1.概述这三种数据结构都用于解决动态查找问题,即能够在插入、删除的同时保持高效的查找性能。它们广泛应用于数据库、文件系统、内存管理等领域。但它们的具体结构和应用场景有所不同。B+树(B+Tree):B+树是一种自平衡的多叉树,常用于数据库系统和文件系统中。它的特点是所有......
  • 解锁二叉树的魅力:链式实现详解
    前言二叉树的简介及顺序实现引言在数据结构的浩瀚星空中,二叉树如同一颗璀璨的明珠,其优雅的结构和强大的功能使其成为计算机科学中不可或缺的工具。从数据库索引到编译器的语法树,二叉树以其独特的方式支撑着许多核心算法与数据处理。本文将深入探讨如何使用C语言实现二叉树的......
  • 111. 二叉树的最小深度【二叉树】
    文章目录111.二叉树的最小深度解题思路111.二叉树的最小深度111.二叉树的最小深度给定一个二叉树,找出其最小深度。最小深度是从根节点到最近叶子节点的最短路径上的节点数量。说明:叶子节点是指没有子节点的节点。示例1:输入:root=[3,9,20,null,null,15,7]......
  • 二叉树的中序遍历
    给定一个二叉树的根节点 root ,返回 它的 中序 遍历 。示例1:输入:root=[1,null,2,3]输出:[1,3,2]示例2:输入:root=[]输出:[]示例3:输入:root=[1]输出:[1]提示:树中节点数目在范围 [0,100] 内-100<=Node.val<=100进阶: 递归算法很简单,你可以通......
  • 109. 有序链表转换二叉搜索树【二叉树】
    文章目录109.有序链表转换二叉搜索树解题思路Go代码109.有序链表转换二叉搜索树109.有序链表转换二叉搜索树给定一个单链表的头节点head,其中的元素按升序排序,将其转换为平衡二叉搜索树。平衡二叉树是指该树所有节点的左右子树的深度相差不超过1。示例......
  • 七、二叉树之链式结构(递归)
    一、前序:前面章节所说二叉树的结构其实是递归的,为什么这样说呢?根节点有左右子树,根节点的左右孩子又有左右子树,以此类推,直到叶子节点,它的左右子树为NULL,开始返回。所以,我们把它分成一个又一个的子树来分析。因此,它的结构是递归的。因为一开始接触二叉树,还不是熟悉,我们先来手动......
  • 【算法】C++中的二分查找
    ......