C++笔记5

目录

图的基础问题

图上的环

无向图的环

DAG图与拓扑排序

拓扑排序

卡恩算法(BFS)

算法描述

        统计图中每个点的入度(即连向该点的边数)

拓扑排序的DFS算法

算法描述

拓扑排序的DFS的实现简单,从一个入度为0的节点开始做DFS,每访问到一个节点,标注为已访问,下次重复访问到时直接跳过。当所有子节点都访问过后,将当前节点加入列表。这样最终将会得到一个叶子节点在前,根结点在最后的节点列表。将该列表反转,得到的就是拓扑排序好的节点。

最小生成树

最小生成树定义

Kruskal算法

最短路径算法

SPFA算法

算法描述

最短路径问题

单源最短路问题

Dijkstra算法

算法描述

Dijkstra优化

算法描述

权值为1的最短路

Manacher算法

最长回文子串问题

暴力算法

算法描述

Manacher 算法是一种可以在O(n)的时间复杂度求出一个长为 n 的字符串的最长回文子串的算法,它思路简单、代码简单,但功效却十分强大。

算法证明

DAG图与拓扑排序

拓扑排序

卡恩算法(BFS)

算法描述

统计图中每个点的入度(即连向该点的边数)

拓扑排序的DFS算法

算法描述

有向无环图(DAG)

DAG图的判定


图的基础问题

图上的环

环也称回路,是图论里的概念。一个环是一个边的排列,并且沿着这个排列走一次可以走回起点。有向图和无向图中均存在环。

在无向图中,不存在环路的图是一棵树。

在有向图中,不存在环路的图是一张有向无环图。

无向图的环

判断一个无向图中是否存在环的方法很简单:

从任意点开始,对图做DFS。遍历点的过程中,将点标注为已访问。

如果遍历到某个点时,发现其已经被标注为已访问,则表示图中存在环。

bool Dfs(int u){if(visted[u])return true;   visted[u] = true;for(v in 与点u有连边的点)if(Dfs(v))return true;return false;
}

DAG图与拓扑排序

拓扑排序

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序:每个顶点出现且只出现一次;

若A在序列中排在B的前面,则在图中不存在从B到A的路径。

从拓扑排序的概念我们就可以看出,只有有向无环图才存在拓扑排序。因此,如果有算法可以帮我们判断一张图是否存在拓扑排序,我们就能判断这张图是否为有向图。事实上,我们不仅能很快的判断一张图是否存在拓扑排序,还能顺便求出它的拓扑排序。

一个图G的拓扑排序大多情况下是不唯一的。

卡恩算法(BFS)

算法描述
        统计图中每个点的入度(即连向该点的边数)

将入度为0的点放入队列

每次从队列中取出一个点u,遍历从这个点出发的所有出边 (u−>v) ,删除u−>v这条边(代码实现上仅表现为让v的入度−1),若v的入度变为0,则将v放入队列

重复(3)直到队列为空,若所有点都进出过队列,则点的出队顺序即为这张图的拓扑序,否则说明该图不存在拓扑排序(存在有向环)。

using namespace std;
const int N = 2e5 + 100;
const int M = 4e5 + 100;
int head[N], Next[M], ver[M], tot;
int deg[N];
void add(int x, int y) {ver[++tot] = y;Next[tot] = head[x];head[x] = tot;
}
queue<int>qc;
int topsort(int n) {int cnt = 0;while (qc.size())qc.pop();for (int i = 1; i <= n; i++) {if (deg[i] == 0) {qc.push(i);}    }while (qc.size()) {int x = qc.front();cnt++;qc.pop();for (int i = head[x]; i; i = Next[i]) {int y = ver[i];if (--deg[y] == 0)qc.push(y);}    }return cnt == n;
}
int main() {int n, m, x, y;cin >> n >> m;for (int i = 1; i <= m; i++) {cin >> x >> y;add(x, y);deg[y]++;}if (topsort(n))cout << "Yes" << endl;else cout << "No" << endl;
}

拓扑排序的DFS算法

算法描述
拓扑排序的DFS的实现简单,从一个入度为0的节点开始做DFS,每访问到一个节点,标注为已访问,下次重复访问到时直接跳过。当所有子节点都访问过后,将当前节点加入列表。这样最终将会得到一个叶子节点在前,根结点在最后的节点列表。将该列表反转,得到的就是拓扑排序好的节点。

最小生成树

最小生成树定义

G是一张无向图,边集为E(第i条边的边权为wi),点集为V,在E中选出|V|−1条边,恰好构成一颗包含V中所有点的树T,就称T是G的一颗生成树。最小生成树即为这些生成树中边权和最小的树。

解决最小生成树问题,在信息学竞赛中我们常使用Kruskal算法或Prim算法,下面我们将逐个介绍这两个算法。

Kruskal算法

Kruskal算法是一种用来查找最小生成树的算法,由Joseph Kruskal在1956年发表。Kruskal算法利用了贪心的思想。

考虑构造最小生成树T,开始时T中只有n个点,没有加任何边,按边权从小到大的顺序枚举m 条边,如果加入当前边不会使得我们的T中出现环,就把这条边加入T中,最后如果T中加入了n−1条边,则T为我们要求的最小生成树,否则生成树不存在。

那么,在什么时候加入一条边(u,v会使得T中出现环呢?

显然,如果原本就存在一条u和v之间的路径(即u,v连通),那么加入(u,v)这条边后,就会出现环。所以我们只需要判断一下u,v当前是否连通,就能知道加入这条边后T中是否会出现环了。

而不断加边并判断某两个点是否连通,在前面的章节中我们学到,用并查集可以判断无向图中两点是否连通。

最短路径算法

SPFA算法

除了Dijkstra算法外,我们还有另外一种在大多情况下都可以高效解决单源最短路问题的算法——SPFA 算法。SPFA 算法是 Bellman−Ford 算法 的队列优化算法的别称,通常用于求含负权边的单源最短路径,以及判负权环。SPFA最坏情况下复杂度和朴素Bellman−Ford相同,为O(MN) 。SPFA 可以处理有负权(边权大小为负数)的情况,同时还可以处理图中的负环(权值为负数的环路)。SPFA算法在大多数情况下表现良好,与Dijkstra 表现不相上下,但在网格图中SPFA算法的复杂度会退化到O(nm)。

算法描述

同样用dis[i]表示源点s到i的最短路径长度,初始时dis[s]=0,其余点dis值为无穷大

将s放入队列每次取出队首节点u,遍历从u出发的所有边u−v(假设边权为c),尝试用dis[u]+c更新dis[v],若dis[v]>dis[u]+c,则令dis[v]=dis[u]+c,并把 v 放入队列

重复(3)直到队列为空

算法实现
#define inf 1000000000
#define N 200020
using namespace std;
queue<int>Q;
int dis[N];
bool vis[N];
int n, m, s;
int head[N], pos;
struct edge { int to, next, c; }e[N];
void add(int a, int b, int c){pos++; e[pos].to = b, e[pos].c = c, e[pos].next = head[a], head[a] = pos;
}
void spfa(){for (int i = 1; i <= n; i++)dis[i] = inf, vis[i] = 0;dis[s] = 0, vis[s] = 1, Q.push(s);while (!Q.empty())   {int u = Q.front(); Q.pop(); vis[u] = 0;for (int i = head[u]; i; i = e[i].next){int v = e[i].to;if (dis[v]>dis[u] + e[i].c) {dis[v] = dis[u] + e[i].c;if (!vis[v])vis[v] = 1, Q.push(v);}}}}
int main(){cin >> n >> m >> s;for (int i = 1; i <= m; i++)    {int x, y, c;cin >> x >> y >> c;add(x, y, c);add(y, x, c);}spfa();}

最短路径问题

单源最短路问题

给出一张n节点,m条边的图(可以是有向图也可以是无向图),第i条边有边权wi,求出给定点s到每个点的最短路的长度。

Dijkstra算法

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家Dijkstra于1959 年提出的。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

Dijkstra的朴素实现的复杂度为O(n2),用堆进行优化后的复杂度为O((n+m)×log(n+m)) 。

算法描述

用dis[u]表示从源点s到u当前找到的最短路径长度

如果存在一条边u−>v,边权为c,那么dis[v]=min(dis[v],dis[u]+c),即可以先从s走到u,再从u通过这条边走到v。

因此,对于最终的dis数组,我们有不等式关系dis[v]<=dis[u]+c(存在边 u−>v ,边权为c)

那么,如何利用这种关系求出最后的dis数组呢

Dijkstra的解决方案是: 把点分成两个集合A,B,集合B中是尚未确定最终dis值的点,集合A中是已经确定dis值的点,初始时所有点都在 B 中,且除了s的dis值为0外,其余点的dis值为无穷大(取尽量大的值作为无穷大)

每次从集合B中取出dis值最小的点u(u为集合B中到s最近的点),遍历所有从u出发的边(u−>v),假设边权为c,用dis[u]+c更新 dis[v] ,即dis[v]=min(dis[v],dis[u]+c)

把u点放入A集合

循环(2)(3)直到B集合为空

Dijkstra的普通实现复杂度为O(n2) 。

特别提醒:Dijkstra不能处理有负权(边权大小为负数)的情况。

#include<bits/stdc++.h>
#define inf 1000000000
#define N 200200
using namespace std;
int head[N], pos;
struct edge { int to, next, c; } e[N];
void add(int a, int b, int c) {pos++; e[pos].c = c, e[pos].to = b, e[pos].next = head[a], head[a] = pos;
}
int n, m, s;
bool vis[N];
int dis[N];
void dijkstra(){for (int i = 1; i <= n; i++)dis[i] = inf;dis[s] = 0;int cnt = 0;while (cnt < n)    {int mn = inf, u = 0;for (int i = 1; i <= n; i++)if (!vis[i] && dis[i] < mn)mn = dis[i], u = i;vis[u] = 1;//vis[u]=1意味着把u放入了A集合for (int i = head[u]; i; i = e[i].next){int v = e[i].to;if (!vis[v] && dis[v] > dis[u] + e[i].c)dis[v] = dis[u] + e[i].c;}cnt++;}
}
int main(){cin >> n >> m >> s;for (int i = 1; i <= m; i++)    {int x, y, c;cin >> x >> y >> c;add(x, y, c);add(y, x, c);}dijkstra();for (int i = 1; i <= n; i++)cout << dis[i] << " ";

Dijkstra优化

分析 Dijkstra 的算法流程,第 (2) 步中,找到 B 集合 dis 值最小点这一操作,我们在上述代码的实现中,是通过枚举所有 B 集合中的点来查找的。每次进行这一操作的复杂度为 O(n) ,但我们知道,每次取一些数据的最小/最大值可以用优先队列/堆在单次 O(logn) 的复杂度内来实现。

这里我们只介绍用优先队列实现 Dijkstra 的方法,优先队列本质上也是堆的一种,因此同学们也可以自行尝试用自己写的堆来代替优先队列优化 Dijkstra 。

算法描述

把源点 s 的 dis 值 dis[s] 赋值为 0 ,其余点的 dis 值赋值为无穷大,并把 pair(s,ds) 放入优先队列。

每次从优先队列中取出 dis 值最小的 (u,du) 。

如果 du!=dis[u],说明现在的 dis[u] 相较于放入 (u,du) 时的 dis[u] 已经变小,那么显然用 (u,dis[u]) 更新其他节点更优,因此没必要再用 (u,du) 更新其他节点,于是重复进行第 (2) 步。

如果 du==dis[u],遍历 u 能到达的所有节点 v,假设 u−>v 的边权为 c,比较 dis[v] 和 dis[u]+c 的大小,如果 dis[v]>dis[u]+c ,令 dis[v]=dis[u]+c,并把 (v,dis[v]) 放入优先队列。

重复 (2)(3)(4) 直到优先队列为空。

实际上,上述算法其实就是用 priority_queue (优先队列)优化了每次在 B 集合中查找 dis 值最小的点的过程,使其复杂度降至 log 级别,用优先队列优化的 Dijkstra 算法复杂度为 O(mlog(m)+n)。

#include<bits/stdc++.h>
#define inf 1000000000
#define N 2000200
using namespace std;
int n, m, s;
int head[N], pos;
struct edge { int to, next, c; }e[N << 1];
void add(int a, intb, int c){pos++; e[pos].to = b, e[pos].next = head[a], e[pos].c = c, head[a] = pos;
}
struct node { int d, id; };
bool operator < (node x, node y) { return x.d > y.d; }
priority_queue< node > Q;
int dis[N];
void dijkstra(){for (int i = 1; i <= n; i++)dis[i] = inf;dis[s] = 0; nodest; st.d = 0, st.id = s;Q.push(st);while (!Q.empty())    {node u = Q.top(); Q.pop();if (u.d > dis[u.id])continue;for (int i = head[u.id]; i; i = e[i].next)        {int to = e[i].to;if (dis[to] > dis[u.id] + e[i].c)            {dis[to] = dis[u.id] + e[i].c;node v; v.id = to, v.d = dis[to];Q.push(v);
.
.}}}
}
int main(){cin >> n >> m >> s;for (int i = 1; i <= m; i++)   {int x, y, c;cin >> x >> y >> c;add(x, y, c);add(y, x, c);}dijkstra();for (int i = 1; i <= n; i++)cout << dis[i] << " ";
}

权值为1的最短路

在网格图中,经常会遇到最少走多少步?最少消耗多代价的问题。这类问题点和点之间的距离都是一样的,被当作1来处理。

在普通图中,也经常遇到最少经过多少条边移动几次的问题。这类问题中,每条连接点的边都是一样的,权值被当作1来处理。遇到这类问题,仍可以沿用BFS。

字符串匹配

Manacher算法

最长回文子串问题

回文串: 对于一个字符串 s,如果 s=rev(s),就称 s 是回文串。其中 rev(s) 表示字符串 s 的翻转。例如,"ababa" 或者 "moom" 就是回文串。

回文子串:s 的子串 t 如果是回文串,我们就称 t 是 s 的回文子串。

最长回文子串问题: 给出一个字符串 s ,求出 s最长的回文子串长度。

暴力算法

解决最长回文子串问题,最为朴素的算法就是依次以每个字符或者两个字符之间的空隙为中心,当两侧对应字符相同时向两侧进行扩展,扩展到不能再扩展时,我们就得到了该中心对应的回文子串最大长度。

代码实现也非常简单:

int work(char* s, int n) {int ans = 0;for (int i = 1; i <= n; i++) {int l = i, r = i;while (l - 1 != 0 && r + 1 <= n && s[l - 1] == s[r + 1])l--, r++;ans = max(ans, r - l + 1);if (i < n && s[i] == s[i + 1]) {l = i, r = i + 1;while (l - 1 != 0 && r + 1 <= n && s[l - 1] == s[r + 1])l--, r++;ans = max(ans, r - l + 1);}}return ans;
}

对于每个枚举的回文中心,最坏情况下要向外扩展 O(n) 次,而回文中心有 O(n) 个,因此总复杂度为 O(n2) 。

算法描述

Manacher 算法是一种可以在O(n)的时间复杂度求出一个长为 n 的字符串的最长回文子串的算法,它思路简单、代码简单,但功效却十分强大。

通过暴力算法也不难发现,求回文串时需要分奇偶,因此在提出 Manacher 算法前,我们需要先对给出的字符串做些简单的处理,来使得奇偶长度的字符串可以统一考虑。

处理的方法很简单,就是在每个相邻的字符之间插入一个分隔符,字符串的首尾也要加,这个分隔符需要是一个在原串中没有出现过的字符。一般我们用'#',例如:

原串 s : abaab

新串 t : #a#b#a#a#b#

这样处理后,原来是奇数长度的回文串还是奇数长度,原来是偶数长度的回文串就变成以 # 为中心的奇数回文串了,因此我们之后只需要讨论奇数长度的回文串的求解方法即可。

在 Manacher 算法中,我们用一个辅助数组 p 记录以每个字符为中心的最长回文半径,也就是 p[i] 记录以 t[i] 为中心的最长回文子串的半径。显然, p[i] 最小为 1 , p[i] 为 1 时对应的回文子串就是字符 t[i] 本身。

我们对上面的例子写出对应的 p 数组:

原串 s

abaab

新串 t

#a#b#a#a#b#

p 数组

1 2 1 4 1 2 5 2 1 2 1

接下来我们证明 p[i]−1 就是以 t[i] 为中心的最长回文子串在原串中的长度。

算法证明

显然 L=2∗p[i]−1 是新串 t 中以 t[i] 为中心的最长回文子串长度。

根据我们构造新串的方式可以发现,以 t[i] 为中心的最长回文子串一定是以 # 开头和结尾的。所以 L 减去最前或者最后的 # 字符之后,就是原串中对应回文子串长度的两倍。即原串长度为 (L−1)/2=p[i]−1 ,得证。

这里用到了动态规划的思想,依次求 p[1],p[2],p[3]...p[n] 的值,在要求解 p[i] 时,之前的 p[] 值已经得到了,我们就可以利用回文串的性质和之前的 p[] 值来对求解 p[i] 的过程进行优化。

先给出求解 p 数组的核心代码,为了防止在扩展回文串的过程中越界,在 t 两侧分别添上了字符:

for (int i = 1; i <= m; i++){

    p[i] = 1;

    if (maxid > i)

        p[i] = min(p[2 * id - i], maxid - i);

    while (t[i - p[i]] == t[i + p[i]])

        p[i]++;

    if (i + p[i] > maxid)

        id = i, maxid = i + p[i];

}

while(t[i-p[i]] == t[i+p[i]])

    p[i]++;

上面这段代码的作用不难理解,就是暴力的向两侧扩展回文子串。

if (maxid > i)

    p[i] = min(p[2 * id - i], maxid - i);

以上这段代码又是怎么一回事呢?

事实上, maxid 记录的是在求解 p[i] 之前,我们找到的回文串中,延伸到最右端的回文串的右端点位置,而 id 记录的就是这个回文串的中心的下标。

可以这样推导的原因,是利用了回文串的对称性,

j=2∗id−i 就是 i 关于 id 的对称点,根据回文串的对称性, p[j] 代表的回文串时可以对称到 i 这边的,但是如果 p[j] 代表的回文串对称过来以后超过 maxid 的话,超出的部分就不能对称过来了(因为不能保证左侧超出的部分和右侧超出的部分相同),所以 p[i] 的下限就取 p[2∗id−i] 和 maxid−i 的较小者,即 :

if(maxid > i)

    p[i] = min(p[2 * id - i], maxid - i);

有了一个下限之后,再暴力的扩展 p[i] 。这样做的时间复杂度是怎样的呢?我们只需要关注暴力扩展 p[i] 的复杂度。

需要暴力扩展 p[i] ,就意味着在暴力扩展前, p[i] 代表的回文串右端点就已经到了 maxid 了,那么我们每暴力扩展一次,就会使得 maxid 增加 1 ,而 maxid 最多为 m ,因此在整个过程中最多只会暴力扩展 O(m) 次,其中 m 表示新串 t 的长度。

完整的代码我们在下面的例题中展示。

DAG图与拓扑排序

拓扑排序

在图论中,由一个有向无环图的顶点组成的序列,当且仅当满足下列条件时,称为该图的一个拓扑排序:

每个顶点出现且只出现一次;若 A 在序列中排在 B 的前面,则在图中不存在从 B 到 A 的路径。

从拓扑排序的概念我们就可以看出,只有有向无环图才存在拓扑排序。因此,如果有算法可以帮我们判断一张图是否存在拓扑排序,我们就能判断这张图是否为有向图。事实上,我们不仅能很快的判断一张图是否存在拓扑排序,还能顺便求出它的拓扑排序。

一个图 G ​ 的拓扑排序大多情况下是不唯一的。

卡恩算法(BFS)

算法描述
统计图中每个点的入度(即连向该点的边数)

将入度为 0 的点放入队列

每次从队列中取出一个点 u ,遍历从这个点出发的所有出边 (u−>v) ,删除 u−>v 这条边(代码实现上仅表现为让 v 的入度 −1 ),若 v 的入度变为 0 ,则将 v 放入队列

重复(3)直到队列为空,若所有点都进出过队列,则点的出队顺序即为这张图的拓扑序,否则说明该图不存在拓扑排序(存在有向环)。

const int N = 2e5 + 100;
const int M = 4e5 + 100;
int head[N], Next[M], ver[M], tot;
int deg[N];
void add(int x, int y) {ver[++tot] = y;Next[tot] = head[x];head[x] = tot;
}
queue<int>qc;
int topsort(int n) {int cnt = 0;while (qc.size())qc.pop();for (int i = 1; i <= n; i++) {if (deg[i] == 0) {qc.push(i);}}while (qc.size()) {int x = qc.front();cnt++;qc.pop();for (int i = head[x]; i; i = Next[i]) {int y = ver[i];if (--deg[y] == 0)qc.push(y);}
}return cnt == n;
}
int main() {int n, m, x, y;cin >> n >> m;for (int i = 1; i <= m; i++) {cin >> x >> y;add(x, y);deg[y]++;}if (topsort(n))cout << "Yes" << endl;else cout << "No" << endl;
}

拓扑排序的DFS算法

算法描述

拓扑排序的 DFS 的实现更加简单,从一个入度为0的节点开始做 DFS ,每访问到一个节点,标注为已访问,下次重复访问到时直接跳过。当所有子节点都访问过后,将当前节点加入列表。这样最终将会得到一个叶子节点在前,根结点在最后的节点列表。将该列表反转,得到的就是拓扑排序好的节点。

有向无环图(DAG)

有向图中,每条边都有特定的方向,当我们能从某一个点 u 出发,通过图中的有向边经过若干个点后又回到 u 点 (u−>v1−>v2−>v3...−>vk−>u)[k>=1] ,说明这张有向图是存在环的,而若 v1v2v3...vk 互不相同,则 u−>v1−>v2−>v3...−>vk−>u 就是这张图中的一个简单环。在图论中,我们把不存在环的有向图称为有向无环图( DAG )

不难想到, DAG 是有向图的一种,由于 DAG 中不含有向环,因此相比于一般有向图, DAG 的相关性质总是更加容易探索。在解决实际问题时,我们也往往会把一般有向图转化为 DAG 。但在此之前,我们首先要学习如何判断一张图是否是有向无环图。

DAG图的判定

在第二章中,我们学习了两种图的遍历方式: BFS (广度优先搜索)与 DFS (深度优先搜索)。我们了解到, BFS 是一种利用队列实现的,基于循环的遍历方式,与 DFS 相比,它具有高效、所需栈空间小的特点。

考虑一张有向无环图,从任意点 u 出发,对这张图进行遍历时,如果点 u 被经过了多于一次(尽管没有再次将这个点放入队列),则说明我们找到了一个有向环(从点 u 出发有一条路径回到点 u )

#define N 200200
using namespace std;
queue<int>Q;
vector<int>G[N];
int n, m, tot;
int in[N], topo[N];
int main(){cin >> n >> m;for (int i = 1; i <= m; i++)    {int x, y;cin >> x >> y;G[x].push_back(y);in[y]++;}for (int i = 1; i <= n; i++)if (!in[i])Q.push(i);while (!Q.empty())    {int u = Q.front(); Q.pop();topo[++tot] = u;for (int i = 0; i < G[u].size(); i++)        {int v = G[u][i];in[v]--;if (!in[v])Q.push(v);}}
.
.if (tot < n)puts("circle!");else    {puts("toposort: ");for (int i = 1; i <= tot; i++)printf("%d ", topo[i]);}
}

上述代码判断有向图是否存在环的复杂度为 O(n∗m) ,其中 n 为有向图的点的个数, m 为有向图的边数。

对于规模较大的有向图,用上面的代码需要耗费大量时间才能跑出结果,事实上,我们有更优美的算法来判断有向图中是否存在环,它就是拓扑排序

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

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

相关文章

新形势下职业教育大数据人才培养策略

一、引言 随着信息技术的飞速发展&#xff0c;大数据已成为驱动经济社会变革的关键力量。在新形势下&#xff0c;职业教育作为技术技能人才培养的重要阵地&#xff0c;面临着如何适应大数据时代要求、提升人才培养质量的紧迫任务。当前&#xff0c;职业教育在大数据人才培养方…

云HIS系统源码,业务云协同和数据云协同的数字化医院信息系统

云HIS是利用云计算、大数据、物联网、移动互联网等技术&#xff0c;打造的一个安全、便捷、共享、协同的医疗互联网云生态平台。核心功能是业务云协同和数据云协同。云HIS具有可扩展、易共享、易协同、低成本、体验号、更便捷、易维护的优势&#xff0c;重新定义了数字化医院信…

leetcode日记(49)旋转链表

其实不难&#xff0c;就是根据kk%len判断需要旋转的位置&#xff0c;再将后半段接在前半段前面就行。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : …

uniapp的h5,读取本地txt带标签的文件

效果图 使用的回显的标签是u-parse&#xff0c;下面的网址讲了这个标签的相关 https://www.cnblogs.com/huihuihero/p/12978903.html 导入此插件 https://ext.dcloud.net.cn/plugin?id364 使用 uni.request({// 本地文件url: "/static/互联网医院医师端用户协议.txt…

C# datetimePicker

1. 直接把控件拉到设计器中&#xff0c;此时不要调整控件的values属性&#xff0c;这样就可以 打开后每次默认显示当天日期。 2. 属性Format long长日期格式默认值short短日期格式Time时间格式custom自定义时间格式在customFormat这个属性设置&#xff0c;比如yyyy-MM-dd HH…

Stage模型应用程序包结构

目录 官网地址 官网结构图 开发态包结构 工程目录结构 配置文件 module.json5配置文件 app.json5配置文件 官网地址 官网地址 包结构 官网结构图 开发态包结构 在DevEco Studio上创建一个项目工程&#xff0c;并尝试创建多个不同类型的Module&#xff08;类似一个一个的页…

C 语言基础概念总结

C 语言基础概念总结 一、数据类型 目录 C 语言基础概念总结 一、数据类型 基本数据类型 构造数据类型 二、变量与常量 三、运算符与表达式 算术运算符 关系运算符 逻辑运算符 赋值运算符 自增自减运算符 四、控制流语句 顺序结构 选择结构 循环结构 五、函数 …

c++笔记1

目录 结构体 关于结构体 结构体变量 成员的获取和赋值 结构体排序 结构体与模板 结构体嵌套 结构体与函数 模板与pair 模板 pair 多重循环 多重循环结构 嵌套注意事项 多重循环实例 再谈变量的作用域 多重循环的控制 嵌套中的break和continue 多重循环中的控…

PCDN技术如何降低运营成本?

PCDN技术通过以下几种方式降低运营商的运营成本: 1.利用用户设备作为缓存节点: PCDN技术将用户设备纳入内容分发网络&#xff0c;利用这些设备的闲置带宽和存储资源来缓存和分发内容。这种方式不需要运营商投入大量的高成本服务器和带宽资源&#xff0c;从而降低了硬件和带宽…

jetbrains 、IntelliJ IDEA、PyCharm 等工具英语音译读音

本心、输入输出、结果 文章目录 jetbrains 、IntelliJ IDEA、PyCharm 等工具英语音译读音前言jetbrains 、IntelliJ IDEA、PyCharm 等工具英语音译读音jetbrains 、IntelliJ IDEA、PyCharm 等工具英语音译读音 编辑 | 简简单单 Online zuozuo 地址 | https://blog.csdn.net/qq_…

go-kratos 学习笔记(6) 数据库gorm使用

数据库是项目的核心&#xff0c;数据库的链接数据是data层的操作&#xff0c;选择了比较简单好用的gorm作为数据库的工具&#xff1b;之前是PHP开发&#xff0c;各种框架都是orm的操作&#xff1b;gorm还是很相似的&#xff0c;使用起来比较顺手 go-kratos官网的实例是ent&…

逆向案例二十九——某品威客登录,请求头参数加密,简单webpack

网址&#xff1a;登录- 一品威客网,创新型知识技能共享服务平台 抓到登陆包分析&#xff0c;发现请求头有参数加密&#xff0c;直接搜索 定位到加密位置&#xff0c;打上断点&#xff0c;很明显是对象f的a方法进行了加密。 往上找f&#xff0c;可以发现f被定义了&#xff0c;是…

pc端vue2实现截图功能

npm install js-web-screen-shot --save import ScreenShort from "js-web-screen-shot"; <button @click="screenhotFnc">点我截图</button> //最后生成一个数组,base格式,可循环渲染到页面上面 screenhotFnc() {new ScreenShort({//ap…

Android中Service学习记录

目录 一 概述二 生命周期2.1 启动服务startService()2.2 绑定服务bindService()2.3 先启动后绑定2.4 先绑定后启动 三 使用3.1 本地服务&#xff08;启动式&#xff09;3.2 可通信的服务&#xff08;绑定式&#xff09;3.3 前台服务3.4 IntentService 总结参考 一 概述 Servic…

【Android】广播机制

【Android】广播机制 前言 广播机制是Android中一种非常重要的通信机制&#xff0c;用于在应用程序之间或应用程序的不同组件之间传递信息。广播可以是系统广播&#xff0c;也可以是自定义广播。广播机制主要包括标准广播和有序广播两种类型。 简介 在Android中&#xff0c…

mybatise全接触-面试宝典-知识大全

1 . 简述什么是Mybatis和原理 &#xff1f; Mybatis工作原理&#xff1a; &#xff08;1&#xff09;Mybatis是一个半ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它内部封装了JDBC&#xff0c;加载驱动、创建连接、创建statement等繁杂的过程&#xff0c;开发者…

Istio_01_Istio初识

文章目录 IstioService Mesh Istio Istio: 以服务网格形态用于服务治理的开放平台和基础设施 本质: 以非侵入式治理服务之间的访问和调用服务治理: 流量管理、可观测性、安全性可同时管理多类基础设施(多种网络方案) 如: Istio和Kubernetes架构的结合 Istio通过Kubernetes的域…

mysql各种锁总结

mysql全局锁 读锁&#xff08;共享锁&#xff09; 阻止其他用户更新&#xff0c;但允许他们读取数据。 写锁&#xff08;排他锁&#xff09; 阻止其他用户读取和更新数据。 全局锁场景&#xff1a;进行数据库备份 数据库备份 背景&#xff1a;备份数据肯定要保证数据一致…

Ubuntu 重置root密码

Ubuntu 重置root密码 当系统管理员或者授权用户忘记了root密码时&#xff0c;重置密码能够提供紧急访问系统的方法。这种情况下&#xff0c;重置密码可以避免因为密码丢失而导致的系统无法访问的问题&#xff0c;确保及时的操作和维护。在进行系统安全审计或者需要紧急恢复访问…

Top K问题及解决

Top K问题及解决 Top K问题&#xff1a;在大规模数据处理中&#xff0c;经常会需要在海量数据中找出频率最高的前K个数。比如&#xff0c;在搜索引擎中&#xff0c;统计搜索最热门的10个查询词等。针对Top K类问题&#xff0c;可以使用分治算法Trie树/hash 小/大顶堆&#xff…