原文链接https://www.cnblogs.com/zhouzhendong/p/Balanced-Binary-Tree.html

注意是简单教程,不是入门教程。

splay

1. 旋转:

假设点 y 原是点 x 的 father,旋转操作可以在不改变中序遍历的基础上,将 y 变成 x 的儿子。例如:

旋转后:

代码:

int wson(int x){
return son[fa[x]][1]==x;
}
void pushup(int x){
tot[x]=cnt[x]+tot[son[x][0]]+tot[son[x][1]];
}
void rotate(int x){
if (!x)
return;
int y=fa[x],z=fa[y],L=wson(x),R=L^1;
if (z)
son[z][wson(y)]=x;
fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
son[y][L]=son[x][R],son[x][R]=y;
pushup(y),pushup(x);
}

2. splay

可以将一个点旋转到他的一个祖先下面,或者旋转到根的位置。

具体操作:

设当前旋转的点是 x ,y = fa[x] , z = fa[y] 。设 wson(x) 表示节点 x 是他的父亲的左儿子还是右儿子。

如果 wson(x) = wson(y) ,那么先旋 y,再旋 x;

如果 wson(x) $\neq$ wson(y) ,那么先选 x,再旋 x。

单次复杂度均摊 $O(\log n)$ 。

如果每次都旋转 x 的话是可以被卡掉的。

关于 splay 时间复杂度的证明:

https://www.cnblogs.com/zhouzhendong/p/JunTanFenXi.html

反正可以用就行了。注意用法是每找一个点都要 splay ,不管是插入节点还是查找特定值在平衡树中的位置。

代码:

void splay(int x,int k){
if (!x)
return;
if (!k)
root=x;
for (int y=fa[x];fa[x]!=k;rotate(x),y=fa[x])
if (fa[y]!=k)
rotate(wson(x)==wson(y)?y:x);
}

3.插入

假设插入的值是 v。找到比 v 小的最大点,把它splay到根,找到它的右子树的最小值的位置,把新节点插到这个节点的左子节点位置即可。

4.删除

找到节点,splay到根,找到它的右子树的最左点,splay到根下面,这样就保证了根的右儿子没有左儿子,直接把根ban掉,把根左儿子变成右儿子的左儿子就好了。

5.其他

诸如查排名、查第k大、查前驱后继等比较简单,这里省略了。

关于区间修改:

可以支持翻转、区间覆盖、区间乘等等操作。

具体一些的方法是:打懒标记,每次splay之前先往上走一遍预先下传好标记,再splay。

6.模板 (BZOJ3224)

#include <bits/stdc++.h>
using namespace std;
const int N=100005;
int n,root=1,size=1,val[N],cnt[N],son[N][2],fa[N],tot[N];
int wson(int x){
return son[fa[x]][1]==x;
}
void pushup(int x){
tot[x]=cnt[x]+tot[son[x][0]]+tot[son[x][1]];
}
void rotate(int x){
if (!x)
return;
int y=fa[x],z=fa[y],L=wson(x),R=L^1;
if (z)
son[z][wson(y)]=x;
fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
son[y][L]=son[x][R],son[x][R]=y;
pushup(y),pushup(x);
}
void splay(int x,int k){
if (!x)
return;
if (!k)
root=x;
for (int y=fa[x];fa[x]!=k;rotate(x),y=fa[x])
if (fa[y]!=k)
rotate(wson(x)==wson(y)?y:x);
}
int find(int x,int v){
return val[x]==v?x:find(son[x][v>val[x]],v);
}
int findkth(int x,int k){
if (k<=tot[son[x][0]])
return findkth(son[x][0],k);
k-=tot[son[x][0]];
if (k<=cnt[x])
return x;
k-=cnt[x];
return findkth(son[x][1],k);
}
int findnxt(int x,int v){
if (!x)
return 0;
if (val[x]<=v)
return findnxt(son[x][1],v);
else {
int res=findnxt(son[x][0],v);
return res?res:x;
}
}
int findpre(int x,int v){
if (!x)
return 0;
if (val[x]>=v)
return findpre(son[x][0],v);
else {
int res=findpre(son[x][1],v);
return res?res:x;
}
}
void insert(int &x,int pre,int v){
if (!x){
x=++size;
val[x]=v,cnt[x]=tot[x]=1,fa[x]=pre;
splay(x,0);
return;
}
tot[x]++;
if (val[x]==v){
cnt[x]++;
return;
}
insert(son[x][v>val[x]],x,v);
}
void Insert(int v){insert(root,0,v);}
void Delete(int v){
int x;
splay(x=find(root,v),0);
if (--cnt[x])
return;
splay(findnxt(root,v),root);
root=son[x][1];
son[root][0]=son[x][0];
fa[son[x][0]]=root;
fa[root]=son[x][0]=son[x][1]=0;
pushup(root);
}
int Rank(int v){
splay(find(root,v),0);
return tot[son[root][0]]+1;
}
int main(){
val[1]=2147483647;
cnt[1]=tot[1]=1;
scanf("%d",&n);
while (n--){
int opt,x;
scanf("%d%d",&opt,&x);
if (opt==1) Insert(x);
if (opt==2) Delete(x);
if (opt==3) printf("%d\n",Rank(x));
if (opt==4) printf("%d\n",val[findkth(root,x)]);
if (opt==5) printf("%d\n",val[findpre(root,x)]);
if (opt==6) printf("%d\n",val[findnxt(root,x)]);
}
return 0;
}

替罪羊树

1.思想

如果数据是随机的,那么直接暴力插入节点就AC了。

但是直接卡成一条链就TLE了。

那么我们来想想办法。

定期重构?

可以得到 $O(n\sqrt n)$ 的优秀复杂度。

这个东西不够智能,我们来给他升升级!

替罪羊树:当一棵子树的左右儿子不平衡到一定程度的时候,我们将整个子树暴力重构。

具体地,我们要设定一个参数 c ,当一个节点的其中左子树或者右子树占当前子树的比重超过c的时候,就将这个子树暴力重构。

注意,暴力重构的时候要从上往下,这样只重构最大的需要被重构的子树,常数较小。

c 的取值一般在 $0.5~1$ 之间,不能取 1 (取1的时候就是暴力),也不要取 0.5 (重构次数太多导致TLE),取 0.75~0.85 差不多就好了。

关于时间复杂度:

首先,树高显然是 $O(\log n)$ 的,所以查询的复杂度是对的。

我们来看看重构的复杂度是什么。

定义势函数 $\phi(T) = \sum_{x} {abs(size[ls]-size[rs])}$ (假设 ls 是 x 的左儿子,rs 是 x 的右儿子),那么,由于树高是 $O(\log n)$ 的,所以显然插入一个节点最多使 $\phi(T)$ 增加 $O(\log n)$ 。

每次暴力重构点 x 的时候,消耗了 $O(size[x])$ 的时间复杂度,使 $\phi(T)$ 至少减少了 $O(size[x]) \times (c-(1-c)) = O(size[x]\cdot (2c-1))$ ,所以重构的时间复杂度是均摊 $O(\log n)$ 的。

2.一道模板题

UOJ#55 紫荆花之恋

替罪羊树思想不一定只能放在平衡树上,还可以放在点分树上。

不过这题还要套个平衡树,可以用splay + 卡常数,或者 Treap 。

代码

#pragma GCC optimize("Ofast","inline")
#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
using namespace std;
typedef long long LL;
LL read(){
LL x=,f=;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<)+(x<<)+(ch^),ch=getchar();
return f?-x:x;
}
const int N=,INF=1.05e9,mod=1e9;
const double checkval=0.80;
namespace spl{
const int S=N*;
int son[S][],fa[S],val[S],size[S];
int cnt=;
namespace cly{
int st[S],top=;
inline int new_node(){
return top?st[top--]:++cnt;
}
inline void Recover(int x){
st[++top]=x;
son[x][]=son[x][]=fa[x]=val[x]=size[x]=;
}
}
using cly::new_node;
using cly::Recover;
#define ls son[x][0]
#define rs son[x][1]
int new_node(int v){
int x=new_node();
val[x]=v;
return x;
}
inline void pushup(int x){
size[x]=size[ls]+size[rs]+;
}
inline int wson(int x){
return son[fa[x]][]==x;
}
void rotate(int x){
int y=fa[x],z=fa[y],L=son[y][]==x,R=L^;
if (z)
son[z][son[z][]==y]=x;
fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
son[y][L]=son[x][R],son[x][R]=y;
pushup(y),pushup(x);
}
void splay(int x){
for (int y=fa[x];fa[x];rotate(x),y=fa[x])
if (fa[y])
rotate(wson(x)==wson(y)?y:x);
}
inline void Ins(int &a,int v){
register int x=a,c=;
int f=;
while (x){
c++;
size[x]++;
f=x;
x=v<val[x]?ls:rs;
}
val[son[f][v>=val[f]]=x=new_node()]=v,fa[x]=f,size[x]=;
if (!a||c>)
splay(a=x);
}
int Query(int &rt,int v){
int ans=,c=;
register int x=rt,p=;
while (x){
p=x,c++;
if (val[x]<=v)
ans+=size[ls]+,x=rs;
else
x=ls;
}
if (p&&c>)
splay(rt=p);
return ans;
}
void Remove(int x){
if (x)
Remove(ls),Remove(rs),Recover(x);
}
int id[N];
void Build(int &x,int L,int R,int f){
if (L>R)
return;
int mid=(L+R)>>;
fa[x=id[mid]]=f;
Build(ls,L,mid-,x);
Build(rs,mid+,R,x);
pushup(x);
}
void Build(int &x,vector <int> &v){
if (v.empty())
return;
int n=;
sort(v.begin(),v.end());
for (auto i : v)
id[++n]=new_node(i);
Build(x,,n,);
}
#undef ls
#undef rs
}
int Test,n;
LL ans=;
vector <int> e[N];
int fa[N][],depth[N],len[N];
int LCA(int x,int y){
if (depth[x]<depth[y])
swap(x,y);
Fod(i,,)
if (depth[x]-(<<i)>=depth[y])
x=fa[x][i];
if (x==y)
return x;
Fod(i,,)
if (fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][];
}
int Dis(int x,int y){
return len[x]+len[y]-*len[LCA(x,y)];
}
int r[N];
namespace dt{
int fa[N],size[N],mxs[N],depth[N];
int rt[N],rtf[N];
int node_cnt=;
void Init(){
clr(fa),clr(size),clr(mxs),clr(depth),clr(rt),clr(rtf);
depth[]=-,node_cnt++;
size[]=,mxs[]=depth[]=rt[]=;
spl::Ins(rt[],-r[]);
}
int id[N],idc=;
void dfs(int x,int pre,int d){
id[++idc]=x;
for (auto y : e[x])
if (y!=pre&&depth[y]>=d)
dfs(y,x,d);
}
int sz[N],msz[N],RT,Size;
void dfs2(int x,int pre){
sz[x]=,msz[x]=;
for (auto y : e[x])
if (y!=pre&&!size[y]){
dfs2(y,x);
sz[x]+=sz[y];
msz[x]=max(msz[x],sz[y]);
}
msz[x]=max(msz[x],Size-sz[x]);
if (!RT||msz[x]<msz[RT])
RT=x;
}
vector <int> id1,id2;
void dfs3(int x,int pre,int anc){
sz[x]=;
id1.pb(Dis(x,anc)-r[x]);
if (fa[anc])
id2.pb(Dis(x,fa[anc])-r[x]);
for (auto y : e[x])
if (y!=pre&&!size[y])
dfs3(y,x,anc),sz[x]+=sz[y];
}
void build(int x,int f,int n){
RT=,Size=n;
dfs2(x,);
x=RT;
fa[x]=f,depth[x]=depth[f]+,size[x]=n,mxs[x]=msz[x];
id1.clear(),id2.clear();
dfs3(x,,x);
spl::Build(rt[x],id1);
spl::Build(rtf[x],id2);
for (auto y : e[x])
if (!size[y])
build(y,x,sz[y]);
}
void Rebuild(int x,int f){
idc=;
dfs(x,,depth[x]);
For(_t,,idc){
int i=id[_t];
spl::Remove(rt[i]),spl::Remove(rtf[i]);
depth[i]=size[i]=fa[i]=mxs[i]=rt[i]=rtf[i]=;
}
build(x,f,idc);
}
void Ins(int x,int f){
static vector <int> v;
fa[x]=f,depth[x]=depth[f]+;
rt[x]=rtf[x]=;
v.clear();
for (int i=x;i;i=fa[i]){
v.pb(i),size[i]++;
mxs[fa[i]]=max(mxs[fa[i]],size[i]);
spl::Ins(rt[i],Dis(i,x)-r[x]);
if (fa[i])
spl::Ins(rtf[i],Dis(fa[i],x)-r[x]);
}
node_cnt++;
reverse(v.begin(),v.end());
for (auto i : v)
if (mxs[i]>checkval*size[i]){
Rebuild(i,fa[i]);
break;
}
if (size[x]>)
ans+=spl::Query(rt[x],r[x])-;
for (int i=x,f,d;depth[i];i=f){
f=fa[i],d=Dis(f,x);
ans+=spl::Query(rt[f],r[x]-d)-spl::Query(rtf[i],r[x]-d);
}
}
}
int main(){
Test=read(),n=read();
For(i,,n){
int f=read()^(ans%mod),c=read();
r[i]=read();
if (!f){
dt::Init();
printf("%lld\n",ans);
continue;
}
assert(<=f&&f<i);
e[i].pb(f),e[f].pb(i);
fa[i][]=f;
For(j,,)
fa[i][j]=fa[fa[i][j-]][j-];
depth[i]=depth[f]+;
len[i]=len[f]+c;
dt::Ins(i,f);
printf("%lld\n",ans);
}
return ;
}

紫荆花之恋

非旋Treap

1. Treap 简介

Treap = Tree + Heap 。

我们考虑给每一个节点随机赋一个值 ckv ,假设这个节点本来的值是 val,那么,我们建一个平衡树,满足对于 val,它是个二叉搜索树,对于 ckv 它满足堆性质,就是满足 $val[ls]\leq val[x]\leq val[rs], ckv[x]\leq ckv[ls], ckv[x] \leq ckv[rs]$。

可以证明这样的树是唯一的。

由于它的随机的,所以树高是 $O(\log n)$ 的。

我们来看看插入节点的时候怎么维护它的性质:旋转!首先插入到一个满足二叉搜索树性质的位置,然后,如果 ckv[x] < ckv[fa[x]] ,那么将 x 上旋,直到满足这个条件为止。

这个需要旋转的 Treap 有一定的局限性,接下来介绍功能更加强大的非旋 Treap 。

2. Merge

合并 Treap x 和 Treap y,需要保证 x 中的任意元素小于 y 中的任意元素

假设 x 为空,则返回 y 。

假设 y 为空,则返回 x 。

假设 ckv[x] < ckv[y] ,则将点 x 作为根, rs[x] = Merge(rs[x], y)

否则,将点 y 作为根, ls[y] = Merge(x, ls[y])  (注意这里 x 要放在前面)

时间复杂度 $O(\log n)$ 。

代码:

    int Merge(int x,int y){
if (!x||!y)
return x+y;
if (ckv[x]<ckv[y]){
son[x][1]=Merge(son[x][1],y),pushup(x);
return x;
}
else {
son[y][0]=Merge(x,son[y][0]),pushup(y);
return y;
}
}

3. Split

这个函数的作用就是将一个平衡树前 k 小节点构成的平衡树和剩余节点构成的平衡树。时间复杂度 $O(\log n)$ 。

直接看代码很容易理解的。

代码:

    pair <int,int> Split(int x,int k){
if (!x)
return mp(0,0);
if (k<=size[ls]){
pair <int,int> p=Split(ls,k);
ls=p.se,pushup(x);
return mp(p.fi,x);
}
else {
pair <int,int> p=Split(rs,k-size[ls]-1);
rs=p.fi,pushup(x);
return mp(x,p.se);
}
}

4. 其他操作

Rank : 直接写。

Find-Kth = Split + Merge

Insert = Rank + Split + Merge + Merge

Delete = Split + Split + Merge

区间修改:由于每次操作都是自顶向下的,所以可以进行标记下传,所以支持区间修改。

可持久化:由于每次操作都是自顶向下的,而且只改变节点的儿子,所以是可以可持久化的。这一点其他平衡树都做不了。

5. 模板(BZOJ3224)

#include <bits/stdc++.h>
#define clr(x) memset(x,0,sizeof (x))
#define For(i,a,b) for (int i=a;i<=b;i++)
#define Fod(i,b,a) for (int i=b;i>=a;i--)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
#define _SEED_ ('C'+'L'+'Y'+'A'+'K'+'I'+'O'+'I')
#define outval(x) printf(#x" = %d\n",x)
#define outvec(x) printf("vec "#x" = ");for (auto _v : x)printf("%d ",_v);puts("")
#define outtag(x) puts("----------"#x"----------")
using namespace std;
typedef long long LL;
LL read(){
LL x=0,f=0;
char ch=getchar();
while (!isdigit(ch))
f|=ch=='-',ch=getchar();
while (isdigit(ch))
x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
return f?-x:x;
}
const int N=100005;
int randint(){
#ifdef windows
return (rand()<<15)^rand();
#else
return rand();
#endif
}
namespace Treap{
const int S=N;
int son[N][2],size[N],val[N],ckv[N];
int cnt=0;
#define ls son[x][0]
#define rs son[x][1]
int new_node(int v){
cnt++,val[cnt]=v,ckv[cnt]=randint(),size[cnt]=1;
return cnt;
}
void pushup(int x){
size[x]=size[ls]+size[rs]+1;
}
int Merge(int x,int y){
if (!x||!y)
return x+y;
if (ckv[x]<ckv[y]){
son[x][1]=Merge(son[x][1],y),pushup(x);
return x;
}
else {
son[y][0]=Merge(x,son[y][0]),pushup(y);
return y;
}
}
pair <int,int> Split(int x,int k){
if (!x)
return mp(0,0);
if (k<=size[ls]){
pair <int,int> p=Split(ls,k);
ls=p.se,pushup(x);
return mp(p.fi,x);
}
else {
pair <int,int> p=Split(rs,k-size[ls]-1);
rs=p.fi,pushup(x);
return mp(x,p.se);
}
}
int Find(int x,int v){
return x?(val[x]==v?x:Find(son[x][val[x]<v],v)):0;
}
int _Rank(int x,int v){
return x?(val[x]>v?_Rank(ls,v):size[ls]+1+_Rank(rs,v)):0;
}
int Rank(int x,int v){
return _Rank(x,v-1);
}
void Insert(int &x,int v){
pair <int,int> p=Split(x,_Rank(x,v));
x=Merge(p.fi,Merge(new_node(v),p.se));
}
void Delete(int &x,int v){
int rk=Rank(x,v);
pair <int,int> pL=Split(x,rk),pR=Split(pL.se,1);
x=Merge(pL.fi,pR.se);
}
int QRank(int x,int v){
return Rank(x,v)+1;
}
int Find_Kth(int x,int k){
return k==size[ls]+1?x:(k<=size[ls]?Find_Kth(ls,k):Find_Kth(rs,k-size[ls]-1));
}
int Get_pre(int &x,int v){
return Find_Kth(x,Rank(x,v));
}
int Get_nxt(int &x,int v){
return Find_Kth(x,_Rank(x,v)+1);
}
#undef ls
#undef rs
}
using namespace Treap;
int n,root=0;
int main(){
srand(_SEED_);
n=read();
while (n--){
int opt=read(),x=read();
if (opt==1)
Insert(root,x);
else if (opt==2)
Delete(root,x);
else if (opt==3)
printf("%d\n",QRank(root,x));
else if (opt==4)
printf("%d\n",val[Find_Kth(root,x)]);
else if (opt==5)
printf("%d\n",val[Get_pre(root,x)]);
else if (opt==6)
printf("%d\n",val[Get_nxt(root,x)]);
}
return 0;
}

一些题目(点击进入)

平衡树简单教程及模板(splay, 替罪羊树, 非旋treap)的更多相关文章

  1. 替罪羊树&&非旋treap

    题解: 替罪羊树的模板和splay差距还是比较大的.. 按照我的splay的写法 真是都是问题.. 替罪羊树就是暴力的搞 当某颗子树大小大于这棵树的alpha时 就退出 另外删除的时候打懒标记删除 当 ...

  2. [模板] 平衡树: Splay, 非旋Treap, 替罪羊树

    简介 二叉搜索树, 可以维护一个集合/序列, 同时维护节点的 \(size\), 因此可以支持 insert(v), delete(v), kth(p,k), rank(v)等操作. 另外, prev ...

  3. 非旋Treap总结 : 快过Splay 好用过传统Treap

    非旋$Treap$ 其高级名字叫$Fhq\ Treap$,既然叫$Treap$,它一定满足了$Treap$的性质(虽然可能来看这篇的人一定知道$Treap$,但我还是多说几句:$Fhp\ Treap$ ...

  4. 2018.08.05 bzoj3223: Tyvj 1729 文艺平衡树(非旋treap)

    传送门 经典的平衡树问题,之前已经用splay写过一次了,今天我突发奇想,写了一发非旋treap的版本,发现挺好写的(虽然跑不过splay). 代码: #include<bits/stdc++. ...

  5. 平衡树及笛卡尔树讲解(旋转treap,非旋转treap,splay,替罪羊树及可持久化)

    在刷了许多道平衡树的题之后,对平衡树有了较为深入的理解,在这里和大家分享一下,希望对大家学习平衡树能有帮助. 平衡树有好多种,比如treap,splay,红黑树,STL中的set.在这里只介绍几种常用 ...

  6. 平衡树之非旋Treap

    平衡树(二叉树) 线段树不支持插入or删除一个数于是平衡树产生了 常见平衡树:treap(比sbt慢,好写吧),SBT(快,比较好写,有些功能不支持),splay(特别慢,复杂度当做根号n来用,功能强 ...

  7. 模板 - 数据结构 - 可持久化无旋Treap/PersistentFHQTreap

    有可能当树中有键值相同的节点时,貌似是要对Split和Merge均进行复制的,本人实测:只在Split的时候复制得到了一个WA,但只在Merge的时候复制还是AC,可能是恰好又躲过去了.有人说假如确保 ...

  8. 左偏树 / 非旋转treap学习笔记

    背景 非旋转treap真的好久没有用过了... 左偏树由于之前学的时候没有写学习笔记, 学得也并不牢固. 所以打算写这么一篇学习笔记, 讲讲左偏树和非旋转treap. 左偏树 定义 左偏树(Lefti ...

  9. P2042 [NOI2005]维护数列[splay或非旋treap·毒瘤题]

    P2042 [NOI2005]维护数列 数列区间和,最大子列和(必须不为空),支持翻转.修改值.插入删除. 练码力的题,很毒瘤.个人因为太菜了,对splay极其生疏,犯了大量错误,在此记录,望以后一定 ...

随机推荐

  1. Java执行jar总结

    1.命令集 1)nohup 用途:不挂断地运行命令. 语法:nohup Command [ Arg … ] [ & ] 无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 ...

  2. UOJ10 UTR #1 pyx的难题(堆)

    显然优先级越高完成的越早,二分答案后用堆模拟就是O(nlog2n)的.考虑去一个log.先固定特殊题的优先级为最低,模拟一遍.这样在特殊题被扔过来到T的这段时间内,如果将特殊题的优先级提高至超过这其中 ...

  3. TP5.x——多数据库连接查询

    前言 需要到不同的数据库获取不同的表,看了下文档发现有这类方法,就记录下 文档 https://www.kancloud.cn/manual/thinkphp5_1/353998 步骤 配置文件 re ...

  4. HTML- 标签语法

    HTML 标签语言 概念  超文本标记语言, 是一种用于创建网页的标记语言 ps: 不是编程语言 利用标签来描述网页 扩展名:.html .htm 语法规范 标签不区分大小写, 推荐小写 双标签必须写 ...

  5. HashMap初认识

    什么是HashSet? 它实现了Set接口,HashSet是Set集合的子类 有哈希表支持的,元素不可重复的哈希码值(实际上是一个HashMap的实例). 它不保证set的迭代顺序(遍历元素的顺序), ...

  6. request param 获取

    通过request对象获取客户端请求信息 getRequestURL方法返回客户端发出请求时的完整URL. getRequestURI方法返回请求行中的资源名部分. getQueryString 方法 ...

  7. Wannafly挑战赛23 T2游戏 SG函数

    哎,被卡科技了,想了三个小时,最后还是大佬给我说是\(SG\)函数. \(SG\)函数,用起来很简单,证明呢?(不可能的,这辈子都是不可能的) \(SG\)定理 游戏的\(SG\)函数就是各个子游戏的 ...

  8. Mybatis-批量执行

    一.使用动态SQL 中的 Foreach 批量插入 1.MySQL // 实体类 public class MyUser { private Integer id; private String na ...

  9. 苹果手机iOS11中fixed弹出框中input光标错位问题

    最近遇到了一个移动前端的BUG:手机弹出框中的输入框focus时光标可能会错位. 刚开始时我完全不知道错误原因是什么,在电脑上调试时完全没有问题,手机上出现问题时也没有找到规律.后来在网上搜索了大量的 ...

  10. linux中使用gdb调试程序

     ref:https://blog.csdn.net/tenfyguo/article/details/8159176 一,什么是coredump 我们经常听到大家说到程序core掉了,需要定位解决, ...