三大平衡树(Treap + Splay + SBT)总结+模板

Treap树

  核心是 利用随机数的二叉排序树的各种操作复杂度平均为O(lgn)

Treap模板:

#include <cstdio>
#include <cstring>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <cstdlib>
#include <cmath>
#include <utility>
#include <vector>
#include <queue>
#include <map>
#include <set>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)>(y)?(y):(x))
#define INF 0x3f3f3f3f
#define MAXN 100005using namespace std;int cnt=1,rt=0; //节点编号从1开始struct Tree
{int key, size, pri, son[2]; //保证父亲的pri大于儿子的privoid set(int x, int y, int z){key=x;pri=y;size=z;son[0]=son[1]=0;}
}T[MAXN];void rotate(int p, int &x)
{int y=T[x].son[!p];T[x].size=T[x].size-T[y].size+T[T[y].son[p]].size;T[x].son[!p]=T[y].son[p];T[y].size=T[y].size-T[T[y].son[p]].size+T[x].size;T[y].son[p]=x;x=y;
}void ins(int key, int &x)
{if(x == 0)T[x = cnt++].set(key, rand(), 1);else{T[x].size++;int p=key < T[x].key;ins(key, T[x].son[!p]);if(T[x].pri < T[T[x].son[!p]].pri)rotate(p, x);}
}void del(int key, int &x) //删除值为key的节点
{if(T[x].key == key){if(T[x].son[0] && T[x].son[1]){int p=T[T[x].son[0]].pri > T[T[x].son[1]].pri;rotate(p, x);del(key, T[x].son[p]);}else{if(!T[x].son[0])x=T[x].son[1];elsex=T[x].son[0];}}else{T[x].size--;int p=T[x].key > key;del(key, T[x].son[!p]);}
}int find(int p, int &x) //找出第p小的节点的编号
{if(p == T[T[x].son[0]].size+1)return x;if(p > T[T[x].son[0]].size+1)find(p-T[T[x].son[0]].size-1, T[x].son[1]);elsefind(p, T[x].son[0]);
}int find_NoLarger(int key, int &x) //找出值小于等于key的节点个数
{if(x == 0)return 0;if(T[x].key <= key)return T[T[x].son[0]].size+1+find_NoLarger(key, T[x].son[1]);elsereturn find_NoLarger(key, T[x].son[0]);    
}
View Code

相关题解:

POJ 3481 treap

POJ 1442 treap

POJ 2352 treap

 

 

Splay Tree(伸展树)

  核心就是 过程Splay(x, y),即将x节点转移到y节点的子节点上面(其中y是x的祖先)。

  利用其中双旋的优势能够保证查询复杂度均摊为O(lgn)

  一开始理解有些困难,其实实际上不做深入的理解就是,双旋的过程就是一个建立相对平衡的二叉树的一个过程。

  》对于二叉树,最极端的情况就是线性插入,使得整棵二叉树退化为一条链。比如你查询链的最后一个节点,之后再次查询第一个节点。

    1)若只是单旋通过Splay(x, 0)将最后一个节点移动到根节点,需要O(n)复杂度,而查询第一个节点时又需要O(n)复杂度,来来往往就退化成一条链了。

    2)若是双旋Splay(x, 0)将最后一个节点移动到根节点上时,移动过程中建立起了相对平衡的二叉树,需要O(n),也就是查询第一个节点时,大概是需要O(lgn)复杂度。这就降低了复杂度。可以证明,总的每个操作的均摊复杂度是O(lgn)。

    具体证明可以参见 杨思雨《伸展树的基本操作与应用》

 

I 用于维护单调队列:(以key为维护对象保证单调)

常用版:(支持相同值)

Struct Tree{

  int key, size, fa, son[2];

}

void PushUp(int x);

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To) //将x节点插入到To的子节点中

int find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处

int prev() //返回比根值小的最大值 若无返回0 若有将其转移到根处

int succ() //返回比根值大的最小值 若无返回0 若有将其转移到根处

void Insert(int key) //插入key 并且将该节点转移到根处

void Delete(int key) //删除值为key的节点 若有重点只删其中一个 x的前驱移动到根处

int GetPth(int p) //获得第p小的节点 并将其转移到根处

int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<

模板:
int cnt=1, rt=0;struct Tree
{int key, size, fa, son[2];void set(int _key, int _size, int _fa){key=_key;size=_size;fa=_fa;son[0]=son[1]=0;}
}T[MAXN];inline void PushUp(int x)
{T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1;
}inline void Rotate(int x, int p) //0左旋 1右旋
{int y=T[x].fa;T[y].son[!p]=T[x].son[p];T[T[x].son[p]].fa=y;T[x].fa=T[y].fa;if(T[x].fa)T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;T[x].son[p]=y;T[y].fa=x;PushUp(y);PushUp(x);
}void Splay(int x, int To) //将x节点插入到To的子节点中
{while(T[x].fa != To){if(T[T[x].fa].fa == To)Rotate(x, T[T[x].fa].son[0] == x);else{int y=T[x].fa, z=T[y].fa;int p=(T[z].son[0] == y);if(T[y].son[p] == x)Rotate(x, !p), Rotate(x, p); //之字旋elseRotate(y, p), Rotate(x, p); //一字旋
        }}if(To == 0) rt=x;
}int find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{int x=rt;while(x && T[x].key != key)x=T[x].son[key > T[x].key];if(x) Splay(x, 0);return x;
}int prev() //返回比根值小的最大值 若无返回0 若有将其转移到根处
{int x=T[rt].son[0];if(!x) return 0;while(T[x].son[1])x=T[x].son[1];Splay(x, 0);return x;
}int succ() //返回比根值大的最小值 若无返回0 若有将其转移到根处
{int x=T[rt].son[1];if(!x) return 0;while(T[x].son[0])x=T[x].son[0];Splay(x, 0);return x;
}void Insert(int key) //插入key 并且将该节点转移到根处
{if(!rt)T[rt = cnt++].set(key, 1, 0);else{int x=rt, y=0;while(x){y=x;x=T[x].son[key > T[x].key];}T[x = cnt++].set(key, 1, y);T[y].son[key > T[y].key]=x;Splay(x, 0);}
}void Delete(int key) //删除值为key的节点 若有重点只删其中一个 x的前驱移动到根处
{int x=find(key);if(!x) return;int y=T[x].son[0];while(T[y].son[1])y=T[y].son[1];int z=T[x].son[1];while(T[z].son[0])z=T[z].son[0];if(!y && !z){rt=0;return;}if(!y){Splay(z, 0);T[z].son[0]=0;PushUp(z);return;}if(!z){Splay(y, 0);T[y].son[1]=0;PushUp(y);return;}Splay(y, 0);Splay(z, y);T[z].son[0]=0;PushUp(z);PushUp(y);
}int GetPth(int p) //获得第p小的节点 并将其转移到根处
{if(!rt) return 0;int x=rt, ret=0;while(x){if(p == T[T[x].son[0]].size+1)break;if(p>T[T[x].son[0]].size+1){p-=T[T[x].son[0]].size+1;x=T[x].son[1];}elsex=T[x].son[0];}Splay(x, 0);return x;
}int GetRank(int key) //获得值<=key的节点个数 并将其转移到根处 若<key只需将<=换为<
{if(!rt) return 0;int x=rt, ret=0, y;while(x){y=x;if(T[x].key <= key){ret+=T[T[x].son[0]].size+1;x=T[x].son[1];}elsex=T[x].son[0];}Splay(y, 0);return ret;
}
View Code

 

完全版:(支持相同值,支持区间删除,支持懒惰标记)

Struct Tree{

  int key, num, size, fa, son[2];

}

void PushUp(int x);

void PushDown(int x);

int Newnode(int key, int fa); //新建一个节点并返回

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To); //将x节点移动到To的子节点中

int GetPth(int p, int To); //返回第p小的节点 并移动到To的子节点中

int Find(int key); //返回值为key的节点 若无返回0 若有将其转移到根处

int Prev(); //返回根节点的前驱

int Succ(); //返回根结点的后继

void Insert(int key); //插入key值

void Delete(int key); //删除值为key的节点

int GetRank(int key); //获得值<=key的节点个数

void Delete(int l, int r); //删除值在[l, r]中的节点

模板:
int cnt, rt;
int Add[MAXN];struct Tree{int key, num, size, fa, son[2];
}T[MAXN];inline void PushUp(int x)
{T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+T[x].num;
}inline void PushDown(int x)
{if(Add[x]){if(T[x].son[0]){T[T[x].son[0]].key+=Add[x];Add[T[x].son[0]]+=Add[x];}if(T[x].son[1]){T[T[x].son[1]].key+=Add[x];Add[T[x].son[1]]+=Add[x];}Add[x]=0;}
}inline int Newnode(int key, int fa) //新建一个节点并返回
{++cnt;T[cnt].key=key;T[cnt].num=T[cnt].size=1;T[cnt].fa=fa;T[cnt].son[0]=T[cnt].son[1]=0;return cnt;
}inline void Rotate(int x, int p) //0左旋 1右旋
{int y=T[x].fa;PushDown(y);PushDown(x);T[y].son[!p]=T[x].son[p];T[T[x].son[p]].fa=y;T[x].fa=T[y].fa;if(T[x].fa)T[T[x].fa].son[T[T[x].fa].son[1] == y]=x;T[x].son[p]=y;T[y].fa=x;PushUp(y);PushUp(x);
}void Splay(int x, int To) //将x节点移动到To的子节点中
{while(T[x].fa != To){if(T[T[x].fa].fa == To)Rotate(x, T[T[x].fa].son[0] == x);else{int y=T[x].fa, z=T[y].fa;int p=(T[z].son[0] == y);if(T[y].son[p] == x)Rotate(x, !p), Rotate(x, p); //之字旋elseRotate(y, p), Rotate(x, p); //一字旋
        }}if(To == 0) rt=x;
}int GetPth(int p, int To) //返回第p小的节点 并移动到To的子节点中
{if(!rt || p > T[rt].size) return 0;int x=rt;while(x){PushDown(x);if(p >= T[T[x].son[0]].size+1 && p <= T[T[x].son[0]].size+T[x].num)break;if(p > T[T[x].son[0]].size+T[x].num){p-=T[T[x].son[0]].size+T[x].num;x=T[x].son[1];}elsex=T[x].son[0];}Splay(x, 0);return x;
}int Find(int key) //返回值为key的节点 若无返回0 若有将其转移到根处
{if(!rt) return 0;int x=rt;while(x){PushDown(x);if(T[x].key == key) break;x=T[x].son[key > T[x].key];}if(x) Splay(x, 0);return x;
}int Prev() //返回根节点的前驱 非重点
{if(!rt || !T[rt].son[0]) return 0;int x=T[rt].son[0];while(T[x].son[1]){PushDown(x);x=T[x].son[1];}Splay(x, 0);return x;
}int Succ() //返回根结点的后继 非重点
{if(!rt || !T[rt].son[1]) return 0;int x=T[rt].son[1];while(T[x].son[0]){PushDown(x);x=T[x].son[0];}Splay(x, 0);return x;
}void Insert(int key) //插入key值
{if(!rt)rt=Newnode(key, 0);else{int x=rt, y=0;while(x){PushDown(x);y=x;if(T[x].key == key){T[x].num++;T[x].size++;break;}T[x].size++;x=T[x].son[key > T[x].key];}if(!x)x=T[y].son[key > T[y].key]=Newnode(key, y);Splay(x, 0);}
}void Delete(int key) //删除值为key的节点1个
{int x=Find(key);if(!x) return;if(T[x].num>1){T[x].num--;PushUp(x);return;}int y=T[x].son[0];while(T[y].son[1])y=T[y].son[1];int z=T[x].son[1];while(T[z].son[0])z=T[z].son[0];if(!y && !z){rt=0;return;}if(!y){Splay(z, 0);T[z].son[0]=0;PushUp(z);return;}if(!z){Splay(y, 0);T[y].son[1]=0;PushUp(y);return;}Splay(y, 0);Splay(z, y);T[z].son[0]=0;PushUp(z);PushUp(y);
}int GetRank(int key) //获得值<=key的节点个数
{if(!Find(key)){Insert(key);int tmp=T[T[rt].son[0]].size;Delete(key);return tmp;}elsereturn T[T[rt].son[0]].size+T[rt].num;
}void Delete(int l, int r) //删除值在[l, r]中的所有节点 l!=r
{if(!Find(l)) Insert(l);int p=Prev();if(!Find(r)) Insert(r);int q=Succ();if(!p && !q){rt=0;return;}if(!p){T[rt].son[0]=0;PushUp(rt);return;}if(!q){Splay(p, 0);T[rt].son[1]=0;PushUp(rt);return;}Splay(p, q);T[p].son[1]=0;PushUp(p);PushUp(q);
}
View Code

(经测NOI2004郁闷的出纳员 POJ3481 POJ2352 POJ1442)

速度相对来说都还不错,POJ这些都3~500ms,郁闷的出纳员900多ms

 

相关题解:

HNOI 2002 营业额统计

POJ 3481 splay

POJ 2352 splay

POJ 1442 splay

NOI2004 郁闷的出纳员 

 

II 用于维护序列:(以序列下标为对象维护,相当于对区间操作)(能够完成线段树的操作及其不能完成的操作)

 Struct Tree{

  int key, sum, size, fa, son[2];

}

支持操作:

void PushUp(int x);

void PushDown(int x);

int MakeTree(int l, int r, int a[]); //新建一个子树返回根节点

void Rotate(int x, int p); //0左旋 1右旋

void Splay(int x, int To); //将x节点移动到To的子节点中

int Select(int p, int To); //将第p个数移动到To的子节点中 并返回该节点

int Find(int key); //返回值为key的节点 若无返回0 若有将其转移到根处

int Prev(); //返回根节点的前驱

int Succ(); //返回根结点的后继

void Insert(int p, int l, int r, int a[]) //将a[l .. r]的数插入到下标为p后面

void Delete(int l, int r); //删除区间[l, r]中的节点

int Query(int l, int r); //返回[l, r]的和

待补充。。

 

Size Balance Tree

  和上述两种二叉树比起来,SBT可能是最像真正平衡二叉树吧。

  SBT能够保证树的高度在lgn,这样对于插入,删除操作都能够准确保证时间复杂度在O(lgn)

  Maintain操作事实上理解起来也是挺简单的,至于证明参见CQF神牛的《SBT》

 

int cnt, rt;struct Tree
{int key, size, son[2];
}T[MAXN];inline void PushUp(int x)
{T[x].size=T[T[x].son[0]].size+T[T[x].son[1]].size+1;
}inline int Newnode(int key)
{++cnt;T[cnt].key=key;T[cnt].size=1;T[cnt].son[0]=T[cnt].son[1]=0;return cnt;
}void Rotate(int p, int &x)
{int y=T[x].son[!p];T[x].son[!p]=T[y].son[p];T[y].son[p]=x;PushUp(x);PushUp(y);x=y;
}void Maintain(int &x, int p) //维护SBT的!p子树
{if(T[T[T[x].son[p]].son[p]].size > T[T[x].son[!p]].size)Rotate(!p, x);else if(T[T[T[x].son[p]].son[!p]].size > T[T[x].son[!p]].size)Rotate(p, T[x].son[p]), Rotate(!p, x);else return;Maintain(T[x].son[0], 0);Maintain(T[x].son[1], 1);Maintain(x, 0);Maintain(x, 1);
}inline int Prev() //返回比根值小的最大值 若无返回0
{int x=T[rt].son[0];if(!x) return 0;while(T[x].son[1])x=T[x].son[1];return x;
}inline int Succ() //返回比根值大的最小值 若无返回0
{int x=T[rt].son[1];if(!x) return 0;while(T[x].son[0])x=T[x].son[0];return x;
}void Insert(int key, int &x)
{if(!x) x=Newnode(key);else{T[x].size++;Insert(key, T[x].son[key > T[x].key]);Maintain(x, key > T[x].key);}
}bool Delete(int key, int &x) //删除值为key的节点 key可以不存在
{if(!x) return 0;if(T[x].key == key){if(!T[x].son[0]){x=T[x].son[1];return 1;}if(!T[x].son[1]){x=T[x].son[0];return 1;}int y=Prev();T[x].size--;return Delete(T[x].key, T[x].son[0]);}elseif(Delete(key, T[x].son[key > T[x].key])){T[x].size--;return 1;}
}int GetPth(int p, int &x) //返回第p小的节点
{if(!x) return 0;if(p == T[T[x].son[0]].size+1)return x;if(p > T[T[x].son[0]].size+1)return GetPth(p-T[T[x].son[0]].size-1, T[x].son[1]);elsereturn GetPth(p, T[x].son[0]);
}int GetRank(int key, int &x) //找出值<=key的节点个数
{if(!x) return 0;if(T[x].key <= key)return T[T[x].son[0]].size+1+GetRank(key, T[x].son[1]);elsereturn GetRank(key, T[x].son[0]);
}
View Code

 

相关题解:

POJ 3481 SBT做法

 

上述题均为用于测试平衡树基本操作的题目。

提高题:(暂时未写)

[NOI2005]维修数列

[POJ3580]SuperMemo

[HNOI2004]宠物收养所

 

转载于:https://www.cnblogs.com/Mathics/p/3971220.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/376647.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

mysqld进程 ut_delay 占用率过高

采用性能分析工具perf top -p mysqld进程 在测试mysql数据库时&#xff0c;用perf top如果看到热点函数是ut_delay或者_raw_spin_lock的话&#xff0c;说明锁争用比较严重。 ut_delay这是innodb的一个自旋琐。也就是说&#xff0c;在这里由于锁等待&#xff0c;innodb不停地在…

滑动窗口在重构数据集的作用

step1&#xff1a;使用滑动窗口重构数据集 给定时间序列数据集的数字序列&#xff0c;我们可以将数据重构为看起来像监督学习问题。 我们可以通过使用以前的时间步作为输入变量并使用下一个时间步作为输出变量来做到这一点。 通过观察重构后的数据集与原本的时间序列&…

sliverlight - Unhandled Error in Silverlight Application错误

使用firebug控制台输出错误&#xff1a; Unhandled Error in Silverlight Application 查询“GetFlow_Process”的 Load 操作失败。远程服务器返回了错误: NotFound。 位于 System.ServiceModel.DomainServices.Client.OperationBase.Complete(Exception error) 位于 System.S…

前向验证对于模型的更新作用

首先&#xff0c;让我们看一个小的单变量时间序列数据&#xff0c;我们将用作上下文来理解这三种回测方法&#xff1a;太阳黑子数据集。该数据集描述了刚刚超过 230 年&#xff08;1749-1983 年&#xff09;观察到的太阳黑子数量的每月计数。 数据集显示了季节之间差异很大的…

PHP-面向对象(八)

1、多态的介绍与优势 多态性是继抽象和继承后&#xff0c;面向对象语言的第三个特征。从字面上理解&#xff0c;多态的意思是“多种形态”&#xff0c;简单来说&#xff0c;多态是具有表现多种形态的能力的特征&#xff0c;在OO中是指“语言具有根据对象的类型以不同方式处理。…

双指数平滑中参数对于预测模型的影响

先看看α 在β一致的情况下&#xff0c;α越小&#xff0c;模型越滞后。 再看看β 在α一致的情况下&#xff0c;β越大&#xff0c;模型对于趋势的预测更敏锐。

分页查询

分页查询算是比较常用的一个查询了在DAO层主要是查两个数据第一个总条数第二个要查询起始记录数到查询的条数当第一次点击查询时候(非下一页时Page类里面预设的就是 index就是0 pageSize是预设值当点击下一页的时候 index 和 pageSize带的就是页面上面给的值了分页的页面一般的…

me23n去价格

SELECT knumv kposn AS ebelp kschl kbetr kpein kwert INTO CORRESPONDING FIELDS OF TABLE gt_konv FROM konv FOR ALL ENTRIES IN gt_ekpo WHERE knumv gt_ekpo-knumv AND kinak EQ AND kschl IN (PB00,PBXX,P101).转载于:…

使用Bootstrap-table创建表单,并且与flask后台进行数据交互

文章目录引用css和js使用htmljavascriptflaskmysql参考引用css和js Bootstrap-table为这些文件提供了 CDN 的支持&#xff0c;所以不需要下载.js .css文件就可以直接用了&#xff0c;十分方便 <!-- Latest compiled and minified CSS --> <link rel"stylesheet…

使用Xcode和Instruments调试解决iOS内存泄露

虽然iOS 5.0版本之后加入了ARC机制&#xff0c;但由于相互引用关系比较复杂时&#xff0c;内存泄露还是可能存在。所以了解原理很重要。 这里讲述在没有ARC的情况下&#xff0c;如何使用Instruments来查找程序中的内存泄露&#xff0c;以及NSZombieEnabled设置的使用。 本文假设…

五大主流浏览器 HTML5 和 CSS3 兼容性比较

转眼又已过去了一年&#xff0c;在这一年里&#xff0c;Firefox 和 Chrome 在拼升级&#xff0c;版本号不断飙升&#xff1b;IE10 随着 Windows 8 在去年10月底正式发布&#xff0c;在 JavaScript 性能和对 HTML5 和 CSS3 的支持方面让人眼前一亮。这篇文章给大家带来《五大主流…

精通 VC++ 实效编程280例 - 02 菜单和光标

菜单和关闭时重要的 Windows 资源之一。SDK 中&#xff0c;用 HCURSOR 和 HMENU 分别表示菜单和光标的句柄。MFC 中&#xff0c;CMenu 类封装了菜单的功能。 23 动态添加和删除菜单项 添加菜单项可以调用 CMenu::AppendMenu 或 CMenu::InserMenu 函数&#xff0c;删除菜单项可以…

我的osu游戏程序设计(oo)

osu是一款社区元素为主旨的音乐游戏,由澳大利亚人Dean Herbert (peppy)独立制作并运行. 游戏的方法简单,就是 1. 圈圈(Circle)&#xff1a;圈圈(Circle) 50。没打中显示X,并减少生命值。圈中序号的最后一个的300、100会显示为激300、喝100。2.滑条(Slider) : 在开始端点击按住不…

Android programming on Mac 之安装Eclipse

1.安装包在此链接下载&#xff1a; http://developer.android.com/sdk/index.html google GoAgent翻墙不好用&#xff0c;更新了host文件也不行&#xff0c;整了半天&#xff0c;还是一怒之下续签了vpn账号。早知如此&#xff0c;何必折腾。~~~~(>_<)~~~~ 更新文件时…

c++关于虚表的一些笔记

文章目录1、虚函数表指针2、多态构成的条件3、重载、重写、重定义 三者区别4、继承与虚函数5、单继承中的虚函数表无虚函数覆盖有虚函数覆盖6、单继承中的虚函数表无虚函数覆盖有虚函数覆盖参考看《深度探索c对象模型》的时候对虚表有了点疑惑&#xff0c;正好网上有些文章解除…

C语言技巧:把单一元素的数组放在末尾,struct可以拥有可变大小的数组

《C 对象模型》第19页有这样一句话 C程序员的巧计有时候却成为c程序员的陷阱。例如把单一元素的数组放在一个struct的末尾&#xff0c;于是每个struct objects可以拥有可变数组的数组&#xff1a; struct mumble {/* stuff */char pc[1]; };//从文件或标准输入装置中取得一个…

探讨C++ 变量生命周期、栈分配方式、类内存布局、Debug和Release程序的区别(二)...

看此文&#xff0c;务必需要先了解本文讨论的背景&#xff0c;不多说&#xff0c;给出链接&#xff1a; 探讨C 变量生命周期、栈分配方式、类内存布局、Debug和Release程序的区别&#xff08;一&#xff09; 本文会以此问题作为讨论的实例&#xff0c;来具体讨论以下四个问题&a…

后台系统可扩展性学习笔记(一)概要

文章目录系统大致架构可扩展性负载均衡器与会话保持引入冗余增强系统可用性缓存减轻数据库压力异步处理参考系统大致架构 当一个用户请求从客户端出发&#xff0c;经过网络传输&#xff0c;达到 Web 服务层&#xff0c;接着进入应用层&#xff0c;最后抵达数据层&#xff0c;它…

后台系统可扩展性学习笔记(二)权衡取舍

文章目录性能与可扩展性延迟与吞吐量可用性与一致性一致性模式可用性模式可用性衡量参考系统设计中也面临许多权衡取舍&#xff1a;性能与可扩展性延迟与吞吐量可用性与一致性 性能与可扩展性 可扩展&#xff0c;意味着服务能以加资源的方式成比例地提升性能&#xff0c;性能…

后台系统可扩展性学习笔记(三)DNS机制原理

文章目录DNS概念梳理域名基本概念资源记录基本概念路由策略DNS 域空间结构实现原理复制机制查询机制缓存机制参考DNS概念梳理 DNS&#xff08;Domain Name System&#xff09;相当于互联网的通讯录&#xff0c;能够把域名翻译成 IP 地址。 从技术角度来讲&#xff0c;DNS 是个…