ARTS-第21周

ARTS (第21周)

开篇词

千里之行始于足下。

本周:

拓扑排序、最短距离算法,位图

Algorithm 算法

拓扑排序:

kahn(感觉就是BFS、广度优先)

DFS(深度优先)

还写了个混合的 但效率其实不如上面2种

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
package algorithm.senior.topo;

import java.util.LinkedList;

public class TopoTest {
public static void main(String[] args) {
test();
}

private static void test() {
int l = 5;
Graph g = new Graph(l);
Graph2 g2 = new Graph2(l);
for (int i = 0; i < l; i++) {

int n1 = random(l);
int n2 = random(l);
while (n2 == n1) {
n2 = random(l);
}
g.addEdge(n1, n2);
g2.addEdge(n2, n1);
// 就是说结果n1要先于n2
System.out.println(n1 + "->" + n2);
}
// 如果存在环 结果就会错误
System.out.println("kanh=========");
String r1 = kahn1(g);
String r2 = kahn2(g2);
String r3 = kahn3(g2);
// 3种结果可能不太一样 因为加载顺序不一样
// kahn2因为效率较低 每有一个顶点就得走一次全部的边
// 所以构建了一个反向邻接表来进行优化 方法为kahn3
// 当然也可以利用g2的邻接表构建反向的邻接表 放到kahn1里执行
System.out.println("->" + r1);
System.out.println("->" + r2);
System.out.println("->" + r3);
// 如果存在环 结果就会错误
// 2种结果可能不太一样 因为加载顺序不一样
System.out.println("dfs==========");
r1 = dfs1(g);
r2 = dfs2(g2);
System.out.println("->" + r1);
System.out.println("->" + r2);
System.out.println("mix==========");
r1 = mix1(g);
r2 = mix2(g2);
r3 = mix3(g);
String r4 = mix4(g2);
System.out.println("->" + r1);
System.out.println("->" + r2);
System.out.println("->" + r3);
System.out.println("->" + r4);
}

// 依赖列表版本
// kahn2因为效率较低 每有一个顶点就得走一次全部的边 所以构建了一个反向邻接表来进行优化
@SuppressWarnings("unchecked")
public static String kahn3(Graph2 g) {
String str = "";
LinkedList<Integer>[] adj = g.adj;
LinkedList<Integer>[] inv = new LinkedList[adj.length];
int[] in = new int[adj.length];
// 统计入度 依赖的数量 即每个顶点还需要多少个依赖性没加载
for (int i = 0; i < in.length; i++) {
in[i] = adj[i].size();
inv[i] = new LinkedList<Integer>();
}
LinkedList<Integer> help = new LinkedList<>();
for (int i = 0; i < in.length; i++) {
// 构建反向邻接表
for (int j = 0; j < adj[i].size(); j++) {
inv[adj[i].get(j)].add(i);
}
if (in[i] == 0) {
help.add(i);
}
}

while (!help.isEmpty()) {
Integer v = help.remove();
str += (v + ",");
// 这里每一次都得走全部的边 几个顶点就得走几次 构建反向邻接表可以更快
LinkedList<Integer> n = inv[v];
for (int i = 0; i < n.size(); i++) {
Integer w = n.get(i);
// 这里-1 就代表了 对应的节点的所需依赖完成了1个
// 当in[w] 为零的时候 就代表了这个节点的所有依赖都已经加载了
in[w]--;
// in[w]只会出现一次为0的情况
// 如果存在环 结果就会丢失 因为存在环 in[w]就不会有0
if (in[w] == 0) {
// 新顶点
help.add(w);
}
}
}
return str;
}

// 依赖列表版本
public static String kahn2(Graph2 g) {
String str = "";
LinkedList<Integer>[] adj = g.adj;
int[] in = new int[adj.length];
// 统计入度 依赖的数量 即每个顶点还需要多少个依赖性没加载
for (int i = 0; i < in.length; i++) {
in[i] = adj[i].size();
}
LinkedList<Integer> help = new LinkedList<>();
for (int i = 0; i < in.length; i++) {
if (in[i] == 0) {
help.add(i);
}
}
while (!help.isEmpty()) {
Integer v = help.remove();
str += (v + ",");
// 这里每一次都得走全部的边 几个顶点就得走几次 构建反向邻接表可以更快
for (int i = 0; i < adj.length; i++) {
// 找到所有有依赖v节点的节点
for (int j = 0; j < adj[i].size(); j++) {
if (adj[i].get(j) == v) {
// 这里-1 就代表了 对应的节点的所需依赖完成了1个
// 当in[i] 为零的时候 就代表了这个节点的所有依赖都已经加载了
in[i]--;
// in[i]只会出现一次为0的情况
// 如果存在环 结果就会丢失 因为存在环 in[i]就不会有0
if (in[i] == 0) {
// 新顶点
help.add(i);
}
}
}
}
}
return str;
}

// 被依赖列表版本
public static String kahn1(Graph g) {
String str = "";
LinkedList<Integer>[] adj = g.adj;
int[] in = new int[adj.length];
// 统计每个顶点需要多少个引用 即每个顶点还需要多少个依赖性没加载
for (int i = 0; i < adj.length; i++) {
for (int j = 0; j < adj[i].size(); j++) {
int w = adj[i].get(j);
in[w]++;
}
}
LinkedList<Integer> help = new LinkedList<>();
// 必须存在0引用的顶点 否则就是存在环的情况
for (int i = 0; i < in.length; i++) {
if (in[i] == 0)
help.add(i);
}
while (!help.isEmpty()) {
Integer v = help.remove();
str += (v + ",");
LinkedList<Integer> n = adj[v];
for (int i = 0; i < n.size(); i++) {
Integer w = n.get(i);
// 这里-1 就代表了 对应的节点的所需依赖完成了1个
// 当in[w] 为零的时候 就代表了这个节点的所有依赖都已经加载了
in[w]--;
// in[w]只会出现一次为0的情况
// 如果存在环 结果就会丢失 因为存在环 in[w]就不会有0
if (in[w] == 0) {
// 新顶点
help.add(w);
}
}
}
return str;
}

// 混合
private static String mix4(Graph2 g) {
StringBuilder r = new StringBuilder();
LinkedList<Integer>[] adj = g.adj;
LinkedList<Integer> help = new LinkedList<>();
boolean[] vt = new boolean[adj.length];
for (int i = 0; i < adj.length; i++) {
if (adj[i].size() != 0) {
help.add(i);
} else {
// 不需要遍历到的顶点
vt[i] = true;
r.append(i + ",");
}
}

while (!help.isEmpty()) {
Integer v = help.remove();
if (vt[v]) {
continue;
}
vt[v] = true;
dfs(v, adj, vt, r);
}

return r.toString();
}

// 混合
private static String mix3(Graph g) {
StringBuilder r = new StringBuilder();
LinkedList<Integer>[] adj = g.adj;
boolean[] vt = new boolean[adj.length];
@SuppressWarnings("unchecked")
LinkedList<Integer>[] inv = new LinkedList[adj.length];
for (int i = 0; i < inv.length; i++) {
inv[i] = new LinkedList<Integer>();
}
for (int i = 0; i < adj.length; i++) {
// 构建反向邻接表
for (int j = 0; j < adj[i].size(); j++) {
inv[adj[i].get(j)].add(i);
}
}

LinkedList<Integer> help = new LinkedList<>();
// 必须存在0引用的顶点 否则就是存在环的情况
for (int i = 0; i < inv.length; i++) {
if (inv[i].size() != 0) {
help.add(i);
} else {

// 不需要遍历到的顶点
vt[i] = true;
r.append(i + ",");
}
}
while (!help.isEmpty()) {
Integer v = help.remove();
if (vt[v]) {
continue;
}
vt[v] = true;
dfs(v, inv, vt, r);
}

return r.toString();
}

// 混合
private static String mix2(Graph2 g) {
StringBuilder r = new StringBuilder();
LinkedList<Integer>[] adj = g.adj;
LinkedList<Integer> help = new LinkedList<>();
boolean[] vt = new boolean[adj.length];
for (int i = 0; i < adj.length; i++) {
if (adj[i].size() != 0) {
help.add(i);
}
}

while (!help.isEmpty()) {
Integer v = help.remove();
if (vt[v]) {
continue;
}
vt[v] = true;
// str += (v + ",");
dfs(v, adj, vt, r);
}
// 没有遍历到的顶点
for (int i = 0; i < vt.length; i++) {
if (!vt[i]) {
r.append(i + ",");
}
}
return r.toString();
}

// 混合
private static String mix1(Graph g) {
StringBuilder r = new StringBuilder();
LinkedList<Integer>[] adj = g.adj;
boolean[] vt = new boolean[adj.length];
@SuppressWarnings("unchecked")
LinkedList<Integer>[] inv = new LinkedList[adj.length];
for (int i = 0; i < inv.length; i++) {
inv[i] = new LinkedList<Integer>();
}
for (int i = 0; i < adj.length; i++) {
// 构建反向邻接表
for (int j = 0; j < adj[i].size(); j++) {
inv[adj[i].get(j)].add(i);
}
}

LinkedList<Integer> help = new LinkedList<>();
// 必须存在0引用的顶点 否则就是存在环的情况
for (int i = 0; i < inv.length; i++) {
if (inv[i].size() != 0)
help.add(i);
}
while (!help.isEmpty()) {
Integer v = help.remove();
if (vt[v]) {
continue;
}
vt[v] = true;
dfs(v, inv, vt, r);
}
// 没有遍历到的顶点
for (int i = 0; i < vt.length; i++) {
if (!vt[i]) {
r.append(i + ",");
}
}
return r.toString();
}

// 深度优先
// 核心思想就是把先于当前执行的节点先先执行 然后在输出自己
private static String dfs2(Graph2 g) {
LinkedList<Integer>[] adj = g.adj;
boolean[] v = new boolean[adj.length];
StringBuilder r = new StringBuilder();
for (int i = 0; i < adj.length; i++) {
if (!v[i]) {
v[i] = true;
dfs(i, adj, v, r);

}
}
return r.toString();
}

// 深度优先
// 核心思想就是把先于当前执行的节点先先执行 然后在输出自己
private static void dfs(int ix, LinkedList<Integer>[] adj, boolean[] v, StringBuilder r) {
for (int i = 0; i < adj[ix].size(); i++) {
Integer w = adj[ix].get(i);
if (!v[w]) {
v[w] = true;
dfs(w, adj, v, r);
}
}
r.append(ix + ",");
}

// 深度优先
// 核心思想就是把先于当前执行的节点先先执行 然后在输出自己
@SuppressWarnings("unchecked")
private static String dfs1(Graph g) {
LinkedList<Integer>[] adj = g.adj;
boolean[] v = new boolean[adj.length];
LinkedList<Integer>[] inv = new LinkedList[adj.length];
StringBuilder r = new StringBuilder();
for (int i = 0; i < inv.length; i++) {
inv[i] = new LinkedList<Integer>();
}
for (int i = 0; i < adj.length; i++) {
// 构建反向邻接表
for (int j = 0; j < adj[i].size(); j++) {
inv[adj[i].get(j)].add(i);
}
}
for (int i = 0; i < inv.length; i++) {
if (!v[i]) {
v[i] = true;
dfs(i, inv, v, r);

}
}
return r.toString();
}

public static int random(int max) {
return ((int) (Math.random() * max));
}

// 被依赖的列表
@SuppressWarnings("unchecked")
static class Graph {
// private int v; // 顶点的个数
private LinkedList<Integer> adj[]; // 邻接表 被依赖的列表

public Graph(int v) {
// this.v = v;
adj = new LinkedList[v];
for (int i = 0; i < v; ++i) {
adj[i] = new LinkedList<>();
}
}

public void addEdge(int s, int t) {
// s 先于 t,边 s->t
// 就是说s要先于t
adj[s].add(t);
}
}

// 依赖的列表
@SuppressWarnings("unchecked")
static class Graph2 {
// private int v; // 顶点的个数
private LinkedList<Integer> adj[]; // 邻接表 依赖的列表

public Graph2(int v) {
// this.v = v;
adj = new LinkedList[v];
for (int i = 0; i < v; ++i) {
adj[i] = new LinkedList<>();
}
}

public void addEdge(int s, int t) { // s 先于 t,边 t->s
adj[s].add(t);
}
}
}

迪杰斯特拉算法

计算最短距离的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
package algorithm.senior.dijkstra;

import java.util.LinkedList;

//迪杰斯特拉算法
public class DijkstraTest {
public static void main(String[] args) {
int l = 10;
Graph g = new Graph(l);
for (int i = 0; i < 2 * l; i++) {
int s = random(l);
int t = random(l);
while (t == s) {
t = random(l);
}
int w = random(l) + 1;
g.addEdge(s, t, w);
g.addEdge(t, s, w);
System.out.println(s + "->" + t + ",distance=" + w);
}
int s = random(l);
int t = random(l);
while (t == s) {
t = random(l);
}
System.out.println("find:" + s + "->" + t);
dijkstra(g, s, t);

}

public static int random(int max) {
return ((int) (Math.random() * max));
}

public static void dijkstra(Graph g, int s, int t) {
LinkedList<Edge>[] adj = g.adj;// 所有边
// 初始化队列
PriorityQueue q = new PriorityQueue(g.v);
int[] p = new int[g.v];// 距离当前节点最近的前驱节点
Vertex[] vs = new Vertex[g.v];// 到每个点的距离
for (int i = 0; i < vs.length; i++) {
vs[i] = new Vertex(i, Integer.MAX_VALUE);
}
vs[s].dist = 0;// 到起点零距离
q.add(vs[s]);
boolean flag = false;
while (!q.isEmpty()) {
// 每次取的都是最短距离的点
// 所以 离当前起点越近的点就会越先处理
// 所以当这个点就是目标节点的时候 就可以结束跳出了
Vertex vt = q.poll();
// 找到
if (vt.id == t) {
flag = true;
break;
}
// 当前最短节点的所有边
LinkedList<Edge> list = adj[vt.id];
for (int i = 0; i < list.size(); i++) {
Edge e = list.get(i);// 边
// 计算起点从起始节点到这个边的目标节点的距离
int distNew = vs[e.sid].dist + e.w;
if (distNew < vs[e.tid].dist) {
// 判断之前到这个边的目标节点是否比从当前节点走更近
// 更近就更新距离、队列、前驱节点
vs[e.tid].dist = distNew;
q.updateOrAdd(vs[e.tid]);// 更新或者新增
p[e.tid] = vt.id;
}

}
}
if (flag) {
LinkedList<Integer> path = new LinkedList<Integer>();
int idx = t;
while (idx != s) {
path.add(idx);
idx = p[idx];

}
System.out.println("找到->");
path.add(idx);
for (int i = path.size() - 1; i >= 0; i--) {
System.out.print(path.get(i) + ",");
}
} else {
System.out.print("没找到");
}
System.out.println();
}

// 因为 Java 提供的优先级队列,没有暴露更新数据的接口,所以我们需要重新实现一个
public static class PriorityQueue { // 根据 vertex.dist 构建小顶堆
private Vertex[] nodes;
private int count;
private int size = 0;

public void print() {
int max = 1;
int left = 1;
for (int i = 1; i <= size; i++) {
System.out.print(nodes[i].dist + " ");
left--;
if (left == 0) {
System.out.println();
left = max = max * 2;
}

}
System.out.println("");
System.out.println("=======");
}

public PriorityQueue(int v) {
this.nodes = new Vertex[v + 1];
this.count = v;
}

// 弹出首个
public Vertex poll() {
// 交换首尾节点
swap(1, size--);
// 从堆顶向下重新堆化
buildDown(1);
return nodes[size + 1];
}

// 新增
public void add(Vertex v) {
if (size == count) {// 满
return;
}
// 放到最后一个节点去 然后和父节点进行比较
nodes[++size] = v;
buildUp(size);
}

// 交换
private void swap(int i, int j) {
Vertex temp = nodes[i];
nodes[i] = nodes[j];
nodes[j] = temp;
}

// 从下向上重新堆化
private void buildUp(int idx) {
while (idx / 2 > 0 && nodes[idx].dist < nodes[idx / 2].dist) {
swap(idx, idx / 2);// 交换
idx = idx / 2;
}
}

// 从上向下重新堆化
private void buildDown(int idx) {
int m = idx;
// left
if (idx * 2 <= size && nodes[idx].dist > nodes[idx * 2].dist) {
m = idx * 2;
}
// right
if (idx * 2 + 1 <= size && nodes[m].dist > nodes[idx * 2 + 1].dist) {
m = idx * 2 + 1;
}
// 是否有变化
if (m != idx) {
swap(idx, m);
buildDown(m);
}

}

private int findNode(Vertex v, int idx) {
if (nodes[idx] == v) {
return idx;
}
// 左节点
if (idx * 2 <= size) {
int f = findNode(v, idx * 2);
if (f != -1) {
return f;
}
}
// right
if (idx * 2 + 1 <= size) {
int f = findNode(v, idx * 2 + 1);
if (f != -1) {
return f;
}
}
return -1;
}

// 更新结点的值,并且从下往上堆化,重新符合堆的定义。时间复杂度 O(logn)。
// 如果没有节点 就进行新增
public void updateOrAdd(Vertex v) {
// 首先找到节点的索引
int idx = findNode(v, 1);
if (idx == -1) {
// 不存在
add(v);// 不存在就进行新增
return;
}
//////////////////////////////
// 值如果变小 只需要向上重新堆化就可以了
// 如果是值变大了 就只需要往下堆化即可
// 也可以向上堆化和向下堆化都写,而实际的时候只会执行其中一个
///////////////////////////////

// 节点向下堆化
buildDown(idx);
// 从节点向上重新堆化
buildUp(idx);

}

// 更新结点的值,并且从下往上堆化,重新符合堆的定义。时间复杂度 O(logn)。
public void update(Vertex v) {
// 首先找到节点的索引
int idx = findNode(v, 1);
if (idx == -1) {
// 不存在
return;
}
//////////////////////////////
// 值如果变小 只需要向上重新堆化就可以了
// 如果是值变大了 就只需要往下堆化即可
// 也可以向上堆化和向下堆化都写,而实际的时候只会执行其中一个
///////////////////////////////

// 节点向下堆化
buildDown(idx);
// 从节点向上重新堆化
buildUp(idx);

}

public boolean isEmpty() {
return size < 1;
}

}

public static class Graph { // 有向有权图的邻接表表示
private LinkedList<Edge> adj[]; // 邻接表
private int v; // 顶点个数

public Graph(int v) {
this.v = v;
this.adj = new LinkedList[v];
for (int i = 0; i < v; ++i) {
this.adj[i] = new LinkedList<>();
}
}

public void addEdge(int s, int t, int w) { // 添加一条边
this.adj[s].add(new Edge(s, t, w));
}
}

public static class Edge {
public int sid; // 边的起始顶点编号
public int tid; // 边的终止顶点编号
public int w; // 权重

public Edge(int sid, int tid, int w) {
this.sid = sid;
this.tid = tid;
this.w = w;
}
}

// 下面这个类是为了 dijkstra 实现用的
public static class Vertex {
public int id; // 顶点编号 ID
public int dist; // 从起始顶点到这个顶点的距离

public Vertex(int id, int dist) {
this.id = id;
this.dist = dist;
}
}

}

位图

省内存的 保存boolean类型的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
package algorithm.senior.bitmap;

import java.util.Arrays;
import java.util.BitSet;
import java.util.Vector;

public class BitMapTest {

public static void main(String[] args) {
int size = 10;
int max = 20;
int[] n = new int[size];
for (int i = 0; i < n.length; i++) {
n[i] = random(max);
}
System.out.println(Arrays.toString(n));
sort(n, max);
}

public static int random(int max) {
return ((int) (Math.random() * max));
}

// 假设我们有 1 亿个整数,数据范围是从 1 到 10 亿,如何高效省内存的排序?
public static void sort(int[] n, int max) {
BitMap bm = new BitMap(max + 1);
for (int i = 0; i < n.length; i++) {
bm.setTrue(n[i]);
}
//如果有重复数字就会丢失
for (int i = 0; i < max; i++) {
if (bm.get(i)) {
System.out.print(i + ",");
}
}
System.out.println();
}

public static class BitMap {
private int[] bytes;// c
private int size = 0;

public BitMap(int size) {
this.size = size;
bytes = new int[size / 32 + 1];
}

public void print() {
for (int i = 0; i < size; i++) {
String s = get(i) ? "1" : "0";
System.out.print(s + ",");
}
System.out.println();
}

public void setTrue(int idx) {
int arrIdx = idx / 32;// 数组索引
int byteIdx = idx % 32;// byte索引
// 二进制的或计算
bytes[arrIdx] |= (1 << byteIdx);
}

public void setFalse(int idx) {
int arrIdx = idx / 32;// 数组索引
int byteIdx = idx % 32;// byte索引
// 索引反码 然后与计算
bytes[arrIdx] &= (~(1 << byteIdx));
}

public boolean get(int idx) {
int arrIdx = idx / 32;// 数组索引
int byteIdx = idx % 32;// byte索引
// 二进制的与计算
// (1 << byteIdx))的二进制只有byteIdx位置会是1 其它都是0
// 所以结果如果为0 就是false 其它情况则是true
return (bytes[arrIdx] & (1 << byteIdx)) != 0;

}
}
}

Review 英文文章

https://blog.mybatis.org/

mybatis 的更新。

Tip 技巧

极客时间的专栏《数据结构与算法之美》

本周继续学习该专栏中的43-45篇

主要内容为:

拓扑排序:有依赖顺序的一些数据 来进行排序

最短路径:迪杰斯特拉算法,应该算是动态规划的一种算法,每次获取最短路径进行计算。

位图:省内存的保存boolean类型数据的结构。

布隆过滤器:可以基于位图来实现,通过多个hash算法保存。但有一定的误判的可能性

Share 分享

版权声明:本文为CSDN博主「JamesFen」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/jameshadoop/article/details/35276083

某个医院早上收了六个门诊病人,如下表。
  症状  职业   疾病
  打喷嚏 护士   感冒
  打喷嚏 农夫   过敏
  头痛  建筑工人 脑震荡
  头痛  建筑工人 感冒
  打喷嚏 教师   感冒
  头痛  教师   脑震荡
现在又来了第七个病人,是一个打喷嚏的建筑工人。请问他患上感冒的概率有多大?
根据贝叶斯定理:
P(A|B) = P(B|A) P(A) / P(B)
可得
P(感冒|打喷嚏x建筑工人)
= P(打喷嚏x建筑工人|感冒) x P(感冒)/ P(打喷嚏x建筑工人)
(假定”打喷嚏”和”建筑工人”这两个特征是独立的,因此,上面的等式就变成了)
= P(打喷嚏|感冒) x P(建筑工人|感冒) x P(感冒) / P(打喷嚏) x P(建筑工人)  
= 感冒总数里打喷嚏的占比 x 感冒总数里建筑工人的占比 x 全部总数里感冒的占比 / 全部总数里打喷嚏的占比
全部总数里建筑工人的占比
= 0.66 x 0.33 x 0.5 / 0.5 x 0.33
= 0.66
因此,这个打喷嚏的建筑工人,有66%的概率是得了感冒。

=================

下雨:0,0,1,0,1,1,0,1,0,1,
上课:1,1,1,0,1,1,0,0,0,0,
总数:10
下雨总数:5

上课数:5

下雨上课:3,下雨上课占比:0.3
下雨不上课:2,下雨不上课占比:0.2
不下雨上课:2,不下雨上课占比:0.2

不下雨不上课:3,不下雨不上课占比:0.3

下雨天的时候
下雨上课占比:0.6

下雨不上课占比:0.4

根据贝叶斯定理:
P(A|B) = P(B|A) P(A) / P(B)
P(结果|条件)= P(条件|结果)
P(结果)/ P(条件)
P(上课|下雨)= P(下雨|上课) P(上课)/ P(下雨)
=(下雨上课总数/上课总数)
(上课总数占比)/ (下雨总数占比)
=0.6*0.5/0.5
=0.6
所以下雨上课概率=60%