首页 > 其他分享 >Mars的学习笔记(更新中)

Mars的学习笔记(更新中)

时间:2024-05-22 12:56:36浏览次数:22  
标签:head int ne 更新 fa maxn 笔记 Mars dis

前言

这篇文章主要用来总结我之前所学的算法,以更好地复习。希望大家在看时能理解我所说的话(包括未来的我)

算法大典

KMP

KMP是一个强大的字符串搜索算法,可以在线性的复杂度下将所需要的子串位置精确的找出。它的最大特点就是搜索是不会回溯。

朴素思想

给出两个字符串

abababab
bab

要求找出第二个字串在第一个字串第一次出现的位置。朴素思想就是直接爆搜,从第一个字符开始搜索,没搜索到就重新跳到第二个字符。代码如下:

#include<bits/stdc++.h>
using namespace std;
string a,b;
int main()
{
	cin>>a>>b;
	for(int i=0;i<a.size();i++)
	{
		for(int j=i;j<b.size();j++)
		{
			if(a[j]!=b[j-i])break;
			else
			{
				cout<<i+1;
				return 0;
			}
		}
	}
	
	return 0;
}

浅显易懂,但代价是复杂度来到了 $O(nm)$ 但凡数据大点就寄了。

KMP思想

考虑优化。

不难发现,在搜索时我们完全可以跳过一些已知的字符,从而继续匹配,这样子既不用回溯浪费时间,也不用一个个的匹配字符

拿两个字符串举例:

abbcabba
abba

当我们匹配到 $c$ 和 $a$ 时,发现匹配失败,因为失败之前的主串部分一定是跟子串部分匹配的,且对于子串来说,失败之前的任意一部分属于子串(模式串)的这段子串(子子串)的后缀,所以我们重新匹配的开始一定是子串的某部分前缀。如果想要将模式串移到最大有效的匹配位置,那么这个位置一定是这段前缀等于后缀的部分。至此问题也转化成了求模式串的前后缀。

前后缀的求法十分巧妙,大家可以先看以下代码:

void get_next()
{
	int t1=0,t2=-1;
	next1[0]=-1;
	while(t1<len2)
	{
		if(t2==-1 || s2[t2]==s2[t1])
			next1[++t1]=++t2;
		else t2=next1[t2];
	}
}

我们可以将t1想象成一直往前开的火车,t2为一节车厢,当目前的前串(t2)与后串(t1)相等时,t2就能向前一步,反之就要倒退。 $else$ 处十分反人类,建议反复斟酌理解透彻。

当你理解透了以上代码,你就很容易理解匹配代码了:

void KMP()
{
	int t1=0,t2=0;
	while(t1<len1)
	{
		if(t2==-1 || s1[t1]==s2[t2])
			t1++,t2++;
		else t2=next1[t2];
		if(t2==len2)printf("%d\n",t1-t2+1),t2=next1[t2];
	}
}

原理是相似的。依旧建议反复观看理解。

贴上完整代码:

#include<bits/stdc++.h>
using namespace std;
int n,k,len1,len2;
int next1[1000005];
char s1[1000005];
char s2[1000005];
void get_next()
{
	int t1=0,t2=-1;
	next1[0]=-1;
	while(t1<len2)
	{
		if(t2==-1 || s2[t2]==s2[t1])
			next1[++t1]=++t2;
		else t2=next1[t2];
	}
}
void KMP()
{
	int t1=0,t2=0;
	while(t1<len1)
	{
		if(t2==-1 || s1[t1]==s2[t2])
			t1++,t2++;
		else t2=next1[t2];
		if(t2==len2)printf("%d\n",t1-t2+1),t2=next1[t2];
	}
}
int main()
{ 
    scanf("%s",s1);
    scanf("%s",s2);
    len1=strlen(s1);
    len2=strlen(s2);
    get_next();
    KMP();
    for(int i=1;i<=len2;++i) 
        printf("%d ",next1[i]);
        
    return 0;
}

例题

KMP模版

最小生成树

最小生成树为边权和最小的生成树,只存在于联通图中,可以解决联通所有点的最小边权和问题。

思想

对于寻找联通所有点的最小边权和,最朴素的思想就是遍历全部图来寻找最小的边权,但操作难度大,坑还特别多,于是最小生成树应运而生。本文主要介绍 Prim 算法和 Kruskal 算法。

Prim算法

Prim 算法的思想就是选取任意点作为根,逐渐寻找最近的点不断加进树中,再将边权取最小值。

借鉴一下 oi-wiki 的图:

思想十分简单但有效,代码如下:

#include<bits/stdc++.h>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=200005;
struct data
{
	int dis,x;
	bool friend operator<(data a,data b)
	{
		return a.dis>b.dis;
	}
};
int ne[maxn<<1],x[maxn<<1],w[maxn<<1],head[maxn],idx;
int n,m,u1,v1,w1,ans,cnt;
bool vis[maxn];
void add(int a,int b,int c)
{
	x[++idx]=b;
	ne[idx]=head[a];
	head[a]=idx;
	w[idx]=c;
}
void Prim()
{
	priority_queue<data>q;
	q.push((data){0,1});
	while(!q.empty())
	{
		data u=q.top();
		ans+=u.dis;
		cnt++;
		vis[u.x]=true;
		q.pop();
		for(int i=head[u.x];i!=-1;i=ne[i])
		{
			if(!vis[x[i]])q.push((data){w[i],x[i]});
		}
		while(!q.empty() && vis[q.top().x])
			q.pop();
	}
}
int main()
{
	memset(head,-1,sizeof(head));
	cin>>n>>m;
	for(int i=1;i<=m;i++)
	{
		cin>>u1>>v1>>w1;
		add(u1,v1,w1);
		add(v1,u1,w1);
	}
	Prim();
	if(cnt<n)
	{
		cout<<"orz";
		return 0;
	}
	else cout<<ans;
	
	return 0;
}

可以看到,代码十分的繁琐,实际操作十分困难。

Kruskal算法

kruskal 算法因为其简短的代码和简洁的思想被广泛使用。与 prim 不同的是, kruskal 的思想是加边,有点贪心的味道(就是贪心)。

借鉴一下 oi-wiki 的图 again :

代码基于并查集,因为加边使得点与点之间的关系可以用“祖先”相称。

#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
int n,m,sum,cnt;
int fa[maxn];
int read()
{
	int date=0,w=1;
	char a=getchar();
	while(a<'0' || a>'9')
	{
		if(a=='-')w=-1;
		a=getchar();
	}
	while(a>='0' && a<='9')
	{
		date=date*10+(a-'0');
		a=getchar();
	}
	return date*w;
}
void init(int n)
{
	for(int i=1;i<=n;i++)fa[i]=i;
}
int f(int x)
{
	return fa[x]=(fa[x]==x) ? fa[x] : f(fa[x]);
}
bool u(int x,int y)
{
	x=f(x),y=f(y);
	if(x==y)return false;
	fa[x]=y;
	return true;
}
struct edge
{
	int u,v,w;
	bool operator<(edge a){return w<a.w;}
}e[maxn];
bool cmp(edge x,edge y)
{
	return x.w<y.w;
}
signed main()
{
	n=read(),m=read();
	init(n);
	for(int i=1;i<=m;i++)
		e[i].u=read(),e[i].v=read(),e[i].w=read();
	sort(e+1,e+m+1,cmp);
	for(int i=1;i<=m;i++)
	{
		if(u(e[i].u,e[i].v))
		{
			sum+=e[i].w;
			cnt++;
		}
	}
	if(cnt!=n-1)
	{
		cout<<"orz";
		return 0;
	}
	cout<<sum;
	return 0;
}

总的来说,最小生成树比较好理解,学过图论的理解起来难度不大。

例题

Trie

字典树,一个好用但有点冷门的东西,值得一学,因为 ACAM 等高级结构都与它有关。

借鉴一下 oi-wiki 的图

思想

如图,可以看到这棵树的边代表了一个字母,例如,1 -> 2 -> 6 -> 7就是 aba 字符串。

trie 的结构十分好懂,做起来也不难,用 $f(u,c)$ 代表节点 $u$ 代表的字符串后添加一个字符 $c$ 形成的节点。

放一个模板。

struct trie
{
	int nex[3000005][65],cnt;
	int exist[3000005];
	void insert(string s,int l)//插入字符串
	{
		int p=0;
		for(int i=0;i<l;i++)
		{
			int c=getnum(s[i]);
			if(!nex[p][c])nex[p][c]=++cnt;
			p=nex[p][c];
			exist[p]++;
		}
	}
	int find(string s,int l)//查找字符串
	{
		int p=0;
		for(int i=0;i<l;i++)
		{
			int c=getnum(s[i]);
			if(!nex[p][c])return false;
			p=nex[p][c];
		}
		return exist[p];
	}
};

字典树可以查找字符串(这不是废话吗)。

例题

十分基础的查找字符串。首先将字符串存入 trie 中,当查找时判断该字符串是否出现过,结束(有点过于简短了)但思想就是这么简单。

进阶练习

跟例题没什么大出入,改一点就能过。

代码如下:

#include<bits/stdc++.h>
using namespace std;
int m,n,len;
struct Trie
{
	int nex[500005][5],cnt;
	int exist[500005],aaa[500005];
	void insert(bool s[])
	{
		int p=0;
		for(int i=1;i<=len;i++)
		{
			int c=s[i];
			if(nex[p][c]==-1)nex[p][c]=++cnt;
			p=nex[p][c];
			exist[p]++;
		}
		aaa[p]++;
	}
	int find(bool s[])
	{
		int p=0,maxx=0;
		for(int i=1;i<=len;i++)
		{
			int c=s[i];
			if(nex[p][c]==-1)return maxx;
			p=nex[p][c];
			maxx+=aaa[p];
		}
		return maxx-aaa[p]+exist[p];
	}
};
Trie a;
bool b[10005];
int main()
{
    scanf("%d%d",&m,&n);
    memset(a.nex,-1,sizeof(a.nex));
    for(int i=1;i<=m;i++)
    {
    	scanf("%d",&len);
    	for(int j=1;j<=len;j++)cin>>b[j]; 
    	a.insert(b);
    }
    for(int i=1;i<=n;i++)
    {
    	scanf("%d",&len);
    	for(int j=1;j<=len;j++)cin>>b[j];
    	cout<<a.find(b)<<"\n";
    }
    return 0;
}

字典树还能用来弄 01-trie ,笔者还没学,等我进省队了再说

LCA(最近公共祖先)

树论大佬必备常识,超级有用!!!(就是因为没学这个 GDKOI 痛失 T1 55555)

接下来开始讲解。

思想

朴素思想

想找到两个树上的点的最近公共祖先,首先应该让深度大的那个点先跳到相同的深度,再同时往上跳,直到找到 LCA 为止。

显然,这样暴力跳的单次查询时间复杂度为 $O(n)$ ,加上 dfs 整棵树预处理的 $O(n)$ ,时间复杂度就达到了 $O(n^2)$ ,如果有多个查询,就完蛋了。

于是不同的方法应运而生。本文主要介绍倍增求 LCA 。

倍增求 LCA

倍增求 LCA 的单次查询复杂度为 $O(\log n)$ ,预处理的时间复杂度为 $O(n \log n)$ ,比暴力快了很多。

我们用游标来让点进行快速移动。$fa(x,i)$ 表示点 $x$ 的 $2^i$ 个祖先。该数组可以预处理出来。

借一下 oi-wiki 的一段话。

在调整游标的第一阶段中,我们要将 u,v 两点跳转到同一深度。我们可以计算出 u,v 两点的深度之差,设其为 y。通过将 y 进行二进制拆分,我们将 y 次游标跳转优化为「y 的二进制表示所含 1 的个数」次游标跳转。 在第二阶段中,我们从最大的 i 开始循环尝试,一直尝试到 0(包括 0),如果 $\text{fa}{u,i}\not=\text{fa}$ ,则 $u\gets\text{fa}{u,i}$ , $v\gets\text{fa}$,那么最后的 LCA 为 $\text{fa}_{u,0}$ 。

金句啊! oi-wiki 我爱你......

存树的话本人喜欢用链式前向星,十分好用建议一试。

练手题

接下来给 2 道题巩固知识。非常好的题,使我大脑旋转。

例题

LCA 板子题。只要将板子打对就行。

代码如下。

#include<bits/stdc++.h>
using namespace std;
const int maxn=500005;
int dep[maxn<<1],fa[maxn<<1][30];
int n,q,root;
int ee,head[maxn<<1],ne[maxn<<1],to[maxn<<1];
int getlca(int x,int y)
{
	if(dep[x]<dep[y])
		swap(x,y);
	while(dep[x]>dep[y])
		x=fa[x][(int)log2(dep[x]-dep[y])];//简洁,建议这么写。
	if(x==y)
		return y;
	for(int j=20;j>=0;j--)//跳到LCA的下面一层
		if(fa[x][j]!=fa[y][j])
			x=fa[x][j],y=fa[y][j];
	return fa[x][0];//返回LCA 
}
void add(int a,int b)
{
	ne[++ee]=head[a];
	to[ee]=b;
	head[a]=ee;
}
void dfs(int x,int pre)//x为当前节点,pre为父亲节点
{
	dep[x]=dep[pre]+1;
	fa[x][0]=pre;
	for(int i=head[x];i;i=ne[i])
		if(to[i]!=pre)
			dfs(to[i],x);
}
signed main()
{
	scanf("%d%d%d",&n,&q,&root);
	for(int i=1,x,y;i<n && cin>>x>>y;i++)
		add(x,y),add(y,x);
	dfs(root,0);//预处理
	for(int j=1;j<=20;j++)
		for(int i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];//预处理
	while(q--)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		printf("%d\n",getlca(x,y));
	}
	return 0;
}

给一道进阶题(就是我痛失的 T1......)。

捉迷藏 pro max 版

虽然是比较版的 LCA ,但卡常能将人卡送走,错了无数次才卡过......

代码奉上:

#include<bits/stdc++.h>
using namespace std;
const int maxn=1000000;
int n,q;
int head[maxn<<1],ne[maxn<<1],to[maxn<<1],ee;
int fa[maxn][30],dep[maxn];
inline int read()
{
	int date=0,w=1;
	char y=getchar();
	while(y<'0' || y>'9')
	{
		if(y=='-')w=-1;
		y=getchar();
	}
	while(y>='0' && y<='9')
	{
		date=date*10+(y-'0');
		y=getchar();
	}
	return date*w;
}
inline int getlca(int x,int y)
{
	if(dep[x]<dep[y])
		swap(x,y);
	while(dep[x]>dep[y])
		x=fa[x][(int)log2(dep[x]-dep[y])];
	if(x==y)
		return y;
	for(int j=20;j>=0;j--)
		if(fa[x][j]!=fa[y][j])
			x=fa[x][j],y=fa[y][j];
	return fa[x][0];
}
inline void add(int a,int b)
{
	ne[++ee]=head[a];
	to[ee]=b;
	head[a]=ee;
}
inline void dfs(int x,int pre)
{
	dep[x]=dep[pre]+1;
	fa[x][0]=pre;
	for(int i=head[x];i;i=ne[i])
		if(to[i]!=pre)
			dfs(to[i],x);
}
inline void c()
{
	for(int i=0;i<=n;i++)
		head[i]=to[i]=ne[i]=dep[i]=0;
	for(int i=n+1;i<=2*n;i++)
		to[i]=ne[i]=0;
	ee=0;
}
signed main()
{
	int A=read(),T=read();
	while(T--)
	{
		n=read(),q=read();
		for(int i=1,x,y;i<n;i++)
			x=read(),y=read(),add(x,y),add(y,x);
		dfs(1,0);
		for(int j=1;j<=20;j++)
			for(int i=1;i<=n;i++)
				fa[i][j]=fa[fa[i][j-1]][j-1];
		for(int i=1;i<=q;i++)
		{
			int x=read(),y=read(),dx=read(),dy=read();
			int ans=getlca(x,y),lo;
			lo=dep[x]-2*dep[ans]+dep[y];
			if(lo<=dx)printf("Zayin\n");
			else if(dx>dy)printf("Zayin\n");
			else if(dx==dy)printf("Draw\n");
			else if(dx<dy)printf("Ziyin\n");
		}
		c();
	}
	return 0;
}

真是爱死这道题了,卡常把我送走了......

LCA 是很有用的,建议巩固好知识。

dijkstra

众所周知,Dijkstra算法是一个十分有效且常用的算法。既然说了:

  • 有效且常用

那我们就有学习的必要了呀!

话不多说,开始讲解。

概念

1.是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。 $Dijkstra$ 算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

2.$Dijkstra$ 用来解决边权全为正的单源最短路问题, $Dijkstra$ 算法又分为朴素 $Dijkstra$ 算法和堆优化的 $Dijkstra$ 算法。朴素版的Dijkstra算法的时间复杂度是 $O(n²)$ ,适合于稠密图,堆优化版的 $Dijkstra$ 算法的时间复杂度是 $O(mlogn)$,适合于稀疏图。

代码讲解

链式前向星


#define MOD 10000000007
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2000005;
int n,m,s;
int idx=1,e[maxn],w[maxn],head[maxn],ne[maxn];
int dis[maxn],vis[maxn];

MOD和INF不做过多解释,重点看e,w,head和ne数组。


void add(int a,int b,int c)
{
	e[idx]=b;
	w[idx]=c;
	ne[idx]=head[a];
	head[a]=idx++;
}

这是一个简单的链式前向星,虽然有些奇怪的东西

a是该点的位置,b是与该点相连的一点的位置,c则是边全
权。我们可以看到,e数组存储了一个点到另一个点的的终点,w存储了这两个点中边的边权,ne和head数组则是普通的链式前向星啦!

结构体


struct Node
{
	int dis,x;
	bool operator<(Node p)const{return dis>p.dis;}
	Node(int dis,int x):dis(dis),x(x){}
};

我们使用重载运算符 $operator$ 重新定义 $<$ 符号来对边权进行排序,方便我们接下来的操作。下面的一行可有可无,主要是装

Dijkstra 主体


void dijstra()
{
   memset(dis,INF,sizeof(dis));
   priority_queue<Node>q;
   dis[s]=0;
   Node u(dis[s],s);
   q.push(u);
   while(!q.empty())
   {
		Node u=q.top();
		q.pop();
		if(vis[u.x])continue;
		vis[u.x]=1;
		for(int i=head[u.x];i!=-1;i=ne[i])
		{
			if(dis[e[i]]>dis[u.x]+w[i])
			{
				dis[e[i]]=dis[u.x]+w[i];
				Node v(dis[e[i]],e[i]);
				q.push(v);
			}
		}
	}
}

首先定义大根堆 $priority__queue$ ,方便我们接下来的操作。其次,我们从 $s$ 点出发,那距离 $s$ 点的最短距离肯定是0,这就是 $dis[s]=0$ 的原因。

跟图有关,那我们就得使出万能且高效的BFS。

如你所见,里面有一个BFS遍历。

while循环的前四行为基操,不做过多讲述,我们来看for循环。


for(int i=head[u.x],i!=-1;i=ne[i])//从头开始,循环到下一个点

从第x个点的链下标出发,向下一个点,也就是 $ne[i]$ 前进,但由于我们标记了每一个点初始值为-1,所以还得判断一下。


if(dis[e[i]]>dis[u.x]+w[i])
{
    dis[e[i]]=dis[u.x]+w[i];
    Node v(dis[e[i]],e[i]);
    q.push(v);
}

这里我们做出判断,如果新路径的边权总和小于原路径边权总和,就改变最佳路径。

完整代码

这里附上完整代码:


#include<bits/stdc++.h>
#define MOD 10000000007
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=2000005;
int n,m,s;
int idx=1,e[maxn],w[maxn],head[maxn],ne[maxn];
int dis[maxn],vis[maxn];
struct Node
{
	int dis,x;
	bool operator<(Node p)const{return dis>p.dis;}
	Node(int dis,int x):dis(dis),x(x){}
};

inline int read()
{
	int date=0,w=1;
	char c;
	c=getchar();
	while(c<'0' || c>'9')
	{
		if(c=='-')w=-1;
		c=getchar();
	}
	while(c>='0' && c<='9')
	{
		date=date*10+(c-'0');
		c=getchar();
	}
	return date*w;
}

void add(int a,int b,int c)
{
	e[idx]=b;
	w[idx]=c;
	ne[idx]=head[a];
	head[a]=idx++;
}

void dijstra()
{
	memset(dis,INF,sizeof(dis));
	priority_queue<Node>q;
	dis[s]=0;
	Node u(dis[s],s);
	q.push(u);
	while(!q.empty())
	{
		Node u=q.top();
		q.pop();
		if(vis[u.x])continue;
		vis[u.x]=1;
		for(int i=head[u.x];i!=-1;i=ne[i])
		{
			if(dis[e[i]]>dis[u.x]+w[i])
			{
				dis[e[i]]=dis[u.x]+w[i];
				Node v(dis[e[i]],e[i]);
				q.push(v);
			}
		}
	}
}
signed main()
{
	memset(head,-1,sizeof(head));
	n=read(),m=read(),s=read();
	for(int i=1;i<=m;i++)
	{
		int a=read(),b=read(),c=read();
		add(a,b,c);
	}
	dijstra();
	for(int i=1;i<=n;i++)
		cout<<dis[i]<<" ";
	return 0;
}

例题

线段树

作为一个树形数据结构,它的时间复杂度是毋庸置疑的快,达到了$O(nlog\ n)$的级别。该数据结构可以用来解决区间求和,区间求最值,区间求积等等区间类问题。

思想

标签:head,int,ne,更新,fa,maxn,笔记,Mars,dis
From: https://www.cnblogs.com/MarsNotFound/p/18206037

相关文章

  • 《梦断代码》阅读笔记3
        对这本书的阅读终于要结束了,“梦断代码”:代码阻断了梦的实现吗?一直以为,计算机是万能的,自己想的都可以通过代码实现。在接触代码以后的这段时间里,我的想法改变了。代码可以实现自己的想法,但是怎么实现却要看自己了,算法自己思考,计算机只负责运行,运行通过就说明算法通过......
  • Java学习笔记(一)
    Java学习笔记(一)字节计算机存储的最小计量单位:byteB存储单位换算:8bit=1B(byte)1024B=1KB1024KB=1MB1024MB=1GJava环境jvm与跨平台:jvm——运行Java程序的假想计算机跨平台——Java代码能在不同操作系统上运行两者关系——想要实现跨平台,需要安装对应版本......
  • Asp-Net-Core开发笔记:使用原生的接口限流功能
    前言之前介绍过使用AspNetCoreRateLimit组件来实现接口限流从.Net7开始,AspNetCore开始内置限流组件,当时我们的项目还在.Net6所以只能用第三方的现在都升级到.Net8了,当然是得来试试这个原生组件体验后:配置使用都比较简单,不过功能也没有AspNetCoreRateLimit那么灵活......
  • ZooKeeper论文笔记.18205343
    概要是什么:ZooKeeper是一个分布式系统的基础构件(协调内核),分布式应用(如RocketMQ)可以使用ZooKeeper来处理分布式系统协同的各个方面,比如可以使用它来实现leader选举、分布式锁等等,分布式应用可以使用它暴露的API实现各种类型的协同原语(考虑Java中的AQS)。它让分布式应用的设计者无需......
  • 《人月神话》阅读笔记
    终于有幸拜读了《人月神话》这部业内经典著作。整体来说,本书的主线——人月神话、没有银弹在现今的软件工程管理领域依然属于有效的基础理论。不过有些东西确实过时了,比方说文档的管理,现在已经有了svn或者在线文档。提到调试的复杂性,现在的集成环境把调试变得非常容易。读完之后才......
  • 《人月神话读书笔记二》
    贯彻执行即使是大型的设计团队,设计结果也必须由一个或两个人来完成,以确保这些决定是一致的。允许体系结构师对实现人员的询问做出电话应答解释是非常重要的,并且必须进行日志记录和整理发布。对于存有疑问的实现人员,应鼓励他们打电话询问相应的结构师,而不是一边自行猜测一边工作......
  • 笔记本温度常识
         ......
  • 问microdnf update命令安装新包,而不仅仅是更新现有的包。
    我的Dockerfile使用基本映像registry.access.redhat.com/ubi8/ubi-minimal,它有microdnf包管理器。当我在docker文件中包含以下代码片段以获得现有包的最新更新时,代码语言:javascript复制RUNtrue\&&microdnfcleanall\&&microdnfupdate--nodocs\......
  • Redis 笔记
    1.什么是redisredis是高性能缓存数据库,基于内存运行key-value存储系统,提供分布式锁,支持事务,持久化,多种分布式集群方案。使用单线程的多路IO服用模型。redis支持数据类型String,Hash,List,Set,SortedSet等String存储字符串,Hash存储key-value键值对集合List字符串链表......
  • DDD 笔记
    1.简单讲讲DDD,和DDD哪些优势领域驱动设计。就是通过领域来指导软件设计,是一种十分抽象的软件设计思想,它主要分为战略设计和战术设计战略方面,通过事件风暴进行领域模型的划分,划分出核心域,子域,支撑域,定义通用语言,划分出界限上下文。在战术设计方面,ddd将架构分层,“松耦合,高内聚”......