简述

对于初学 Tarjan 的你来说,肯定和我一开始学 Tarjan 一样无比迷茫。

网上大框大框的定义就足以让一个萌新从入门到入土自闭。

所以本文决定不在这里对于 Tarjan 的定义和原理做过多介绍。(当然如果还是无法理解可以尝试直接理解代码)

注意&特别鸣谢:这篇文章,本文也有多处讲解与图片转自此文

作用

其实这都是后话了...。(毕竟你不会这个东西知道它的作用也没什么用)

下面是一段令人窒息的专业定义,萌新慎入:

  1. 在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected)。
  2. 如果有向图G的每两个顶点都强连通,称G是一个强连通图
  3. 非强连通图有向图的极大强连通子图,称为强连通分量(strongly connected components)。

所以 Tarjan 的基础用处就是:求一个图的强连通分量

举个栗子:

下图中,子图 \(\{1,2,3,4\}\) 为一个强连通分量,因为顶点 \(1,2,3,4\) 两两可达。\(\{5\},\{6\}\)也分别是两个强连通分量。

让我们带着疑惑和不解去探索 Tarjan 的奥秘吧。

Tarjan 算法

原理

Tarjan 算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。

搜索时,把当前搜索树中未处理的节点加入一个堆栈,回溯时可以判断栈顶到栈中的节点是否为一个强连通分量。

当然,主要还是靠下面两个标记。

出场人物

其实就是用到了哪几个变量。

  1. \(Dfn(u)\):为节点u搜索的次序编号(时间戳)。
  2. \(Low(u)\):为 \(u\) 或 \(u\) 的子树能够追溯到的最早的栈中节点的次序号(人话:\(u\) 的子树以及其连向的节点中 \(dfn[]\) 的最小值)。

由定义可以得出:

Low(u)=Min
{
DFN(u),
Low(v),(u,v)为树枝边,u为v的父节点
DFN(v),(u,v)为指向栈中节点的后向边(非横叉边)
}

当 \(Dfn(u)=Low(u)\) 时,以 \(u\) 为根的搜索子树上所有节点是一个强连通分量。

感性理解一下,当一个集合 \(S\) 构成强连通分量时,它们的祖先节点肯定是 \(dfn[]\) 最小的。(遍历时间最早)

那么除了这个祖先节点之外,其他所有节点的 \(low[]\) 肯定都会被刷新。

所以唯一不变的就是祖先节点,找到祖先节点也就找到了整个强连通分量,而栈中的残留数字都属于 \(S\) 集合。

图示

从节点 \(1\) 开始 DFS,把遍历到的节点加入栈中。

搜索到节点 \(u=6\) 时,\(Dfn[6]=Low[6]\),找到了一个强连通分量。

退栈到 \(u=v\) 为止,\(\{6\}\)为一个强连通分量。

返回节点 \(5\),发现 \(Dfn[5]=Low[5]\),退栈后 \(\{5\}\) 为一个强连通分量。

返回节点 \(3\),继续搜索到节点 \(4\),把 \(4\) 加入堆栈。

发现节点 \(4\) 向节点 \(1\) 有后向边,节点 \(1\) 还在栈中,所以 \(Low[4]=1\)。

节点 \(6\) 已经出栈,\((4,6)\) 是横叉边,返回 \(3\),\((3,4)\) 为树枝边,所以 \(Low[3]=Low[4]=1\)。

继续回到节点 \(1\),最后访问节点 \(2\)。访问边 \((2,4)\),\(4\) 还在栈中,所以 \(Low[2]=Dfn[4]=5\)。

返回 \(1\) 后,发现 \(Dfn[1]=Low[1]\),把栈中节点全部取出,组成一个连通分量 \(\{1,3,4,2\}\)。

至此,算法结束。经过该算法,求出了图中全部的三个强连通分量 \(\{1,3,4,2\},\{5\},\{6\}\)。

可以发现,运行 Tarjan 算法的过程中,每个顶点都被访问了一次,且只进出了一次堆栈,每条边也只被访问了一次,所以该算法的时间复杂度为 \(O(N+M)\)。

代码实现

代码实现很简单(前提是你看懂了上面的讲解,也不排除很多同学可以直接通过代码加深理解)。

//co[] 表示这个强联通分量的“颜色”,sum[] 表示这个“颜色”的强连通分量的节点个数。
//num 是时间戳,tot 是栈顶,col 是“颜色”总数。
void tarjan(int u){
dfn[u]=low[u]=++num;//打上时间戳,并同时给 low[u] 赋初值。
s[++tot]=u;//进栈。
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to;
if(!dfn[v])//如果没有遍历过(即这是一条树枝边)。
tarjan(v),low[u]=min(low[u],low[v]);
else if(!co[v])//遍历过(即这是一条返祖变或横向边)。
low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u]){//找到啦。
co[u]=++col,sum[col]=a[u];
while(u!=s[tot])
sum[col]+=a[s[tot]],co[s[tot]]=col,--tot;
--tot;
}
return;
} for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);

例题

多做例题通常是了解新算法的重要过程,建议读者完全消化上面的模板后在食用。

题目都不难,而且十分适合初学者。

例题一

受欢迎的牛

简化题意:

有一个包括 \(N\) 个点,\(M\) 条边的有向图,求所有节点都能到达的节点的个数。如果没有,则输出 0。

分析题目,这样的节点只可能出现在唯一一个出度为 0 的强联通分量内。

若出现两个以上出度为 0 的强连通分量则不存在这样的节点,因为那几个出度为零的分量无法传递出去。

而分量内的节点一定是可以相互到达的。

那么题目就变为求:图中出度为 0 的强联通分量的节点总数,如果有多个这样的强连通分量则输出 0

标准的 Tarjan 模板题。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>
#define N 10010
#define M 50010
using namespace std; int n,m,head[N],cnt=0;
int col=0,num=0;
int co[N],dfn[N],low[N],s[N],de[N],sum[N];
struct Edge{
int nxt,to;
}ed[M]; int read(){
int x=0,f=1;char c=getchar();
while(c<'0' || c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' && c<='9') x=x*10+c-48,c=getchar();
return x*f;
} void add(int u,int v){
cnt++;
ed[cnt].nxt=head[u];
ed[cnt].to=v;
head[u]=cnt;
return;
} int tot=0; void tarjan(int u){
dfn[u]=low[u]=++num;
tot++;
s[tot]=u;
for(int i=head[u];i;i=ed[i].nxt){
int v=ed[i].to;
if(!dfn[v])
tarjan(v),low[u]=min(low[u],low[v]);
else if(!co[v])
low[u]=min(low[u],dfn[v]);
}
if(low[u]==dfn[u]){
co[u]=++col;
++sum[col];
while(s[tot]!=u)
++sum[col],co[s[tot]]=col,--tot;
--tot;
}
return;
} int main(){
n=read(),m=read();
int u,v;
for(int i=1;i<=m;i++)
u=read(),v=read(),add(u,v);
for(int i=1;i<=n;i++)
if(!dfn[i]) tarjan(i);
int ans,k=0;
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=ed[j].nxt)
if(co[i]!=co[ed[j].to])
de[co[i]]++;
for(int i=1;i<=col;i++)
if(!de[i])
ans=sum[i],k++;
if(k==1) printf("%d\n",ans);
else puts("0");
return 0;
}

例题二

最大半连通子图

对于初学者来说可能有一定的难度,码量也是 100+ 行。

简单来说,这道题是有向图中常用的套路:Tarjan + 拓扑排序 + DP

定义挺长的,理解起来比较困难,但是读懂后就发现是水题了:缩点后求有向图中的最长链

我们先用模板式的 Tarjan 把图进行缩点。

然后我们对这个缩过点的图重新进行建边(注意:重新建图需要判重边,否则最后最长链的数量会受到影响)。

最后我们拓扑加 DP 把最长链的节点的数量和最长链的数量求出。

#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
#define N 100010
#define M 1000010
using namespace std; int n, m, MOD, head[N], cnt = 0;
int low[N], dfn[N], s[N], co[N], sum[N];
int tot = 0, numm = 0, col = 0;
struct Edge {
int nxt, to;
} ed[M]; int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
} void add(int u, int v) {
++cnt;
ed[cnt].nxt = head[u];
ed[cnt].to = v;
head[u] = cnt;
return;
} void tarjan(int u) {
dfn[u] = low[u] = ++numm;
s[++tot] = u;
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (!co[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
co[u] = ++col;
++sum[col];
while (s[tot] != u) ++sum[col], co[s[tot]] = col, --tot;
--tot;
}
return;
} int nu[M], x[M], y[M], ru[N]; bool cmp(int a, int b) {
if (x[a] != x[b])
return x[a] < x[b];
return y[a] < y[b];
} void remove() {
for (int i = 1; i <= m; i++) {
nu[i] = i;
x[i] = co[x[i]];
y[i] = co[y[i]];
}
sort(nu + 1, nu + m + 1, cmp);
cnt = 0;
memset(head, 0, sizeof(head));
memset(ed, 0, sizeof(ed));
for (int i = 1; i <= m; i++) {
int z = nu[i];
if (x[z] != y[z] && (x[z] != x[nu[i - 1]] || y[z] != y[nu[i - 1]])) {
add(x[z], y[z]);
++ru[y[z]];
}
}
return;
} int dis[N], num[N], ans; void topo() {
queue<int> q;
for (int i = 1; i <= col; i++) {
if (!ru[i]) {
q.push(i), dis[i] = sum[i], num[i] = 1;
if (dis[ans] < dis[i])
ans = i;
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (dis[v] < dis[u] + sum[v]) {
dis[v] = dis[u] + sum[v];
num[v] = 0;
if (dis[ans] < dis[v])
ans = v;
}
if (dis[v] == dis[u] + sum[v])
num[v] = (num[v] + num[u]) % MOD;
if (!--ru[v])
q.push(v);
}
}
return;
} int anss; void ask() {
for (int i = 1; i <= n; i++)
if (dis[i] == dis[ans])
anss = (anss + num[i]) % MOD;
} int main() {
n = read(), m = read(), MOD = read();
int u, v;
for (int i = 1; i <= m; i++) {
x[i] = read(), y[i] = read();
add(x[i], y[i]);
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
remove();
topo();
ask();
printf("%d\n%d\n", dis[ans], anss);
return 0;
}

例题三

网络协议

Tarjan 和 DAG 是有不解之缘的,所以经常通过 DAG 的某些性质来做题。

其实如果没有第二问的话,这道题就十分的简单了。

  1. 第一问:Tarjan 缩点后寻找入度为 0 的强连通分量的个数。
  2. 第二问:\(max(sum_{入度为 0 的强连通分量},sum_{出度为 0 的强连通分量})\)。

那么第二问的为什么可以这么求呢?

定理:将 DAG 变为有向连通图的最小方案数为 \(max(sum_{入度为 0 的强连通分量},sum_{出度为 0 的强连通分量})\)。

构造:连接 \(min(sum_{入度为 0 的强连通分量},sum_{出度为 0 的强连通分量})\) 个入度为 0 与出度为 0 的强连通分量构成环,

然后再随意连接剩余的入度为 0 或出度为 0 的强连通分量到环上任意节点。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#include <queue>
#define N 110
using namespace std; int n, head[N], cnt = 0;
int low[N], dfn[N], s[N], co[N], sum[N], col = 0, tot = 0, num = 0;
struct Edge {
int nxt, to;
} ed[N * N]; int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
} void add(int u, int v) {
ed[++cnt].nxt = head[u];
ed[cnt].to = v;
head[u] = cnt;
return;
} void tarjan(int u) {
dfn[u] = low[u] = ++num;
s[++tot] = u;
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (!co[v])
low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
co[u] = ++col;
++sum[col];
while (s[tot] != u) ++sum[col], co[s[tot]] = col, --tot;
--tot;
}
return;
} int ru[N], cu[N]; int main() {
n = read();
int v;
for (int u = 1; u <= n; u++) {
v = read();
while (v) add(u, v), v = read();
}
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
if (col == 1) {
printf("1\n0\n");
return 0;
}
for (int u = 1; u <= n; u++)
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (co[u] != co[v])
++cu[co[u]], ++ru[co[v]];
}
int a = 0, b = 0;
for (int i = 1; i <= col; i++) {
if (!ru[i])
++a;
if (!cu[i])
++b;
}
printf("%d\n%d\n", a, max(a, b));
return 0;
}

例题四

间谍网络

这道题没有看上去那么简单,与普通缩点题唯一的不同是它加上了限制条件。

那怎么办?其实我们在循环是也可以加上限制条件,同时通过 \(dfn[]\) 判重(具体见代码)。

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#define N 3010
#define M 8010
using namespace std; int n, m1, m2, head[N], cnt = 0, cost[N];
int low[N], dfn[N], co[N], s[N], ru[N], sum[N];
int col = 0, tot = 0, num = 0;
struct Edge {
int nxt, to;
} ed[M]; int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
} void add(int u, int v) {
ed[++cnt].nxt = head[u];
ed[cnt].to = v;
head[u] = cnt;
return;
} void init() {
n = read(), m1 = read();
int u, v;
memset(cost, 0x3f, sizeof(cost));
memset(sum, 0x3f, sizeof(sum));
for (int i = 1; i <= m1; i++) u = read(), v = read(), cost[u] = v;
m2 = read();
for (int i = 1; i <= m2; i++) u = read(), v = read(), add(u, v);
return;
} void tarjan(int u) {
dfn[u] = low[u] = ++num;
s[++tot] = u;
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (!co[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
co[u] = ++col, sum[col] = cost[u];
while (s[tot] != u) sum[col] = min(sum[col], cost[s[tot]]), co[s[tot]] = col, --tot;
--tot;
}
return;
} int main() {
init();
for (int i = 1; i <= n; i++)
if (!dfn[i] && cost[i] != 0x3f3f3f3f)//限制条件。
tarjan(i);
for (int i = 1; i <= n; i++)
if (!dfn[i]) {//判断无解。
printf("NO\n%d\n", i);
return 0;
}
for (int u = 1; u <= n; u++)
for (int i = head[u]; i; i = ed[i].nxt)
if (co[ed[i].to] != co[u])
++ru[co[ed[i].to]];
int ans = 0;
for (int i = 1; i <= col; i++)
if (!ru[i])
ans += sum[i];
printf("YES\n%d\n", ans);
return 0;
}

例题五

抢掠计划

首先日常缩点,重新建立有向图,然后...,有一个神仙做法:

  1. 化点权为边权:举个栗子,假设有边 \(u\to v\),\(v\) 的点权为 \(w\),那么令 \(edge(u,v)=-w\)。
  2. 因为置了负边权,所以将最长路变为熟悉的最短路,跑 SPFA 即可。
  3. 找到有酒吧的点中 \(-dis[]\) 最大的即可。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <queue>
#define N 500010
using namespace std; int n, m, S, P, x[N], y[N], a[N], head[N], cnt = 0;
int low[N], dfn[N], co[N], s[N], sum[N];
int col = 0, tot = 0, num = 0;
bool bar[N];
struct Edge {
int nxt, to, val;
} ed[N]; int read() {
int x = 0, f = 1;
char c = getchar();
while (c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = getchar();
while (c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
} void add(int u, int v) {
ed[++cnt].nxt = head[u];
ed[cnt].to = v;
head[u] = cnt;
return;
} void tarjan(int u) {
dfn[u] = low[u] = ++num;
s[++tot] = u;
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else if (!co[v])
low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
co[u] = ++col, sum[col] = a[u];
while (u != s[tot]) sum[col] += a[s[tot]], co[s[tot]] = col, --tot;
--tot;
}
return;
} void addedge(int u, int v, int w) {
ed[++cnt].nxt = head[u];
ed[cnt].to = v;
ed[cnt].val = w;
head[u] = cnt;
return;
} void Remove() {
cnt = 0;
memset(head, 0, sizeof(head));
memset(ed, 0, sizeof(ed));
for (int i = 1; i <= m; i++) {
if (co[x[i]] != co[y[i]])
addedge(co[x[i]], co[y[i]], -sum[co[y[i]]]);
}
return;
} int dis[N];
bool vis[N];
void SPFA(int s) {
queue<int> q;
memset(dis, 0x3f, sizeof(dis));
memset(vis, false, sizeof(vis));
s = co[s];
dis[s] = -sum[s];
vis[s] = true;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = head[u]; i; i = ed[i].nxt) {
int v = ed[i].to, w = ed[i].val;
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v])
q.push(v), vis[v] = true;
}
}
}
return;
} int main() {
n = read(), m = read();
int u, v;
for (int i = 1; i <= m; i++) x[i] = read(), y[i] = read(), add(x[i], y[i]);
for (int i = 1; i <= n; i++) a[i] = read();
for (int i = 1; i <= n; i++)
if (!dfn[i])
tarjan(i);
Remove();
S = read();
SPFA(S);
P = read();
int ans = 0;
for (int i = 1; i <= P; i++) u = read(), ans = max(ans, -dis[co[u]]);
printf("%d\n", ans);
return 0;
}

总结

图论中常用的算法之一就是 Tarjan 了,所以也是很重要的啦。

希望这篇文章对每一位读者都有帮助。

再次注意&特别鸣谢:这篇文章,本文也有多处讲解与图片转自此文

完结撒花。

浅谈 Tarjan 算法的更多相关文章

  1. 浅谈Tarjan算法

    从这里开始 预备知识 两个数组 Tarjan 算法的应用 求割点和割边 求点-双连通分量 求边-双连通分量 求强连通分量 预备知识 设无向图$G_{0} = (V_{0}, E_{0})$,其中$V_ ...

  2. 浅谈Tarjan算法及思想

    在有向图G中,如果两个顶点间至少存在一条路径,称两个顶点强连通(strongly connected).如果有向图G的每两个顶点都强连通,称G是一个强连通图.非强连通图有向图的极大强连通子图,称为强连 ...

  3. 浅谈 Tarjan 算法之强连通分量(危

    引子 果然老师们都只看标签拉题... 2020.8.19新初二的题集中出现了一道题目(现已除名),叫做Running In The Sky. OJ上叫绮丽的天空 发现需要处理环,然后通过一些神奇的渠道 ...

  4. 浅谈分词算法(5)基于字的分词方法(bi-LSTM)

    目录 前言 目录 循环神经网络 基于LSTM的分词 Embedding 数据预处理 模型 如何添加用户词典 前言 很早便规划的浅谈分词算法,总共分为了五个部分,想聊聊自己在各种场景中使用到的分词方法做 ...

  5. 浅谈分词算法(4)基于字的分词方法(CRF)

    目录 前言 目录 条件随机场(conditional random field CRF) 核心点 线性链条件随机场 简化形式 CRF分词 CRF VS HMM 代码实现 训练代码 实验结果 参考文献 ...

  6. 浅谈分词算法(3)基于字的分词方法(HMM)

    目录 前言 目录 隐马尔可夫模型(Hidden Markov Model,HMM) HMM分词 两个假设 Viterbi算法 代码实现 实现效果 完整代码 参考文献 前言 在浅谈分词算法(1)分词中的 ...

  7. 浅谈分词算法基于字的分词方法(HMM)

    前言 在浅谈分词算法(1)分词中的基本问题我们讨论过基于词典的分词和基于字的分词两大类,在浅谈分词算法(2)基于词典的分词方法文中我们利用n-gram实现了基于词典的分词方法.在(1)中,我们也讨论了 ...

  8. 浅谈Manacher算法与扩展KMP之间的联系

    首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解.网上解法颇多.时间复杂度也不尽同样,这里列述几种常见的解法. 解法一   ...

  9. 浅谈KMP算法及其next[]数组

    KMP算法是众多优秀的模式串匹配算法中较早诞生的一个,也是相对最为人所知的一个. 算法实现简单,运行效率高,时间复杂度为O(n+m)(n和m分别为目标串和模式串的长度) 当字符串长度和字符集大小的比值 ...

随机推荐

  1. How to read h5 file by Matlab

    In matlab, one can use the following command to read h5 file data = h5read(filename,ds) data = h5rea ...

  2. spring-boot-route(二)读取配置文件的几种方式

    Spring Boot提供了两种格式的配置文件,分别是properties 和 yml.Spring Boot最大的特点就是自动化配置,如果我们想修改自动化配置的默认值,就可以通过配置文件来指定自己服 ...

  3. 01 AS 首次编译执行项目过程中遇到的几个常见问题

    问题01 as打开时出现The environment variable JAVA_HOME (with The value of C:\Java\jdk1.8.0_101\bin) does not ...

  4. 【随笔】菜刀(代码执行)函数和命令执行函数详解及Getshell方法

    代码执行函数 VS 命令执行函数 一直想整理这两块的内容,但是一直没时间弄,直到前两天碰上一个写入了菜刀马但是死活连不上菜刀的站,顿时不知道怎么继续了,所以就趁这个机会整理了一下代码执行函数怎么get ...

  5. Lesktop开源IM移动端:接入LayIM移动端UI

    在<开源企业即时通讯和在线客服>中已介绍了Lesktop的桌面模式和Web模式,但是没有移动端.评论中 dotnetcms.org工作室 提到了LayIM,看了一下官网的演示和文档,如果用 ...

  6. 深入理解Logger日志——框架绑定原理

    深入理解Logger日志--框架绑定原理 说到Logger日志的动态绑定,主要归功与Slf4j,在之前的文章也说过,Slf4j是类似于Apache Common-Logging,英文为Simple L ...

  7. golang 语言的特性

    给函数传递参数的时候 map.slice.channel是按引用传递的 同一个变量不能用 := 这种方式创建并赋值两次. 一个包(package)的func .结构体类型变量如果要被外部的包调用.fu ...

  8. 2017-01-26--编译Linux内核2.6.30版本报错解决

    错误一: LD .tmp_vmlinux1 init/built-in.o: In function `run_init_process': /home/ox/tq2440/opt/EmbedSky/ ...

  9. 2014年 实验二 B2C网上购物

    实验二 B2C网上购物 [实验目的] ⑴.熟悉虚拟银行和网上支付的应用 ⑵.熟悉并掌握消费者B2C网上购物和商家的销售处理 [实验条件] ⑴.个人计算机一台 ⑵.计算机通过局域网形式接入互联网 (3) ...

  10. 2014年 实验一  C2C个人拍卖

    实验一  C2C个人拍卖 --实战淘宝 [实验目的] 掌握网上拍卖的基本流程和拍卖平台的运营 [实验条件]⑴.个人计算机一台 ⑵.计算机通过局域网形式接入互联网. ⑶.常用浏览器. [知识准备] 本实 ...