(希望各位大佬不要怒对我,我只是想跟大家讨论讨论。)
刚才YY了出了一种数据结构,也不知道叫什么名字(我的意思是,估计以前已经有很多人YY出来过并且已经命了名了),也不知道时间复杂度是否正确(毕竟这是YY出来的),如果各位大神发现了本文证明中的问题,欢迎在下方评论区评论。
定义
这种数据结构是一种二叉搜索树,或者说是一种平衡树。
lch(x) l c h ( x ) 表示 x x 结点的左子,表示 x x 结点的右子。如果一个结点没有左子,则。同理,如果一个结点没有右子,则 rch(x)=null r c h ( x ) = n u l l 。另外,规定 lch(null)=rch(null)=null l c h ( n u l l ) = r c h ( n u l l ) = n u l l 。
len(x) l e n ( x ) 表示以 x x 为根的子树中深度最大的叶子结点的深度,定义式为。规定 len(null)=0 l e n ( n u l l ) = 0 。
旋转同treap/splay。左旋表示把原来的根节点变为新根节点的左子,右旋表示把原来的根节点变为新根节点的右子(对不起,我插入不了图片)。
操作
操作很简单,就是每当 |len(lch(x))−len(rch(x))|>1 | l e n ( l c h ( x ) ) − l e n ( r c h ( x ) ) | > 1 时,把 x x 旋入len较小的子树。
一个随性的代码
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <algorithm>
using namespace std;
const int maxn = 2*1000000 + 6;
namespace BST {
int ch[maxn][2], val[maxn], siz[maxn], len[maxn], ncnt;
void maintain(int rt) {
siz[rt] = 1 + siz[ch[rt][0]] + siz[ch[rt][1]];
len[rt] = 1 + max(len[ch[rt][0]], len[ch[rt][1]]);
}
int shortson(int rt) { /// 返回较 "短"的儿子
return len[ch[rt][0]]<len[ch[rt][1]] ? 0: 1;
}
void rotate(int& x, int d) {
int k = ch[x][d^1]; ch[x][d^1] = ch[k][d];
ch[k][d] = x; maintain(x); x = k; maintain(x);
}
int dir(int t, int v) {
return v<val[t] ? 0:1;
}
void insert(int& t, int v) {
if(t == 0) {
t = ++ ncnt; ch[t][0] = ch[t][1] = 0;
val[t] = v; siz[t] = 1; len[t] = 1; return;
}
int d = dir(t, v); insert(ch[t][d], v); maintain(t);
if(abs(len[ch[t][0]] - len[ch[t][1]]) > 1)
rotate(t, shortson(t)); /// 维护最长链
}
int rnk(int t, int v) {
if(t == 0) return 1;
int lsiz = 1 + siz[ch[t][0]];
int d = dir(t, v);
if(d == 0) return rnk(ch[t][0], v);
else return rnk(ch[t][1], v) + lsiz;
}
#define inf (0x7f7f7f7f)
int kth(int t, int k) {
if(t == 0) return k<=0? -inf:inf;
int lsiz = 1 + siz[ch[t][0]];
if(k <= lsiz) return kth(ch[t][0], k);
else return kth(ch[t][1], k - lsiz);
}
void del(int& t, int x) {
if(t == 0) return;
if(val[t] != x) {
int d = dir(t, x); del(ch[t][d], x);
maintain(t);
if(abs(len[ch[t][0]] - len[ch[t][1]]) > 1)
rotate(t, shortson(t)); /// 维护最长链
}else {
if(ch[t][0]==0 || ch[t][1]==0) {
t = ch[t][0] + ch[t][1]; /// !不能 maintain(0)
}else {
rotate(t, 0); del(ch[t][0], x); /// 在左子中删除
maintain(t);
if(abs(len[ch[t][0]] - len[ch[t][1]]) > 1)
rotate(t, shortson(t)); /// 维护最长链
}
}
}
void debug() {
for(int i = 1; i <= ncnt; i ++) {
printf("%3d: %3d, %3d, %3d\n", i, ch[i][0], ch[i][1], val[i]);
}
}
}
这还有一份:代码 https://paste.ubuntu.com/p/PHFnVZ4Rq4/
证明
我们假定这棵树的任意一个结点,在插入操作结束时都能保证 |len(lch(x))−len(rch(x))|≤1 | l e n ( l c h ( x ) ) − l e n ( r c h ( x ) ) | ≤ 1 。(如果您感兴趣可以试着去证明一发,不过我把这个当成结论。如果有人证明出这是错的,请在评论区评论。)
定义 f(H) f ( H ) 表示数高为H的树拥有的最少的结点数,那么有 f(H)=1+f(H−1)+f(H−2) f ( H ) = 1 + f ( H − 1 ) + f ( H − 2 ) 。理由是这棵树有根节点,除去根节点之后的两棵子树的高度为H-1。又因为两棵树可以有1的高度差,所以可以有一棵子树的高度为H-2。
根据计算可以得出:
f[ 2]= 3
f[ 3]= 5
f[ 4]= 9
f[ 5]= 15
f[ 6]= 25
f[ 7]= 41
f[ 8]= 67
f[ 9]= 109
f[ 10]= 177
f[ 11]= 287
f[ 12]= 465
f[ 13]= 753
f[ 14]= 1219
f[ 15]= 1973
f[ 16]= 3193
f[ 17]= 5167
f[ 18]= 8361
f[ 19]= 13529
f[ 20]= 21891
f[ 21]= 35421
f[ 22]= 57313
f[ 23]= 92735
f[ 24]= 150049
f[ 25]= 242785
f[ 26]= 392835
f[ 27]= 635621
f[ 28]= 1028457
f[ 29]= 1664079
f[ 30]= 2692537
f[ 28]= 1028457 所以,一棵1000000的树的深度不会超过28(随机数据实测深度为25)。
求了一下通项公式,不知道对不对,求各位大佬指导。
fn=(1+15√)⋅(1+5√2)n+(1−15√)⋅(1−5√2)n−1 f n = ( 1 + 1 5 ) ⋅ ( 1 + 5 2 ) n + ( 1 − 1 5 ) ⋅ ( 1 − 5 2 ) n − 1
我的校验程序:
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
#define long long long
const int maxn = 100 + 3;
long f[maxn], g[maxn];
double d(double i) {
double sqrt5 = sqrt(5.0);
double x0 = (1.0 + sqrt5)/2.0;
double x1 = (1.0 - sqrt5)/2.0;
double A = (sqrt5 - 1.0)/2.0;
double B = (-1.0 - sqrt5)/2.0;
double ans = A*pow(x0, i) + B*pow(x1, i);
return ans;
}
double Sd(int n) {
double sqrt5 = sqrt(5.0);
double x0 = (1.0 + sqrt5)/2.0;
double x1 = (1.0 - sqrt5)/2.0;
double A = (sqrt5 - 1.0)/2.0;
double B = (-1.0 - sqrt5)/2.0;
double ans = A*x0*(1.0-pow(x0,n))/(1.0-x0) + B*x1*(1.0-pow(x1,n))/(1.0-x1);
return ans;
}
double Ans(int n) {
double sqrt5 = sqrt(5);
double x0 = (1.0 + sqrt5)/2.0;
double x1 = (1.0 - sqrt5)/2.0;
double A = 1.0 + 1.0/sqrt5;
double B = 1.0 - 1.0/sqrt5;
double ans = A*pow(x0, n) + B*pow(x1, n) -1;
return ans;
}
int main() {
for(int i = -1; i <= 10; i ++) {
printf("%3d : %lf\n", i, Sd(i));
}
f[0] = 1; f[1] = 1; g[0] = 0; g[1] = 1;
for(int i = 2; i <= 30; i ++) {
f[i] = 1 + f[i-1] + f[i-2];
g[i] = g[i-1] + g[i-2];
printf("f[%3d] %10lld %20lf\n", i, f[i], Ans(i));
}
return 0;
}
运行结果:
-1 : 1.000000
0 : 0.000000
1 : 2.000000
2 : 3.000000
3 : 6.000000
4 : 10.000000
5 : 17.000000
6 : 28.000000
7 : 46.000000
8 : 75.000000
9 : 122.000000
10 : 198.000000
f[ 2] 3 3.000000
f[ 3] 5 5.000000
f[ 4] 9 9.000000
f[ 5] 15 15.000000
f[ 6] 25 25.000000
f[ 7] 41 41.000000
f[ 8] 67 67.000000
f[ 9] 109 109.000000
f[ 10] 177 177.000000
f[ 11] 287 287.000000
f[ 12] 465 465.000000
f[ 13] 753 753.000000
f[ 14] 1219 1219.000000
f[ 15] 1973 1973.000000
f[ 16] 3193 3193.000000
f[ 17] 5167 5167.000000
f[ 18] 8361 8361.000000
f[ 19] 13529 13529.000000
f[ 20] 21891 21891.000000
f[ 21] 35421 35421.000000
f[ 22] 57313 57313.000000
f[ 23] 92735 92735.000000
f[ 24] 150049 150049.000000
f[ 25] 242785 242785.000000
f[ 26] 392835 392835.000000
f[ 27] 635621 635621.000000
f[ 28] 1028457 1028457.000000
f[ 29] 1664079 1664079.000000
f[ 30] 2692537 2692537.000000