二叉搜索树的和值
题目
知识点:二叉搜索树,不是模拟
二叉搜索树是一种特殊的二叉树(每个节点最多只有两个儿子的树)。树的每个节点上存有一个唯一的值,并且满足:这个节点的左子树内所有点的值都比这个节点的值小,且右子树内所有点的值都比这个节点的值要大。
我们定义一棵二叉搜索树的和值为当前树的所有节点的深度和(根节点的深度为0)。现在有 N 个数需要插入一棵空树中。给定插入序列,请在每个元素被插入之后,输出当前树的和值。
输入
第一行为一个整数 n 接下来一行是n个各不相同的数字,这些数字在[1, n]区间。
(0<n<300000)
输出
输出 n 行,第 i 行整数表示第 i个数插入树后,当前树的和值。
输出样例
8
3 5 1 6 8 7 2 4
输出样例
0
1
2
4
7
11
13
15
思路
自己模拟一下,会发现,新加入的一个数后,找到它(以水平向坐标为准)左右两个点,那么它的深度就是左右深度较大值的下一层。
用个set记录节点信息(数值和深度),每次找左右两个节点,操作一下即可,或者用map即可。
首先想到的肯定是模拟,但是tle了。
仔细想想,在一条链的时候,模拟的过程会从nlog退化到n2,所以我们应该借助别的数据结构来快速查询插入的位置。
考虑这题两个重要的条件,n个不同的元素、二叉搜索树,不难证明一个元素插入的位置是一定是在和它大小紧邻的两个元素的孩子处。(结合二叉搜索树中序遍历左边小于该元素,右边大于和没有相同的元素这几点来反证)
那么具体应该是插入在哪个位置呢,我们可以假设插在两者中的一个,然后可以发现这个带你一定是深度比较大的(还是结合二叉搜索树的性质和元素互异来证)
那么这题就很简单了,我们结合一个不容易退化的数据结构(set,map之类的)来保证查询和插入都是log即可
代码
#include <cstdlib>
#include <algorithm>
#include <iostream>
#include <cstring>
#include <set>
using namespace std;
typedef long long ll;
const int ms = 300300;
int d[ms];
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, root = 0, a;
set<int>s;
s.insert(0);
s.insert(ms - 1);
d[0] = d[ms - 1] = -1;
cin >> n;
ll res = 0;
for (int i = 0; i < n; ++i)
{
cin >> a;
auto t = s.lower_bound(a);
d[a] = max(d[*t], d[*(t--)]) + 1;
cout << (res += d[a]) << "\n";
s.insert(a);
}
return 0;
}