-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
1113 lines (1113 loc) · 263 KB
/
search.xml
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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>2021年1月编译原理考题汇总</title>
<url>/2021%E5%B9%B41%E6%9C%88%E7%BC%96%E8%AF%91%E5%8E%9F%E7%90%86%E8%80%83%E9%A2%98%E6%B1%87%E6%80%BB.html</url>
<content><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在编译原理的复习过程中,参考了许多前辈总结的考题,获益匪浅,在此向他们表示感谢。趁现在对考题仍有几分记忆,做下记录,希望帮助到学弟学妹们。</p>
<h2 id="考试题型"><a href="#考试题型" class="headerlink" title="考试题型"></a>考试题型</h2><p>考试题型近几年都较为固定,分为以下几种:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210214162929530.png" alt="image-20210214162929530"></p>
<a id="more"></a>
<h2 id="2021年1月试题汇总"><a href="#2021年1月试题汇总" class="headerlink" title="2021年1月试题汇总"></a>2021年1月试题汇总</h2><h3 id="一、基础知识题"><a href="#一、基础知识题" class="headerlink" title="一、基础知识题"></a>一、基础知识题</h3><p>判断该正则表达式文法是否正确?如不正确应该如何进行改写?</p>
<script type="math/tex; mode=display">S->RE| RE"|" RF | RE·RF |(RE)|RE^*|RE^+</script><h3 id="二、正则表达式-gt-DFA分析题"><a href="#二、正则表达式-gt-DFA分析题" class="headerlink" title="二、正则表达式->DFA分析题"></a>二、正则表达式->DFA分析题</h3><p>请画出 C++ 语言中单词的DFA图,并写出对应的词法分析程序。</p>
<h3 id="三、自顶向下分析设计题"><a href="#三、自顶向下分析设计题" class="headerlink" title="三、自顶向下分析设计题"></a>三、自顶向下分析设计题</h3><p>请写出 C 语言中 if-else 语句的文法,并用语法树为其生成四元组,写出该语法树的程序。</p>
<h3 id="四、LR分析题"><a href="#四、LR分析题" class="headerlink" title="四、LR分析题"></a>四、LR分析题</h3><p>写出判断一个文法是否为 SLR(1) 文法的程序。</p>
<h3 id="五、语义分析题"><a href="#五、语义分析题" class="headerlink" title="五、语义分析题"></a>五、语义分析题</h3><p>小明发明了一种新的类型声明语句,形式如下,请你写出其对应的文法及语义分析程序。</p>
<figure class="highlight c++"><table><tr><td class="code"><pre><span class="line">i:<span class="keyword">float</span></span><br><span class="line">i,j,k:<span class="keyword">int</span></span><br></pre></td></tr></table></figure>
<h3 id="六、综合分析设计题-矩阵连乘"><a href="#六、综合分析设计题-矩阵连乘" class="headerlink" title="六、综合分析设计题 (矩阵连乘)"></a>六、综合分析设计题 (矩阵连乘)</h3><p>当有多个矩阵进行运算时,各个矩阵之间的运算顺序可影响到最终的运算次数。如 A 为 50×20 的矩阵,B为 20×5 的矩阵,C为 5×30 的矩阵,那么 (AB)C 的运算次数为12500次,A(BC) 的运算次数为33000次,下面请你用编译原理的方法完成这一类式子的计算和判别功能,写出相关的思路和代码,要求的输入输出格式如下:</p>
<p><strong>注意:不用编译原理相关的方法最多得3分!!</strong></p>
<figure class="highlight plain"><table><tr><td class="code"><pre><span class="line">Input:</span><br><span class="line">(ABC</span><br><span class="line">(AB)C</span><br><span class="line">A(BC)</span><br><span class="line"></span><br><span class="line">Output:</span><br><span class="line">No</span><br><span class="line">12500</span><br><span class="line">33000</span><br></pre></td></tr></table></figure>
<h2 id="写在后面"><a href="#写在后面" class="headerlink" title="写在后面"></a>写在后面</h2><p>本次汇总是根据回忆所写出来的,因此难免有疏漏或错误。如各位在观看时对汇总的题目有不解,或是发现了错误,请通过我的邮箱<strong>mail#charfole.top(#换成@)</strong>联系我,感谢!</p>
]]></content>
<categories>
<category>课内学习</category>
</categories>
<tags>
<tag>课程</tag>
<tag>编译原理</tag>
</tags>
</entry>
<entry>
<title>Hello World</title>
<url>/Hello-World.html</url>
<content><![CDATA[<p> 进入大学后,我直观地感受到了这里的知识体系远比高中庞大。此外,高中学习时我们常有充足的时间与精力来梳理碎片化的知识,构建自己的学习框架,而在大学做好这一点并不容易。</p>
<p> 因此,身为CS学子的我,决定用博客记录下自己的学习过程、思考感悟、和一些观影与读书的感受,希望能以这一种分享的方式来对所学所闻进行总结、分享,同时也期望能够在这广阔的互联网中,留下一些属于自己的印记。</p>
]]></content>
<categories>
<category>思考与感悟</category>
</categories>
<tags>
<tag>博客</tag>
</tags>
</entry>
<entry>
<title>RECLE 阅读笔记</title>
<url>/RECLE-Notes.html</url>
<content><![CDATA[<h3 id="Paper-Information"><a href="#Paper-Information" class="headerlink" title="Paper Information"></a>Paper Information</h3><hr>
<p><strong>Title</strong>: Representation Learning from Limited Educational Data with Crowdsourced Labels<br><strong>Links</strong>: <span class="exturl" data-url="aHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzIwMDkuMTEyMjI=">https://arxiv.org/abs/2009.11222<i class="fa fa-external-link-alt"></i></span><br><strong>Date</strong>: 2020.09.23<br><strong>Comments</strong>: IEEE Transacitons on Knowledge and Data Engineering (TKDE\CCF-A)<br><strong>Subjects</strong>: ML、AI<br><strong>Index Terms</strong>: Representation learning, crowdsourcing, hard example mining, educational data.<br><strong>Authors</strong>: Wentao Wang, Guowei Xu, Wenbiao Ding, Gale Yan Huang, Guoliang Li, Jiliang Tang, Zitao Liu<br><a id="more"></a></p>
<h3 id="Notes"><a href="#Notes" class="headerlink" title="Notes"></a>Notes</h3><hr>
<h3 id="Abstract"><a href="#Abstract" class="headerlink" title="Abstract"></a>Abstract</h3><ul>
<li>提出了一个以分组数据为基础的深度神经网络来从有限的数据中生成更多训练样本来学习嵌入。</li>
<li>提供了一个贝叶斯置信度预估模型来捕捉众包标签中的不一致现象。</li>
<li>提出了一个困难例子选择流程来自动选取被错误分类的训练样本(通过找验证集中错误的部分来计算寻找和训练集相似的元素)。</li>
<li>将模型运行在三个真实的数据集中、并提供了综合分析。</li>
</ul>
<hr>
<h3 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h3><ul>
<li>小样本学习和不一致众包标签的研究都非常多,然而研究用小样本和不一致的众包标签来进行表征学习的却非常少,更不要说只针对教育领域的研究。</li>
<li>提出的三个问题:<ul>
<li>如何充分利用有限且不一致的众包标签?</li>
<li>如何建立一个众包标签的表征学习框架?</li>
<li>如何让这个学习过程更加高效?</li>
</ul>
</li>
<li>主要贡献<ul>
<li>提出了一个高效的特征学习框架来从有限和不一致的数据中学习。</li>
<li>一个困难例子挖掘策略让训练过程动态地在每个迭代中选取最难训练的例子。</li>
<li>利用该模型对三个教育数据集进行了实验,并和不同的策略进行对比。</li>
</ul>
</li>
</ul>
<hr>
<h3 id="Related-Work"><a href="#Related-Work" class="headerlink" title="Related Work"></a>Related Work</h3><ul>
<li>目前的小样本学习主要是针对非常少的而且是无噪声的样本,但本文的研究对象是较少的众包标签样本,因此不适用。</li>
<li>目前的众包标签学习模型都是用于解决数据的噪声和不一致性,但是当样本较少时则不具有泛用性。</li>
<li>在困难例子挖掘中没有分析当前的弊端,而是直接阐述了他们的策略。</li>
</ul>
<hr>
<h3 id="Problem-Statement-And-Notations"><a href="#Problem-Statement-And-Notations" class="headerlink" title="Problem Statement And Notations"></a>Problem Statement And Notations</h3><h4 id="Notations"><a href="#Notations" class="headerlink" title="Notations"></a>Notations</h4><p>对于每个样本 $x<em>i$,我们假设其标签集 $y_i$ 被 $d$ 个工人打上了二分类标签,其中 $y_i =[y</em>{i,1},y<em>{i,2},…,y</em>{i,d}]$, 其中 $y_{i,l} \in \lbrace{0,1}\rbrace$,$l = [ 1,2,…,d\space]$。 </p>
<p>以下是具体的公式符号约束:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201104194239150.png" alt="image-20201104194239150"></p>
<h4 id="Crowdsourced-Label-Confidence"><a href="#Crowdsourced-Label-Confidence" class="headerlink" title="Crowdsourced Label Confidence"></a>Crowdsourced Label Confidence</h4><p>其中,有一点特别需要注意,对于每一个样本 $x_i$,它也许会同时对应着多个标签,同一个正样本可能由不同个数的 1 组成,因此我们需要把这种差异考虑进去,将其定义为众包标签的置信度。</p>
<h4 id="Hard-Example"><a href="#Hard-Example" class="headerlink" title="Hard Example"></a>Hard Example</h4><p>我们称 $x_i$ 为一个困难样本,当它和验证集中某个被错分类的样本 $v_i$ 最为相近,这种样本可能在应用不同选择策略时出现不一样的样本,论文中默认选择余弦函数来计算 $x_i$ 和 $v_i$ 之间的距离。</p>
<hr>
<h3 id="The-Proposed-Framework"><a href="#The-Proposed-Framework" class="headerlink" title="The Proposed Framework"></a>The Proposed Framework</h3><h4 id="Grouping-Strategy"><a href="#Grouping-Strategy" class="headerlink" title="Grouping Strategy"></a>Grouping Strategy</h4><p>从 $X^+$ 中选择两个正样本,从$X^-$中选取 $S-2$ 个负样本,将这 $S$ 个样本作为一组。<br>那么有 $g<em>j=\langle{x^+</em>{a^j<em>1}, x^+</em>{a^j<em>2}, x^-</em>{a^j<em>3},…,x^-</em>{a^j_S}}\rangle$。对于整个训练组,有 $g=\lbrace{g_1,g_2,…,g_k}\rbrace$。</p>
<p>对于每一组$g_j$,我们都将其作为深层神经网络(DNN) 的输入,从而获取到低维度的输出向量。在 DNN 中我们通过多层全连接非线性投影来学习特征。</p>
<h4 id="Model-Learning"><a href="#Model-Learning" class="headerlink" title="Model Learning"></a>Model Learning</h4><p>我们定义一个后验概率为给定 $x^+<em>{a^j_1}$,计算 $x^+</em>{a^j_2}$ 出现的概率,这个概率通过余弦函数和Softmax函数组成,其定义如下图所示:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201104215115957.png" alt="image-20201104215115957"></p>
<p>$z$ 代表的是学习后的集合,$r$ 代表的是余弦函数,$exp$ 为指数函数,$η$ 则为Softmax函数的超参数,为了使我们模型预测的向量更准确,应该让正样本在嵌入空间中尽量靠近,并且让正负样本尽量远离。因此,我们最大化这一个后验概率便可达到此目的。为了让模型使这个概率尽量变大,因此我们要定义一个损失函数,当概率越大时,损失函数越小,模型的目的便是最小化这个损失函数。损失函数定义如下:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201104215803616.png" alt="image-20201104215803616"></p>
<h4 id="Bayesian-Confidence-Estimation"><a href="#Bayesian-Confidence-Estimation" class="headerlink" title="Bayesian Confidence Estimation"></a>Bayesian Confidence Estimation</h4><p>让 $δ<em>i$ 为某个样本众包标签的置信度,一个常规的方法是将其看作一个符合伯努利分布的样本来计算其置信度。即: ${\hatδ^{Bernoulli}_i}=\sum</em>{j=1}^{d} {y_{i,j}/d}$ 。</p>
<p>但由于在当前的问题中,我们的众包标签数目十分有限,因此用上述方法来进行极大似然估计会有欠佳的表现,因此采用 Beta 分布来构造置信度。即:${\hatδ<em>i}= \frac{α+\sum</em>{j=1}^{d}y_{i,j}}{α+β+d}$。</p>
<p>在加入了置信度估计后,目标函数更新为:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201105101633351.png" alt="image-20201105101633351"></p>
<h4 id="Adaptive-Hard-Example-Selection"><a href="#Adaptive-Hard-Example-Selection" class="headerlink" title="Adaptive Hard Example Selection"></a>Adaptive Hard Example Selection</h4><p>该过程可分以下几步进行描述:</p>
<ol>
<li>在每个训练迭代中,根据该次迭代的参数来对验证集进行评估,并将错分类的验证集加入到集合 $V_{miss}(t)$ 中。</li>
<li>对于 $V<em>{miss}(t)$ 中的每个样本,我们选取一个和其距离最近的训练样本,这个距离使用余弦函数来进行评判,最终得到 $X</em>{hard}(t)$ 。</li>
<li>类似地,我们将 $X<em>{hard}(t)$ 进行分组,得到分组后的集合$g</em>{hard}(t)$ 。</li>
<li>在 $(t+1)$ 次迭代,我们将训练组 $g(t+1)$ 定义为 $g(t+1)=\lbrace{g<em>{base},g</em>{hard}(t)}\rbrace$,其中 $g_{base}$ 就代表着原始的分组训练集。</li>
</ol>
<p>该迭代过程将一直重复到预测结果收敛或达到最大迭代次数才结束。</p>
<p>经过这几步的介绍后,我们可以得到模型的架构图:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201105212757171.png" alt="image-20201105212757171"></p>
<h4 id="The-Representation-Learning-Algorithm"><a href="#The-Representation-Learning-Algorithm" class="headerlink" title="The Representation Learning Algorithm"></a>The Representation Learning Algorithm</h4><p>模型的训练过程可被总结如下:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201105150344301.png" alt="image-20201105150344301"></p>
<p>此外,还有两个训练小技巧需要特别提醒一下:</p>
<ul>
<li>在前五轮的训练中,暂不启用Hard Example Selection,确保模型有一个较为稳定的开始。</li>
<li>此外,为了在每次训练中有一个稳定的表现,当测试集的正确率小于0.7,便在这次迭代中不使用Hard Example Selection策略,而使用基础的分组训练集来代替。</li>
</ul>
<hr>
<h3 id="Experiments"><a href="#Experiments" class="headerlink" title="Experiments"></a>Experiments</h3><h4 id="Experimental-Settings"><a href="#Experimental-Settings" class="headerlink" title="Experimental Settings"></a>Experimental Settings</h4><p>用于训练模型的三个数据集的信息如下:</p>
<ul>
<li><strong>fluency-1&2</strong>:5位标注者对743个1、2年级孩子回答数学问题的音频流利程度进行评判,即进行二分类,0代表不流利,1代表流利。</li>
<li><strong>fluency-4&5</strong>:类似于上面的数据,收集了1965个4、5年级的音频数据,请了11个标注者进行标注。</li>
<li><strong>preschool</strong>:从学前儿童的演讲比赛中收集了1767个演讲片段,每一段被11个标注者评分。</li>
</ul>
<p>随后,将三个数据集随机打乱为三个集合,即训练集、校验集和测试集。测试集的数据邀请专家来标注,作为评价的基准。由于原始的数据集均由音频组成,因此需要进行特征提取才能够交付模型来训练。</p>
<h4 id="Raw-Feature-Extraction"><a href="#Raw-Feature-Extraction" class="headerlink" title="Raw Feature Extraction"></a>Raw Feature Extraction</h4><p>对于原始数据集,我们分两类特征进行抽取,第一类为韵律特征,第二类为语言特征。韵律特征有音量、信号能量、梅尔频率等,可以通过一些工具 (<span class="exturl" data-url="aHR0cHM6Ly93d3cuYXVkZWVyaW5nLmNvbS9vcGVuc21pbGUv">OpenSMILE<i class="fa fa-external-link-alt"></i></span>) 进行抽取。通过演讲自动识别模型来获取语义特征,如:中间词的数量、重复的次数等。</p>
<p>最终,抽取后的特征可被分成以下几类:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201105154216236.png" alt="image-20201105154216236"></p>
<p>其中,前两个数据集的特征均为语义特征,而最后一个数据集的特征为韵律特征。</p>
<h4 id="Baselines"><a href="#Baselines" class="headerlink" title="Baselines"></a>Baselines</h4><p>下面将数据集试验于三种不同的 Baseline。</p>
<ul>
<li>流行的分类模型(数据集的众包标签使用众数决定)<ul>
<li>LR</li>
<li>GBDT</li>
<li>SVM</li>
</ul>
</li>
<li>有限样本的特征学习模型(数据集的众包标签使用众数决定)<ul>
<li>SiameseNet</li>
<li>FaceNet</li>
<li>RelationNet</li>
</ul>
</li>
<li>基于有限众包标签样本的特征学习模型<ul>
<li>RLL-Bayesian</li>
<li>RECLE(本论文提出的模型)</li>
</ul>
</li>
</ul>
<h4 id="Experimental-Results"><a href="#Experimental-Results" class="headerlink" title="Experimental Results"></a><strong>Experimental Results</strong></h4><p>对于第二第三组 Baseline,我们用线性回归对其学习到的特征进行二分类。对于第一组,我们直接采用原始的数据来训练评估。下面是这三组模型在第三组数据集的表现:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201105214206707.png" alt="image-20201105214206707"></p>
<p>我们可以得到以下几点结论:</p>
<ol>
<li>第二第三组模型拥有Grouping-based Strategy,因此可以获得更充足的数据,有着更好的表现。</li>
<li>因为第一第二组的模型都需要对标签进行推测,而没有引入置信度估计,因此会导致一些信息损失。</li>
<li>所有的模型在前两个数据集中都表现得更好,推测其原因是因为最后一个数据集的韵律特征没有语义特征有代表性。</li>
</ol>
<h4 id="Evaluation-of-Learned-Representations"><a href="#Evaluation-of-Learned-Representations" class="headerlink" title="Evaluation of Learned Representations"></a>Evaluation of Learned Representations</h4><p>为了能够更直观地观察模型学习到的特征是否有效,我们对原始数据和学习后的数据作图显示,可以发现在学习后的嵌入空间中,标签相同的数据距离也靠得更近,更有利于分类模型的学习。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20201105215404489.png" alt="image-20201105215404489"></p>
<h4 id="Impact-of-Different-Distance-Metrics"><a href="#Impact-of-Different-Distance-Metrics" class="headerlink" title="Impact of Different Distance Metrics"></a>Impact of Different Distance Metrics</h4><p>这部分采取了三种不同的距离来用于模型的置信度估计(其他的参数设定保持一致),经实验发现,余弦距离在一、二数据集上表现更优,即语义特征,而欧式距离则在韵律特征上表现更好。</p>
<h4 id="Impact-of-Imbalanced-Data-Distribution"><a href="#Impact-of-Imbalanced-Data-Distribution" class="headerlink" title="Impact of Imbalanced Data Distribution"></a>Impact of Imbalanced Data Distribution</h4><p>这部分阐述了将原始的 Grouping Strategy 换为了选取 S-2 个负样本和 2 个正样本,其他的参数保持不变,之后在第二个数据集上进行实验,发现结果和原来的策略类似。</p>
<hr>
<h3 id="Conclusion-And-Future-Work"><a href="#Conclusion-And-Future-Work" class="headerlink" title="Conclusion And Future Work"></a>Conclusion And Future Work</h3><p>这篇论文主要解决了有限的众包标签数据问题。为了解决小样本数据带来的不一致性与稀缺性,提出了以下框架来解决三个问题:</p>
<ol>
<li>自动组成样本组,扩充神经网络的训练数据。</li>
<li>引入了众包标签置信度评估模型来对不一致性进行衡量。</li>
<li>自动选择训练集中较难训练的样本来让训练过程更充分高效。</li>
</ol>
<p>为了评估我们的模型,我们将模型与众多其他的 Baseline 作对比,实验证明我们的模型确实能够解决众包标签有限和不一致的问题,同时学习到高效的嵌入信息。</p>
<p>在未来,作者计划从三方面继续开展我们的工作:</p>
<ol>
<li>计划将众包工人得个人信息融入到模型中。</li>
<li>计划将学习更多的高级技巧来将训练组从不同的训练阶段中进行结合,从而改善模型预测准确率。</li>
<li>计划将对其他有限的众包标签数据进行更多的实验,比如癌症诊断问题和生物医药图像问题等。</li>
</ol>
<h3 id="Reproduction"><a href="#Reproduction" class="headerlink" title="Reproduction"></a>Reproduction</h3><p>论文作者已提供了该研究的<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3RhbC1haS9SRUNMRQ==">代码<i class="fa fa-external-link-alt"></i></span>,但在克隆到本地后进行实验时仍有一些问题。在阅读其代码结构后,本人对其进行debug,并已将论文的核心模型——RECLE在本地跑通,并在代码中加入了注释以便理解,详情请见<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NoYXJmb2xlL1JFQ0xFLVJlcHJvZHVjdGlvbg==">仓库<i class="fa fa-external-link-alt"></i></span>。</p>
]]></content>
<categories>
<category>科研</category>
</categories>
<tags>
<tag>Paper</tag>
<tag>Notes</tag>
</tags>
</entry>
<entry>
<title>大三上数据挖掘项目总结</title>
<url>/SCNU-CS-2018-DataMining.html</url>
<content><![CDATA[<h2 id="SCNU-CS-2018-DataMining"><a href="#SCNU-CS-2018-DataMining" class="headerlink" title="SCNU-CS-2018-DataMining"></a>SCNU-CS-2018-DataMining</h2><p>这一次整理的是数据挖掘课程项目,分为平时项目与期末项目。以小组为单位,课程要求使用数据挖掘方法进行数据分析和建模,最终需要提交代码、论文,期末项目还需要进行小组汇报,具体的内容请参见 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NoYXJmb2xlL1NDTlUtQ1MtMjAxOC1EYXRhTWluaW5n">Github<i class="fa fa-external-link-alt"></i></span>。</p>
<h2 id="项目结构"><a href="#项目结构" class="headerlink" title="项目结构"></a>项目结构</h2><p>Project-1为平时项目,Project-2为期末项目,项目结构与具体的使用方法请参照下面的内容。<br><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/项目结构图.png" alt="项目结构图"></p>
<a id="more"></a>
<h2 id="项目一简介"><a href="#项目一简介" class="headerlink" title="项目一简介"></a>项目一简介</h2><p><strong>客户流失管理</strong>:根据多个客户的数据,利用数据挖掘方法进行建模,使模型能够通过用户的信息来预测用户是否已经流失(二分类问题:1为流失、0为无流失)。在这个项目中,你需要使用决策树(预剪枝和后剪枝)、多层感知机、逻辑回归、决策树来进行实验,提交每个模型的混淆矩阵,并在最终的报告中呈现各个模型的性能。</p>
<h2 id="项目二简介"><a href="#项目二简介" class="headerlink" title="项目二简介"></a>项目二简介</h2><p><strong>抑郁症 HAMD 评分预测</strong>:数据集“data_depression.xlsx”包含两个数据表单:“抑郁脑电图” 和 “针刺效益、前后脑电图”。表单 “抑郁症脑电图” 字段有姓名、性别、年龄、HAMD评分、以及针刺前脑电节律波幅指数和节律指数。表单“针刺效益、前后脑电图”字段主要有姓名、针刺前脑电指标和针刺后脑电指标。<strong>其中,表单 ”针刺效益、前后脑电图“ 中患者只是表单抑郁症脑电图中患者的一部分。</strong>根据17-item HAMD抑郁症评价量表,字段HAMD评分是患者抑郁症程度评价。</p>
<p><strong>提示</strong>:在预处理中需要将同一个患者的数据拼接成一行,表单一中的数据为针刺前的脑电指数,有 HAMD 评分,可以进行有监督学习;表单二中加入了部分患者无 HAMD 评分的针刺后脑电数据,可以将脑电前后数据进行拼接,从而进行半监督学习。</p>
<h2 id="开发环境和使用方法"><a href="#开发环境和使用方法" class="headerlink" title="开发环境和使用方法"></a>开发环境和使用方法</h2><p>为了能够实时查看到数据,我和项目成员选取了 Jupyter Notebook 的方式在 <span class="exturl" data-url="aHR0cHM6Ly9jb2xhYi5yZXNlYXJjaC5nb29nbGUuY29tL25vdGVib29rcy9pbnRyby5pcHluYg==">Colab<i class="fa fa-external-link-alt"></i></span> 上进行实验。如果你此前没有配置过相关的环境,在此极力推荐在线的 Jupyter Notebook环境(<span class="exturl" data-url="aHR0cHM6Ly9jb2xhYi5yZXNlYXJjaC5nb29nbGUuY29tL25vdGVib29rcy9pbnRyby5pcHluYg==">Colab<i class="fa fa-external-link-alt"></i></span> , <span class="exturl" data-url="aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9ub3RlYm9va3M/c29ydEJ5PWRhdGVSdW4mYW1wO3RhYj1wcm9maWxl">Kaggle<i class="fa fa-external-link-alt"></i></span>)进行实验,以免花费太多不必要的精力在配置环境和安装依赖库中。</p>
<p>所有 .ipynb 文件 均可在 <span class="exturl" data-url="aHR0cHM6Ly9jb2xhYi5yZXNlYXJjaC5nb29nbGUuY29tL25vdGVib29rcy9pbnRyby5pcHluYg==">Colab<i class="fa fa-external-link-alt"></i></span> 上直接运行,但不排除会因为没有 pip 安装库等原因导致一些小 bug 出现。</p>
<h2 id="写在后面"><a href="#写在后面" class="headerlink" title="写在后面"></a>写在后面</h2><p>由于项目以小组单位进行开发,所以各位成员的代码风格有一定的差异,也不排除某些方面的问题有所忽略,如发现有错误或不足,十分欢迎 issue 或 pr。希望能帮助到学习该门课程的同学。</p>
]]></content>
<categories>
<category>课内学习</category>
<category>科研</category>
<category>技术</category>
</categories>
<tags>
<tag>Data Mining</tag>
<tag>Machine Learning</tag>
</tags>
</entry>
<entry>
<title>大三上数据库项目总结</title>
<url>/SCNU-CS-2018-DatabaseProject.html</url>
<content><![CDATA[<h2 id="SCNU-CS-2018-DatabaseProject"><a href="#SCNU-CS-2018-DatabaseProject" class="headerlink" title="SCNU-CS-2018-DatabaseProject"></a>SCNU-CS-2018-DatabaseProject</h2><p>最近打算整理一下上学期的项目作业到 Github 上,同时 post 到博客上以作记录与归档。这一次分享的是数据库课程项目,为了能够快速实现需求,我选用了微后端框架 Flask+web 的形式进行开发。</p>
<p>在与同学的合作下,两个子项目的开发与撰写文档分别花了将近一周的时间,完成了课程的基本和额外要求,具体的内容请参见 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NoYXJmb2xlL1NDTlUtQ1MtMjAxOC1EYXRhYmFzZVByb2plY3Q=">GitHub<i class="fa fa-external-link-alt"></i></span>。</p>
<p>课程一共有两阶段的任务,要求分别选取一款传统型数据库和 NoSQL 数据库,在 Linux 环境下部署服务端,以 C/S 架构实现数据库的各项基本功能和部分特色操作。</p>
<p>本项目选用的是 MySQL 和 Redis ,由于课程不对性能作要求,因此为了能够快速实现要求的功能,项目选用了 Flask 作为服务端,并以 web 的方式开发客户端,具体的项目框架请参照下图。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/MySQL架构图.png" alt="MySQL架构图"></p>
<a id="more"></a>
<h2 id="开发环境与依赖"><a href="#开发环境与依赖" class="headerlink" title="开发环境与依赖"></a>开发环境与依赖</h2><p><strong>CentOS Linux release 8.3.2011</strong><br>(项目使用阿里云服务器,可兼容其他类型云服务器或虚拟机,尚未在其它系统实现,因此不保证兼容性)</p>
<p><strong>python 3.6</strong></p>
<p><strong>MySQL 8.0.21</strong></p>
<p><strong>Redis 5.0.3</strong></p>
<p>其余依赖均包含在项目对应的虚拟环境文件夹 (DatabaseVenv) </p>
<h2 id="部署流程"><a href="#部署流程" class="headerlink" title="部署流程"></a>部署流程</h2><ol>
<li><p>clone 本仓库</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git clone https://github.com/charfole/SCNU-CS-2018-DatabaseProject.git</span><br><span class="line">cd SCNU-CS-2018-DatabaseProject</span><br></pre></td></tr></table></figure>
</li>
<li><p>在 Linux 环境下安装相关依赖</p>
<p>安装Python、MySQL和Redis,安装教程可参照菜鸟教程(<span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9weXRob24zL3B5dGhvbjMtaW5zdGFsbC5odG1s">Python<i class="fa fa-external-link-alt"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9teXNxbC9teXNxbC1pbnN0YWxsLmh0bWw=">MySQL<i class="fa fa-external-link-alt"></i></span>, <span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9yZWRpcy9yZWRpcy1pbnN0YWxsLmh0bWw=">Redis<i class="fa fa-external-link-alt"></i></span>)</p>
</li>
<li><p>部署 MySQL 项目</p>
<ul>
<li><p>创建数据表</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd MySQL/data</span><br><span class="line"><span class="meta">#</span><span class="bash"> 在连接mysql数据库后执行</span></span><br><span class="line">source cardTable.sql</span><br><span class="line">source geoTable.sql</span><br><span class="line">source userTable.sql</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> PS:除此之外,也可以通过Navicat等DBMS修改并执行三个sql文件</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>修改数据库连接信息</p>
<figure class="highlight python"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 打开 MySQL/backend/app.py</span></span><br><span class="line"><span class="comment"># 根据个人情况修改user、password和db三个参数,确保连接到希望操纵的数据库</span></span><br><span class="line">conn = pymysql.connect(host=<span class="string">'127.0.0.1'</span>, user=<span class="string">'root'</span>, password=<span class="string">''</span>, db=<span class="string">'charfoleTable'</span>, charset=<span class="string">'utf8'</span>) <span class="comment"># connect to the database</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>部署后端代码</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">source ../DatabaseVenv/bin/activate</span><br><span class="line">cd MySQL/backend</span><br><span class="line">gunicorn -b :5000 app:app # debug模式运行</span><br><span class="line">gunicorn -c config.py app:app # config模式运行</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li><p>修改 MySQL 客户端对应的 IP 地址</p>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 打开 MySQL/web/page.html</span></span><br><span class="line"><span class="comment">// 根据个人情况,将所有出现yourIPAddress语句中的yourIPAddress替换为你部署flask的ip(服务器ip或者是虚拟机的ip)</span></span><br><span class="line"></span><br><span class="line">url: <span class="string">"http://yourIPAddress:5000/charfoleTransaction"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改完后,打开page.html并刷新即可成功运行项目</span></span><br></pre></td></tr></table></figure>
</li>
<li><p>部署 Redis 项目</p>
<ul>
<li><p>创建数据</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd Redis/data</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 将 Redis/data/charfole.csv 中的数据写入redis的第0号数据库</span></span><br><span class="line">python3 readData.py</span><br></pre></td></tr></table></figure>
</li>
<li><p>部署后端代码</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">source ../DatabaseVenv/bin/activate</span><br><span class="line">cd MySQL/backend</span><br><span class="line">gunicorn -b :5000 app:app # debug模式运行</span><br><span class="line">gunicorn -c config.py app:app # config模式运行</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li><p>修改 Redis 客户端对应的 IP 地址</p>
</li>
</ol>
<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 打开 Redis/web/page.html</span></span><br><span class="line"><span class="comment">// 根据个人情况,将所有出现yourIPAddress语句中的yourIPAddress替换为你部署flask的ip(服务器ip或者是虚拟机的ip)</span></span><br><span class="line"></span><br><span class="line">url: <span class="string">"http://yourIPAddress:5000/charfoleCRUD"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 修改完后,打开page.html并刷新即可成功运行项目</span></span><br></pre></td></tr></table></figure>
<h2 id="实现的功能"><a href="#实现的功能" class="headerlink" title="实现的功能"></a>实现的功能</h2><ol>
<li>MySQL项目<ul>
<li>执行基本的 SQL 语句,包括但不限于增删查改、索引、跨表操作</li>
<li>事务支持(execute、commit、rollback)</li>
<li>用户性能查询(仅在后端实现、需提前创建对应的用户并修改后端代码)</li>
</ul>
</li>
<li>Redis项目<ul>
<li>Redis 的基本功能,对数据库中所包含的键值对进行增删查改</li>
<li><span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9yZWRpcy9yZWRpcy1oeXBlcmxvZ2xvZy5odG1s">基数查询<i class="fa fa-external-link-alt"></i></span>(Hyperloglog)功能</li>
</ul>
</li>
</ol>
<p>由于篇幅所限,更多的功能说明请参照<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NoYXJmb2xlL1NDTlUtQ1MtMjAxOC1EYXRhYmFzZVByb2plY3QvdHJlZS9tYXN0ZXIvaW5mb3JtYXRpb24vJUU5JUExJUI5JUU3JTlCJUFFJUU2JTk2JTg3JUU2JUExJUEz">项目文档<i class="fa fa-external-link-alt"></i></span>。</p>
<h2 id="项目截图"><a href="#项目截图" class="headerlink" title="项目截图"></a>项目截图</h2><ol>
<li><p>MySQL</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/MySQL项目截图-1613657129070.png" alt="MySQL项目截图"></p>
</li>
<li><p>Redis</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/Redis项目截图-1613657154813.png" alt="Redis项目截图"></p>
</li>
</ol>
<h2 id="写在后面"><a href="#写在后面" class="headerlink" title="写在后面"></a>写在后面</h2><p>为了快速兑现课程要求的相关功能,因此项目在性能、鲁棒性方面有所欠缺。如发现有错误或不足,十分欢迎 issue 或 pr。希望能帮助到学习该门课程的同学。</p>
]]></content>
<categories>
<category>课内学习</category>
<category>技术</category>
</categories>
<tags>
<tag>MySQL</tag>
<tag>Redis</tag>
<tag>Flask</tag>
<tag>Database</tag>
<tag>web</tag>
</tags>
</entry>
<entry>
<title>Win10系统下的Windows Terminal+WSL配置指南</title>
<url>/Win10%E7%B3%BB%E7%BB%9F%E4%B8%8B%E7%9A%84Windows%20Terminal+WSL%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97.html</url>
<content><![CDATA[<p>在计算机学习的过程中,我们常常会纠结于 Windows、Linux 和 Mac OS 的选择。Mac OS X 凭借其出色的GUI与天然的 Unix-like 环境,倍受程序猿欢迎。但对于大部分同学来说,Macbook 系列的价格较为高昂,因而也自然无法接触到 Mac OS X 了。那么开发环境的选择一般就只剩下纯 Windows、Windows+虚拟机 和 Windows+双系统了。纯Windows环境往往会增加折腾过程的难度,同时Windows 老旧的命令行界面也让人不那么提得起劲。(<del>毕竟好看才是第一生产力</del>)后两种固然能够体验最纯粹的 Gnu/Linux 环境,但虚拟机的启动等待和系统的切换等待让这一过程显得不那么顺滑。</p>
<p>那么,有没有一种方法能让我们能够顺滑高效地体验Linux的魅力呢?</p>
<p>有!那就是上手微软的 <strong>Windows Terminal</strong> 和 <strong>Windows Subsystem for Linux</strong>(WSL)了!<br><a id="more"></a></p>
<hr>
<h2 id="Why-Windows-Terminal-and-WSL?"><a href="#Why-Windows-Terminal-and-WSL?" class="headerlink" title="Why Windows Terminal and WSL?"></a>Why Windows Terminal and WSL?</h2><p>微软的 Windows Terminal 发布于2019年,它是一个现代的终端工具,集成了 CMD、PowerShell、WSL 这三大环境,同时还内置了SSH 客户端,让用户能够在同一个窗口中<strong>顺滑</strong>地使用不同的环境与工具。</p>
<p>而 WSL 则是内嵌于 Windows 的 Linux 子系统,它允许开发人员直接在Windows系统中”套娃”运行一个 GNU/Linux 环境,在其中你可以使用各类的命令行工具和应用程序,避免使用虚拟机或双系统设置的庞大开销。WSL 2 则是 WSL 的升级版本,它提供了完整的 Linux 内核,提升了文件 IO 性能,并支持 Docker 的使用,不过这也导致了其跨 OS 文件系统的性能不如 WSL 1, 即在访问 Windows 的文件系统时会比 WSL 1慢一点。</p>
<p>个人认为,如果想体验较为纯正的 Linux 环境,并且希望在其中获得更好的 IO 性能,推荐使用 WSL2。但若有跨 OS 使用文件的需求,则可以考虑 WSL 1。如果暂时还没有考虑好自己的需求的话,任意选择一个先折腾起来也不要紧,因为WSL可以很方便地在1、2版本间切换(后面会提到)。</p>
<p>下面贴一张微软官网上的 WSL 1和 WSL 2的性能对比图:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/53aef1cc2413b3fe50469c5e3a38723.png" alt="比较WSL 1和WSL 2"></p>
<hr>
<h2 id="Windows-Terminal安装与初步体验"><a href="#Windows-Terminal安装与初步体验" class="headerlink" title="Windows Terminal安装与初步体验"></a>Windows Terminal安装与初步体验</h2><p>在安装之前,请先 Win+R 并键入winver来确认自己 Win10 版本,<strong>Windows Terminal的最低要求为 Win 10-18362 及以上的版本</strong>,而后续 <strong>WSL 2 的安装则至少需要Win 10-2004-19041版本</strong>,如果版本过低,请先进行系统更新。如系统无法自动升级,可以通过<span class="exturl" data-url="aHR0cHM6Ly93d3cubWljcm9zb2Z0LmNvbS96aC1jbi9zb2Z0d2FyZS1kb3dubG9hZC93aW5kb3dzMTA=">Microsoft的升级助手<i class="fa fa-external-link-alt"></i></span>升级版本。在这里,我建议最好确保电脑升级到了 Win10-2004-19041版本,否则无法体验 WSL 2(笔者成功更新到2004版本之前系统还是一年前的1903版本,更新后一切正常,所以大家可以放心~)</p>
<p>折腾完更新后,我们马上来安装Windows Terminal,安装过程十分简单,只需要在电脑上打开<span class="exturl" data-url="aHR0cHM6Ly9ha2EubXMvdGVybWluYWw=">Microsoft Store<i class="fa fa-external-link-alt"></i></span>(微软商店),搜索Windows Terminal并安装即可。安装完启动,便可以愉快地玩耍啦!</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/1342f00573e07513d603a8fa7dc2251.png" alt="Terminal"></p>
<p>从图中我们可以看到,在 Windows Terminal 中通过选项卡我们可以随意切换 Powershell、CMD、WSL 等多个环境。使用快捷键Ctrl+Shift+1/2/3/4…可以快速地打开不同的窗口,使用 Alt+shift+减号(加号)可以实现水平(垂直)分屏。通过 settings.json 文件,我们还可以让 Terminal 变得更酷,在本文的第四点将进行详细的描述。</p>
<hr>
<h2 id="WSL-Ubuntu-18-04安装与体验"><a href="#WSL-Ubuntu-18-04安装与体验" class="headerlink" title="WSL-Ubuntu 18.04安装与体验"></a>WSL-Ubuntu 18.04安装与体验</h2><p>安装完 Windows Terminal 后,下一步我们来安装 WSL 。正常情况下,微软商店会默认将WSL下载到系统驱动器中(也就是大部分人容量较少的C盘),如果你目前的C盘空间不是很足(少于50G),那么我建议你按照下面的步骤来将WSL安装在其他位置。</p>
<h3 id="启用系统中的WSL和虚拟机平台功能"><a href="#启用系统中的WSL和虚拟机平台功能" class="headerlink" title="启用系统中的WSL和虚拟机平台功能"></a>启用系统中的WSL和虚拟机平台功能</h3><p>首先以管理员模式启动 Powershell,其后分别输入以下代码:</p>
<figure class="highlight powershell"><table><tr><td class="code"><pre><span class="line">dism.exe /online /<span class="built_in">enable-feature</span> /featurename:Microsoft<span class="literal">-Windows</span><span class="literal">-Subsystem</span><span class="literal">-Linux</span> /all /norestart</span><br><span class="line">dism.exe /online /<span class="built_in">enable-feature</span> /featurename:VirtualMachinePlatform /all /norestart</span><br></pre></td></tr></table></figure>
<p>上文的第一串代码是开启系统的 WSL 功能,第二串则是开启 ”虚拟机平台“ 可选组件,重启电脑后两项功能便已打开。</p>
<h3 id="安装Linux分发版到电脑的任意位置"><a href="#安装Linux分发版到电脑的任意位置" class="headerlink" title="安装Linux分发版到电脑的任意位置"></a>安装Linux分发版到电脑的任意位置</h3><p>(写在前面:如果你觉得将WSL安装在系统盘问题也不大,那么可以直接在微软应用商店中安装你想要的WSL版本,完成密码设置后即可跳到下一步)</p>
<p>由于我们需要跳开默认的安装位置,因此我们需要手动安装一下 Linux 分发版。首先,我们从<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy93c2wvaW5zdGFsbC1tYW51YWw=">微软的文档<i class="fa fa-external-link-alt"></i></span>中找到可供手动下载的WSL目录,选择自己需要的版本(Ubuntu、Debian、Kali等),下载其对应的.appx文件。第二步,将下载好的以.appx后缀结尾的文件更名为.zip文件,之后解压到你自定义的位置(系统盘或非系统盘)。最后,点击解压后出现以分发版本命名的.exe文件(比如,下载的是Ubuntu-18.04版本,则对应文件名为 “ubuntu1804.exe”),等待安装完成后,输入你的账户和密码,便完成了 WSL 的初步安装!</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200813162244438.png" alt="Ubuntu"></p>
<p>(PS: 如果你在设定或输入密码时屏幕没有显示,请不要担心,这是正常现象,想进一步了解该密码的作用可戳<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy93c2wvdXNlci1zdXBwb3J0">这里<i class="fa fa-external-link-alt"></i></span>)</p>
<h3 id="在Windows-Terminal中管理与体验WSL"><a href="#在Windows-Terminal中管理与体验WSL" class="headerlink" title="在Windows Terminal中管理与体验WSL"></a>在Windows Terminal中管理与体验WSL</h3><p>Win+R+输入wt快速打开Windows Terminal后,在Powershell中通过以下的命令来管理我们的WSL。</p>
<figure class="highlight powershell"><table><tr><td class="code"><pre><span class="line"><span class="comment"># 检查分配给每个已安装的 Linux 分发版的 WSL 版本</span></span><br><span class="line">wsl -<span class="literal">-list</span> -<span class="literal">-verbose</span> <span class="comment"># 等同于wsl -l -v</span></span><br><span class="line"><span class="comment"># 将某个Linux分发版设置为受某一 WSL 版本</span></span><br><span class="line">wsl -<span class="literal">-set</span><span class="literal">-version</span> <Linux 分发版的名称> <版本号:<span class="number">1</span>、<span class="number">2</span>></span><br><span class="line"><span class="comment"># 将WSL 2设置为默认Linux分发版的默认版本</span></span><br><span class="line">wsl -<span class="literal">-set</span><span class="literal">-default</span><span class="literal">-version</span> <span class="number">2</span></span><br><span class="line"><span class="comment"># 再次提醒,以上三条命令中的后两条需要用到WSL 2,而WSL 2仅支持Win10 2004以上的版本(前文第二点已提到)</span></span><br></pre></td></tr></table></figure>
<p>完成以上的设定后,点击选项卡中的下拉框,选择安装好的WSL版本便可以开始愉快的玩耍啦~可以看到,原来Windows中的文件均被挂载到了/mnt 目录下(C盘在 /mnt/c,D盘在 /mnt/d,依此类推),我们可以很方便地进行跨文件系统的访问。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200814230639846.png" alt="image-20200814230639846"></p>
<p>到了这里,Windows Terminal和WSL的基本配置就已经完成了!如果你想进一步对Windows Terminal和WSL进行美化(如上图),那么强烈建议你继续阅读下一部分内容。</p>
<hr>
<h2 id="WSL美化与Windows-Terminal美化"><a href="#WSL美化与Windows-Terminal美化" class="headerlink" title="WSL美化与Windows Terminal美化"></a>WSL美化与Windows Terminal美化</h2><h3 id="安装-oh-my-zsh"><a href="#安装-oh-my-zsh" class="headerlink" title="安装 oh-my-zsh"></a>安装 oh-my-zsh</h3><p>为了让WSL的shell界面变得更加美观,笔者推荐安装 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL29obXl6c2gvb2hteXpzaA==">oh-my-zsh<i class="fa fa-external-link-alt"></i></span>,它是一款针对zsh终端的扩展框架,支持配置多种插件与主题来丰富我们的终端。</p>
<p>首先,我们需要安装zsh来作为我们的 shell,打开 WSL,键入以下代码:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sudo apt-get install zsh # Ubuntu下安装zsh,安装后重启wsl</span><br><span class="line">echo $SHELL # 查看当前使用的shell是否为zsh</span><br><span class="line"><span class="meta">#</span><span class="bash"> 如果显示为/usr/bin/zsh,则代表安装成功,否则使用以下命令切换为zsh</span></span><br><span class="line">chsh -s /bin/zsh # 将shell切换为zsh</span><br></pre></td></tr></table></figure>
<p>之后,我们通过curl来安装下载oh-my-zsh。(如果遇到了无法下载Github文件的问题,可以参考一下<span class="exturl" data-url="aHR0cHM6Ly9zaGlsZWl5ZS5jb20vODM1">这一篇文章<i class="fa fa-external-link-alt"></i></span>来修改host文件)</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"</span><br></pre></td></tr></table></figure>
<p>安装的过程中需要获取一些权限,输入y(yes)就好。安装完成后,等待WSL自动重启,此时便用上了oh-my-zsh。</p>
<h3 id="配置使用-oh-my-zsh"><a href="#配置使用-oh-my-zsh" class="headerlink" title="配置使用 oh-my-zsh"></a>配置使用 oh-my-zsh</h3><p>上一步的折腾成功后,我们就离WSL美化大功告成只差一步了。首先,进入WSL,运行以下命令:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 利用vim编辑器打开zsh配置文件</span></span><br><span class="line">vim ~/.zshrc</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 修改oh-my-zsh的主题,这里推荐使用agnoster主题(也可选择其他)</span></span><br><span class="line">ZSH_THEME="agnoster"</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 保存文件后退出回到Shell,然后输入</span></span><br><span class="line">source ~/.zshrc</span><br></pre></td></tr></table></figure>
<p>以上的操作要求对Vim的使用有所了解,如果你第一次接触Vim,不妨先快速浏览一下这篇<span class="exturl" data-url="aHR0cHM6Ly93d3cuamlhbnNodS5jb20vcC84YjY3OWIzNWM5ZDU=">文章<i class="fa fa-external-link-alt"></i></span>。</p>
<p>修改完主题后,因为缺少一个合适的字体,所以主题的图标显示有所异常。因此,我们需要安装一下<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Bvd2VybGluZS9mb250cw==">powerline<i class="fa fa-external-link-alt"></i></span>字体到我们的<strong>Windows系统</strong>中(非WSL)来解决这个问题。</p>
<p>我们可以根据 Github 的指引,在 Powershell 中使用git来安装。</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 下载</span></span><br><span class="line">git clone https://github.com/powerline/fonts.git --depth=1</span><br><span class="line"><span class="meta">#</span><span class="bash"> install</span></span><br><span class="line">cd fonts # git clone的文件夹</span><br><span class="line">./install.sh</span><br></pre></td></tr></table></figure>
<p>如果你不太熟悉git安装,也可以下载仓库的zip文件,解压后双击运行文件夹中的 “install.sh”文件或者“install.ps1”文件即可。</p>
<p>安装完字体后,我们再次进入Windows Terminal,在下拉框中选择设置。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200813234409331.png" alt="设置"></p>
<p>进入设置文件 “settings.json” 后,找到 “profiles” 字段下的 “list” 字段,在WSL配置模块中增加或修改 <code>fontFace</code> 字段为你想要的字体名称。(字体名称改为刚刚安装好的 Powerline 字体中的一种,我使用的是DejaVu Sans Mono for Powerline字体)</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200813235847851.png" alt="设置"></p>
<p>VS Code中使用WSL也会出现乱码情况,解决的方法类似,也是下载字体与修改配置。因为篇幅有限,关于VS Code下的 WSL 配置就不展开叙述了,详情可参考这两个链接:<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy93c2wvdHV0b3JpYWxzL3dzbC12c2NvZGU=">适用于WSL的VS Code入门<i class="fa fa-external-link-alt"></i></span>和<span class="exturl" data-url="aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC82ODMzNjY4NQ==">解决VS Code中的WSL乱码<i class="fa fa-external-link-alt"></i></span>。</p>
<p>如果想要WSL更酷炫一点,可以进行<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2R5bGFuYXJhcHMvbmVvZmV0Y2g=">neofetch<i class="fa fa-external-link-alt"></i></span>的配置,Ubuntu中的操作如下:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> Ubuntu下安装neofetch</span></span><br><span class="line">sudo apt install neofetch</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 利用vim编辑器打开zsh配置文件</span></span><br><span class="line">vim ~/.zshrc</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 在配置文件中主题行的后一行添加neofetch(在文件的末尾加也可以)</span></span><br><span class="line">ZSH_THEME="agnoster"</span><br><span class="line">neofetch</span><br></pre></td></tr></table></figure>
<h3 id="安装zsh常用插件"><a href="#安装zsh常用插件" class="headerlink" title="安装zsh常用插件"></a>安装zsh常用插件</h3><p>zsh中有许多优秀的插件帮助我们提高效率,下面介绍两个常用插件的安装流程,分别是语法高亮插件和自动提示插件。首先,将当前路径切换到 plugins 目录中。</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">cd ~/.oh-my-zsh/custom/plugins</span><br></pre></td></tr></table></figure>
<p>之后,我们用git下载这两款插件到 WSL 中。</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> git下载 zsh-syntax-highlighting</span></span><br><span class="line">git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> git下载 zsh-autosuggestions</span></span><br><span class="line">git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions</span><br></pre></td></tr></table></figure>
<p>下载完成后,我们修改一下zsh的配置文件,使插件生效。</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 首先用vim进入.zshrc配置文件</span></span><br><span class="line">vim ~/.zshrc</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 之后利用vim编辑文件为</span></span><br><span class="line">plugins=(</span><br><span class="line"> zsh-syntax-highlighting</span><br><span class="line"> zsh-autosuggestions</span><br><span class="line"> )</span><br><span class="line">source $ZSH/oh-my-zsh.sh</span><br><span class="line">source $ZSH_CUSTOM/plugins/zsh-syntax-highlighting/zsh-syntax-highlighting.zsh</span><br><span class="line">source $ZSH_CUSTOM/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh</span><br></pre></td></tr></table></figure>
<p>配置好oh-my-zsh并安装好插件后,WSL方面的美化工作就基本完成啦!!最终,我们的WSL变成下图所示的样子。(<del>真香!</del>)</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200814104151498.png" alt="WSL Final"></p>
<h3 id="Windows-Terminal-美化"><a href="#Windows-Terminal-美化" class="headerlink" title="Windows Terminal 美化"></a>Windows Terminal 美化</h3><p> Windows Terminal的配置文件是一个 JSON 格式的文件,它支持我们对Terminal进行大规模地自定义,关于该 JSON 的一切信息都可以在<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy90ZXJtaW5hbC9jdXN0b21pemUtc2V0dGluZ3MvZ2xvYmFsLXNldHRpbmdz">微软文档<i class="fa fa-external-link-alt"></i></span>中找到。下面我们来看看设置文件中最主要的部分,并简要介绍前三部分。</p>
<ul>
<li><p>全局设置:位于 JSON 文件的最顶端,主要用于设置Windows Terminal的亮暗主题、和启动时的默认环境</p>
</li>
<li><p>环境入口 <code>profiles</code>:其中包含defaults和list两个字段,defaults中的设置默认对所有的环境生效,list中可以对每个环境进行细化的定义,这些定义会覆盖defaluts中的内容。</p>
</li>
<li><p>配色方案 <code>schemes</code>:这里可以自定义多个主题配色,在profiles中可以为不同的环境分配不同的配色。</p>
</li>
<li><p>键绑定 <code>keybindings</code>:自定义多种快捷键。</p>
<p> </p>
<p><strong>全局设置</strong>中,都是一些Terminal里的宏观设置,比较重要的有这两项:</p>
</li>
<li><p>亮暗主题设置:<code>"theme":"system"(default)、"dark"、"light"</code></p>
</li>
<li><p>初始配置:<code>"defaultProfile":"{默认为Powershell的GUID}"</code>,其中 GUID 为不同环境的唯一标识码,可以在 profiles 中的 list 找到你想默认启动的环境,将其GUID作为defaultProfile的值。</p>
</li>
</ul>
<p> 在环境入口<strong>profiles</strong>中,各个环境通用的设置可以放在defaults字段中,而每个环境特定的配置可以在 list 中准确定义。(字体、背景、配色方案等,详情可看<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy90ZXJtaW5hbC9jdXN0b21pemUtc2V0dGluZ3MvcHJvZmlsZS1zZXR0aW5ncw==">这里<i class="fa fa-external-link-alt"></i></span>)</p>
<p> 我在Profiles中使用的配置项有这些,其中的照片、图标、字体、颜色主题等可以自行更换。</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">"useAcrylic": true, // 推荐启用毛玻璃</span><br><span class="line">"acrylicOpacity": 0.6, // 启用毛玻璃后,可调整背景透明度</span><br><span class="line">"icon": "ms-appx:///ProfileIcons/{9acb9455-ca41-5af7-950f-6bca1bc9722f}.png", //图标</span><br><span class="line">"backgroundImage": "C:\\Users\\13359\\Pictures\\Camera Roll\\wolf.png", //背景图片</span><br><span class="line">"backgroundImageOpacity": 0.3, //图片透明度</span><br><span class="line">"backgroundImageStretchMode": "fill", //填充模式</span><br><span class="line">"fontFace": "DejaVu Sans Mono for Powerline", //字体种类</span><br><span class="line">"suppressApplicationTitle": true,//固定选项卡中的标题</span><br><span class="line">"fontSize": 12, //文字大小</span><br><span class="line">"colorScheme": "cyberpunk", //主题,与schemes中的名称对应</span><br><span class="line">"cursorColor": "#FFFFFF", //光标颜色</span><br><span class="line">"cursorShape": "vintage", //光标形状</span><br><span class="line">"startingDirectory":"C://" //起始目录</span><br></pre></td></tr></table></figure>
<p>在配色方案schemes中,我们可以定义多个不同的颜色主题,并在不同的环境中运用不同的颜色主题,以获取最适合自己的终端体验。以下代码是两种配色方案的定义,其中 <code>name</code>用于命名配色方案,并用于赋值给环境设置中的<code>colorScheme</code>字段。关于更多配色方案的选取可以参照Iterm2-color-schemes的<span class="exturl" data-url="aHR0cHM6Ly9pdGVybTJjb2xvcnNjaGVtZXMuY29tLw==">网站<i class="fa fa-external-link-alt"></i></span>和<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL21iYWRvbGF0by9pVGVybTItQ29sb3ItU2NoZW1lcw==">Github<i class="fa fa-external-link-alt"></i></span>。</p>
<figure class="highlight"><table><tr><td class="code"><pre><span class="line">"schemes": [</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"cyberpunk"</span>,</span><br><span class="line"> <span class="attr">"black"</span>: <span class="string">"#000000"</span>,</span><br><span class="line"> <span class="attr">"red"</span>: <span class="string">"#ff7092"</span>,</span><br><span class="line"> <span class="attr">"green"</span>: <span class="string">"#00fbac"</span>,</span><br><span class="line"> <span class="attr">"yellow"</span>: <span class="string">"#fffa6a"</span>,</span><br><span class="line"> <span class="attr">"blue"</span>: <span class="string">"#00bfff"</span>,</span><br><span class="line"> <span class="attr">"purple"</span>: <span class="string">"#df95ff"</span>,</span><br><span class="line"> <span class="attr">"cyan"</span>: <span class="string">"#86cbfe"</span>,</span><br><span class="line"> <span class="attr">"white"</span>: <span class="string">"#ffffff"</span>,</span><br><span class="line"> <span class="attr">"brightBlack"</span>: <span class="string">"#647da1"</span>,</span><br><span class="line"> <span class="attr">"brightRed"</span>: <span class="string">"#ff8aa4"</span>,</span><br><span class="line"> <span class="attr">"brightGreen"</span>: <span class="string">"#21f6bc"</span>,</span><br><span class="line"> <span class="attr">"brightYellow"</span>: <span class="string">"#fff787"</span>,</span><br><span class="line"> <span class="attr">"brightBlue"</span>: <span class="string">"#1bccfd"</span>,</span><br><span class="line"> <span class="attr">"brightPurple"</span>: <span class="string">"#e6aefe"</span>,</span><br><span class="line"> <span class="attr">"brightCyan"</span>: <span class="string">"#99d6fc"</span>,</span><br><span class="line"> <span class="attr">"brightWhite"</span>: <span class="string">"#ffffff"</span>,</span><br><span class="line"> <span class="attr">"background"</span>: <span class="string">"#332a57"</span>,</span><br><span class="line"> <span class="attr">"foreground"</span>: <span class="string">"#e5e5e5"</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">"name"</span>: <span class="string">"DimmedMonokai"</span>,</span><br><span class="line"> <span class="attr">"black"</span>: <span class="string">"#3a3d43"</span>,</span><br><span class="line"> <span class="attr">"red"</span>: <span class="string">"#be3f48"</span>,</span><br><span class="line"> <span class="attr">"green"</span>: <span class="string">"#879a3b"</span>,</span><br><span class="line"> <span class="attr">"yellow"</span>: <span class="string">"#c5a635"</span>,</span><br><span class="line"> <span class="attr">"blue"</span>: <span class="string">"#4f76a1"</span>,</span><br><span class="line"> <span class="attr">"purple"</span>: <span class="string">"#855c8d"</span>,</span><br><span class="line"> <span class="attr">"cyan"</span>: <span class="string">"#578fa4"</span>,</span><br><span class="line"> <span class="attr">"white"</span>: <span class="string">"#b9bcba"</span>,</span><br><span class="line"> <span class="attr">"brightBlack"</span>: <span class="string">"#368bfa"</span>,</span><br><span class="line"> <span class="attr">"brightRed"</span>: <span class="string">"#eb001f"</span>,</span><br><span class="line"> <span class="attr">"brightGreen"</span>: <span class="string">"#0f722f"</span>,</span><br><span class="line"> <span class="attr">"brightYellow"</span>: <span class="string">"#eef107"</span>,</span><br><span class="line"> <span class="attr">"brightBlue"</span>: <span class="string">"#428ffa"</span>,</span><br><span class="line"> <span class="attr">"brightPurple"</span>: <span class="string">"#fb0067"</span>,</span><br><span class="line"> <span class="attr">"brightCyan"</span>: <span class="string">"#2e706d"</span>,</span><br><span class="line"> <span class="attr">"brightWhite"</span>: <span class="string">"#fdffb9"</span>,</span><br><span class="line"> <span class="attr">"background"</span>: <span class="string">"#1f1f1f"</span>,</span><br><span class="line"> <span class="attr">"foreground"</span>: <span class="string">"#b9bcba"</span></span><br><span class="line"> }</span><br><span class="line"> ],</span><br></pre></td></tr></table></figure>
<p>我将上面的两种配色方案分别应用在 Powershell 和 WSL 中,这个操作十分简单,只需要在 profiles 的 list 中将不同颜色主题的名字分别赋值到 Powershell 和 WSL 中的 <code>colorScheme</code>中即可。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200814231326728.png" alt="image-20200814231326728"></p>
<p>到了这里,我们 Windows Terminal 和 WSL 的美化工作就<strong>大功告成</strong>啦!!最终的成果请参看下图~</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200814161425692.png" alt="Powershell"></p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20200814161521972.png" alt="WSL"></p>
<hr>
<h2 id="结语与相关链接"><a href="#结语与相关链接" class="headerlink" title="结语与相关链接"></a>结语与相关链接</h2><p>感谢你看到这里,这篇文章是我断断续续构(mo)思(yu)了两三天才写好的,如果它对你有一点小小的帮助,那就是我最大的欣慰了。同时,这也是我第一次写这么详细的技术分享文章,因此难免会有一些纰漏出现。如果你发现了有疑问或者有错误的地方,不妨在在下方讨论或指出,<strong>再次感谢!</strong></p>
<p>在撰写文章时,我从很多地方获得了帮助与指引,如果你想了解更多关于 Windows Terminal 和 WSL 的内容,可以浏览以下链接:</p>
<ul>
<li><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy90ZXJtaW5hbC8=">微软 Windows Terminal 使用文档<i class="fa fa-external-link-alt"></i></span></li>
<li><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLm1pY3Jvc29mdC5jb20vemgtY24vd2luZG93cy93c2wv">微软 WSL 使用文档<i class="fa fa-external-link-alt"></i></span></li>
<li><span class="exturl" data-url="aHR0cHM6Ly9zc3BhaS5jb20vcG9zdC81OTM4MA==">少数派:Windows Terminal 自定义教程<i class="fa fa-external-link-alt"></i></span></li>
<li><span class="exturl" data-url="aHR0cHM6Ly96aHVhbmxhbi56aGlodS5jb20vcC82ODMzNjY4NQ==">知乎:WSL + oh-my-zsh 配置教程<i class="fa fa-external-link-alt"></i></span></li>
<li><span class="exturl" data-url="aHR0cHM6Ly9qdWVqaW4uaW0vZW50cnkvNjg0NDkwMzg0Nzc5NDU3MzMxOQ==">掘金:WSL 美化<i class="fa fa-external-link-alt"></i></span></li>
<li><span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9saW51eC9saW51eC12aW0uaHRtbA==">菜鸟教程:vim 的使用<i class="fa fa-external-link-alt"></i></span></li>
</ul>
]]></content>
<categories>
<category>技术</category>
</categories>
<tags>
<tag>WSL</tag>
<tag>Windows Terminal</tag>
<tag>terminal</tag>
</tags>
</entry>
<entry>
<title>Docker镜像、容器、仓库基本概念介绍</title>
<url>/docker-overview.html</url>
<content><![CDATA[<p>Docker是快速搭建开发环境的好帮手,之前对它的了解一直处于使用层面,最近阅读了<span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20v">Docker的官方文档<i class="fa fa-external-link-alt"></i></span>发现里面的概念还挺清晰易懂的,遂写下博客,记录Docker中镜像、容器、仓库这三个最重要的概念。</p>
<a id="more"></a>
<h2 id="Docker基本概念"><a href="#Docker基本概念" class="headerlink" title="Docker基本概念"></a>Docker基本概念</h2><p>Docker是一个帮助应用进行开发、迁移和运行的平台,它将软件与系统架构进行隔离,从而使得软件可以被快速部署。</p>
<p>Docker通过容器来为应用提供一个轻量级的运行环境,在保证各个应用能通过容器来运行与协作的同时,降低了容器对环境的依赖。此外Docker还提供了一系列命令来帮助管理应用的生命周期。</p>
<p>如下图,Docker使用客户端/服务器架构,客户端发出指令(如docker run、docker build),服务端的守护进程负责侦听和接收指令,并对镜像、容器、网络和数据卷等内容进行管理。Docker客户端和服务端通过基于UNIX套接字的REST API进行通信。Docker Compose是另一个Docker客户端,允许用户使用一组容器来组成应用程序。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20230921170151004.png" alt="image-20230921170151004"></p>
<h2 id="Docker对象"><a href="#Docker对象" class="headerlink" title="Docker对象"></a>Docker对象</h2><h3 id="Docker镜像"><a href="#Docker镜像" class="headerlink" title="Docker镜像"></a>Docker镜像</h3><p>Docker镜像是一个用于创建Docker容器的模板,通常一个镜像是基于其他镜像来构建的。通过创建Dockerfile可以来创建一个镜像,每条指令用于构建Docker中的不同层次,当改变某一层时将会重建这个镜像,此时只有受到影响的层次会改变。</p>
<p>一个镜像包含支持容器运行的所有东西,如:所有依赖、配置、脚本、二进制文件、环境变量、元数据等等。</p>
<h3 id="Docker容器"><a href="#Docker容器" class="headerlink" title="Docker容器"></a>Docker容器</h3><p>Docker容器是一个镜像的运行实例,它根据镜像来定义其初始化配置。容器可以连接多个网络,关联数据、甚至当前容器状态可以被用于复制来创建一个镜像。</p>
<p>Docker容器具有良好的隔离性,可以与其他容器进行隔离依赖,专注于运行自己的软件。隔离性的实现依赖于Linux的命名空间与cgroups。<br>与Linux中的<span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9saW51eC9saW51eC1jb21tLWNocm9vdC5odG1s">chroot<i class="fa fa-external-link-alt"></i></span>命令类似,可看作是它的一个升级版。</p>
<p>Docker容器可用于创建一个安全的沙盒环境,其文件系统来源于镜像文件。容器在<code>chroot</code>的基础上添加了一些额外的功能,来保证环境更为安全。</p>
<h3 id="Docker仓库"><a href="#Docker仓库" class="headerlink" title="Docker仓库"></a>Docker仓库</h3><p>Docker仓库用于存储Docker镜像,如<span class="exturl" data-url="aHR0cHM6Ly9odWIuZG9ja2VyLmNvbS8=">Docker Hub<i class="fa fa-external-link-alt"></i></span>。<code>docker pull</code>从远程仓库中拉取所需的镜像,<code>docker push</code>则将镜像推送到仓库。</p>
<h3 id="Docker容器创建过程"><a href="#Docker容器创建过程" class="headerlink" title="Docker容器创建过程"></a>Docker容器创建过程</h3><p>假设执行以下的命令来创建一个<code>ubuntu</code>容器:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">docker run -i -t ubuntu /bin/bash</span><br></pre></td></tr></table></figure>
<ol>
<li>如果在本地没有<code>ubuntu</code>容器,Docker将从配置的远程仓库中拉取<code>docker pull ubuntu</code>镜像。</li>
<li>接下来Docker会新建一个容器,<code>docker container create</code>。</li>
<li>Docker为该容器分配一个读写文件系统,其允许运行的容器在其中创建或修改文件与目录。</li>
<li>接下来Docker将创建一个网络接口,默认情况下容器可通过主机的网络连接到外部网络。</li>
<li>Docker启动该容器,由于<code>-i</code>,<code>-t</code>命令代表该容器可交互与连接到终端,后续将直接运行<code>/bin/bash</code>命令并打开<code>bash</code>终端,接受输入,返回输出。</li>
<li>当使用<code>exit</code>命令来退出终端时,容器虽然停止了但不会被移除,其可以被重启或者是移除。</li>
</ol>
<h3 id="参考链接"><a href="#参考链接" class="headerlink" title="参考链接"></a>参考链接</h3><p><span class="exturl" data-url="aHR0cHM6Ly9kb2NzLmRvY2tlci5jb20vZ2V0LXN0YXJ0ZWQvb3ZlcnZpZXcv">Docker Overview<i class="fa fa-external-link-alt"></i></span></p>
]]></content>
<tags>
<tag>Docker</tag>
</tags>
</entry>
<entry>
<title>ginTodoList——基于gin和gorm的待办小清单</title>
<url>/ginTodoList.html</url>
<content><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p><a href>ginTodoList</a>是一款基于Vue.js、Golang、gin、gorm的的待办小清单,项目可通过前后端分离的方式进行部署,并按照web开发的规范对项目的功能进行了划分。项目的<span class="exturl" data-url="aHR0cDovLzEyMC4yNC4yMzIuNzk6ODA4MC8jLw==">demo<i class="fa fa-external-link-alt"></i></span>演示效果如下:</p>
<p>最近在整理该<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2NoYXJmb2xlL2dpblRvZG9MaXN0">项目<i class="fa fa-external-link-alt"></i></span>,也一并将其同步到博客当中以作记录。项目学习自<span class="exturl" data-url="aHR0cHM6Ly93d3cuYmlsaWJpbGkuY29tL3ZpZGVvL0JWMWdKNDExcDd4Qy8/dmRfc291cmNlPTFmMDYxY2VkNGU5ZDg5NTNmNTEzMzIxYzRhNDRmODk3">该课程<i class="fa fa-external-link-alt"></i></span>,并在原基础上增加了记录待办清单的创建日期功能。下面是项目的详细文档。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20230114192014784.png" alt="image-20230114192014784"></p>
<a id="more"></a>
<h2 id="1-项目部署"><a href="#1-项目部署" class="headerlink" title="1. 项目部署"></a>1. 项目部署</h2><ul>
<li><p>下载源代码并切换目录</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line">git clone</span><br><span class="line">cd ./ginTodoList</span><br></pre></td></tr></table></figure>
</li>
<li><p>前端部署(请确保本地的8080和9090端口能使用)</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 进入目录</span></span><br><span class="line">cd codeOfFrontend</span><br><span class="line"><span class="meta">#</span><span class="bash"> 依赖安装</span></span><br><span class="line">npm install</span><br><span class="line"><span class="meta">#</span><span class="bash"> 前端项目启动</span></span><br><span class="line">npm run serve</span><br></pre></td></tr></table></figure>
</li>
<li><p>数据库配置</p>
<p>需要环境中有MySQL数据库,同时数据库的用户名、密码、端口号和数据库名称需要与下面的配置相对应。下面提供一种快速使用<span class="exturl" data-url="aHR0cHM6Ly93d3cucnVub29iLmNvbS9kb2NrZXIvdWJ1bnR1LWRvY2tlci1pbnN0YWxsLmh0bWw=">docker<i class="fa fa-external-link-alt"></i></span>安装MySQL8.0.19的方法:</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 在本地的33306端口运行一个名为mysql8019,root为用户名,密码为mysql1234的MySQL容器环境</span></span><br><span class="line">docker run --name mysql8019 -p 33306:3306 -e MYSQL_ROOT_PASSWORD=mysql1234 -d mysql:8.0.19</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 启动一个MySQL客户端,连接上面的MySQL容器,密码为上一步指定的密码mysql1234</span></span><br><span class="line">docker run -it --network host --rm mysql mysql -h127.0.0.1 -P33306 --default-character-set=utf8mb4 -uroot -p</span><br><span class="line"></span><br><span class="line"><span class="meta">#</span><span class="bash"> 创建项目用到的ginTodoList数据库,也可以是其他数据库,但需要修改配置文件</span></span><br><span class="line">CREATE DATABASE ginTodoList DEFAULT CHARSET=utf8mb4;</span><br></pre></td></tr></table></figure>
</li>
<li><p>后端服务配置</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 打开conf文件夹下的config.ini文件</span></span><br><span class="line"><span class="meta">#</span><span class="bash"> 下面的信息和</span></span><br><span class="line">[mysql]</span><br><span class="line">; 你的数据库用户名</span><br><span class="line">user = root</span><br><span class="line">; 你的数据库密码</span><br><span class="line">password = mysql1234</span><br><span class="line">; 你的IP(一般输入127.0.0.1即可)</span><br><span class="line">host = 127.0.0.1</span><br><span class="line">; 你的数据库端口号</span><br><span class="line">port = 33306</span><br><span class="line">; 你的数据库名称</span><br><span class="line">db = ginTodoList</span><br></pre></td></tr></table></figure>
</li>
<li><p>后端部署</p>
<figure class="highlight shell"><table><tr><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> 返回项目根目录</span></span><br><span class="line">cd ..</span><br><span class="line"><span class="meta">#</span><span class="bash"> 项目启动</span></span><br><span class="line">./ginTodoList conf/config.ini</span><br><span class="line"><span class="meta">#</span><span class="bash"> 如改变了代码则需要重新编译</span></span><br><span class="line">go build</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="2-项目架构"><a href="#2-项目架构" class="headerlink" title="2. 项目架构"></a>2. 项目架构</h2><p><strong>ginTodoList</strong></p>
<p><strong>├── codeOfFrontend</strong> // 前端部分代码</p>
<p><strong>├── conf</strong> // 配置文件夹</p>
<p>│ └── config.ini // 配置文件</p>
<p><strong>├── controller</strong> // 控制器:负责处理请求与调用逻辑</p>
<p>│ └── controller.go</p>
<p><strong>├── dao</strong> // dao (Data Access Object) 层:负责控制数据库</p>
<p>│ └── mysql.go</p>
<p><strong>├── models</strong> // 模型层:负责与数据库进行交互,实现ORM中的逻辑</p>
<p>│ └── todo.go</p>
<p>├── <strong>routers</strong> // 路由层:负责注册路由并调用控制器的函数</p>
<p>│ └── routers.go</p>
<p>└── <strong>setting</strong> // 配置器:根据配置文件对应用中的服务进行配置</p>
<p> └── setting.go</p>
<p><strong>├── main.go</strong> //应用主入口</p>
<p>├── ginTodoList // 应用的可执行文件</p>
<p>├── go.mod</p>
<p>├── go.sum</p>
<h2 id="3-项目各模块解析"><a href="#3-项目各模块解析" class="headerlink" title="3. 项目各模块解析"></a>3. 项目各模块解析</h2><p>项目主要为前后端分离架构,前端由Vue.js和Element UI实现,后端主要通过gin实现业务逻辑、gorm实现数据库操作。后端部分可以分为配置文件(conf)、控制器(controller)、数据持久层(dao)、模型层(models)、路由层(routers)、设置文件(setting)。</p>
<ol>
<li><p>配置文件(conf):配置文件夹,存储MySQL配置文件。</p>
</li>
<li><p>设置文件(setting):定义结构体,读入配置文件中的参数,从而对应用中的各项服务进行配置,当前应用仅包含MySQL服务。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 定义结构体,读入配置文件中的参数,从而对应用中的各项服务进行配置</span></span><br><span class="line"><span class="comment">// 当前应用仅包含MySQL服务</span></span><br><span class="line"><span class="keyword">package</span> setting</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"gopkg.in/ini.v1"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> Conf = <span class="built_in">new</span>(AppConfig)</span><br><span class="line"></span><br><span class="line"><span class="comment">// AppConfig 应用程序配置</span></span><br><span class="line"><span class="keyword">type</span> AppConfig <span class="keyword">struct</span> {</span><br><span class="line"> *MySQLConfig <span class="string">`ini:"mysql"`</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// MySQLConfig 数据库配置</span></span><br><span class="line"><span class="comment">// 设置MySQL的用户名、密码,数据库名称、IP地址、端口号</span></span><br><span class="line"><span class="keyword">type</span> MySQLConfig <span class="keyword">struct</span> {</span><br><span class="line"> User <span class="keyword">string</span> <span class="string">`ini:"user"`</span></span><br><span class="line"> Password <span class="keyword">string</span> <span class="string">`ini:"password"`</span></span><br><span class="line"> DB <span class="keyword">string</span> <span class="string">`ini:"db"`</span></span><br><span class="line"> Host <span class="keyword">string</span> <span class="string">`ini:"host"`</span></span><br><span class="line"> Port <span class="keyword">int</span> <span class="string">`ini:"port"`</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将conf文件夹下的配置文件config.ini映射到Conf中</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Init</span><span class="params">(file <span class="keyword">string</span>)</span> <span class="title">error</span></span> {</span><br><span class="line"> <span class="keyword">return</span> ini.MapTo(Conf, file)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>数据持久层(dao):负责初始化数据库连接与关闭数据库连接。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// dao(Data Access Object):负责初始化数据库连接与关闭数据库连接</span></span><br><span class="line"><span class="keyword">package</span> dao</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"fmt"</span></span><br><span class="line"> <span class="string">"ginTodoList/setting"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"github.com/jinzhu/gorm"</span></span><br><span class="line"> _ <span class="string">"github.com/jinzhu/gorm/dialects/mysql"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义数据库对象</span></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line"> DB *gorm.DB</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化MySQL数据库,指定数据库用户、密码、IP、端口号和数据库名称信息</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">InitMySQL</span><span class="params">(cfg *setting.MySQLConfig)</span> <span class="params">(err error)</span></span> {</span><br><span class="line"> dsn := fmt.Sprintf(<span class="string">"%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local"</span>,</span><br><span class="line"> cfg.User, cfg.Password, cfg.Host, cfg.Port, cfg.DB)</span><br><span class="line"> DB, err = gorm.Open(<span class="string">"mysql"</span>, dsn)</span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> DB.DB().Ping()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 关闭数据库</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">Close</span><span class="params">()</span></span> {</span><br><span class="line"> DB.Close()</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>模型层(models):与dao层中的数据库对象进行交互,负责实现数据库中某张表的增删改查等基本操作。通过gorm,将type与MySQL的某张表进行映射,从而通过操作type来操作数据表。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">与dao层中的数据库进行交互,负责数据库中某张表的增删改查等基本操作</span></span><br><span class="line"><span class="comment">通过gorm,将type与MySQL的某张表进行映射,通过操作type来操作数据表</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">package</span> models</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"ginTodoList/dao"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// Todo Model 默认映射到当前数据库中的todos表</span></span><br><span class="line"><span class="keyword">type</span> Todo <span class="keyword">struct</span> {</span><br><span class="line"> ID <span class="keyword">int</span> <span class="string">`json:"id"`</span></span><br><span class="line"> Title <span class="keyword">string</span> <span class="string">`json:"title"`</span></span><br><span class="line"> Date <span class="keyword">string</span> <span class="string">`json:"date"`</span></span><br><span class="line"> Status <span class="keyword">bool</span> <span class="string">`json:"status"`</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建一个todo:直接传入一个结构体进行创建,有错误则返回</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">CreateATodo</span><span class="params">(todo *Todo)</span> <span class="params">(err error)</span></span> {</span><br><span class="line"> err = dao.DB.Create(&todo).Error</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取所有todo:传入todo结构体指针数组todoList,获取当前todos表中的所有数据</span></span><br><span class="line"><span class="comment">// 同时更新todoList,有错误则返回</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetAllTodo</span><span class="params">()</span> <span class="params">(todoList []*Todo, err error)</span></span> {</span><br><span class="line"> <span class="keyword">if</span> err = dao.DB.Find(&todoList).Error; err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span>, err</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取某个todo:通过id获取某个todo事项,有错误则返回</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetATodo</span><span class="params">(id <span class="keyword">string</span>)</span> <span class="params">(todo *Todo, err error)</span></span> {</span><br><span class="line"> todo = <span class="built_in">new</span>(Todo)</span><br><span class="line"> <span class="keyword">if</span> err = dao.DB.Debug().Where(<span class="string">"id=?"</span>, id).First(todo).Error; err != <span class="literal">nil</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">nil</span>, err</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新某个todo:传入一个todo结构体指针,更新todos表中对应的todo项</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">UpdateATodo</span><span class="params">(todo *Todo)</span> <span class="params">(err error)</span></span> {</span><br><span class="line"> err = dao.DB.Save(todo).Error</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除某个todo:传入一个id,删除对应id的todo项</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">DeleteATodo</span><span class="params">(id <span class="keyword">string</span>)</span> <span class="params">(err error)</span></span> {</span><br><span class="line"> err = dao.DB.Where(<span class="string">"id=?"</span>, id).Delete(&Todo{}).Error</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>路由层(routers):负责注册路由,引导请求到对应的控制函数中。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 负责注册应用中的路由,并引导请求到对应的控制函数中</span></span><br><span class="line"><span class="keyword">package</span> routers</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"ginTodoList/controller"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"github.com/gin-gonic/gin"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">SetupRouter</span><span class="params">()</span> *<span class="title">gin</span>.<span class="title">Engine</span></span> {</span><br><span class="line"> <span class="comment">// 初始化路由引擎</span></span><br><span class="line"> r := gin.Default()</span><br><span class="line"> <span class="comment">// 初始化v1路由组,用于处理对应的请求</span></span><br><span class="line"> v1Group := r.Group(<span class="string">"v1"</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 操作待办事项</span></span><br><span class="line"> <span class="comment">// 添加某一个待办事项</span></span><br><span class="line"> v1Group.POST(<span class="string">"/todo"</span>, controller.CreateTodo)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 查看所有的待办事项</span></span><br><span class="line"> v1Group.GET(<span class="string">"/todo"</span>, controller.GetTodoList)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 修改某一个待办事项</span></span><br><span class="line"> v1Group.PUT(<span class="string">"/todo/:id"</span>, controller.UpdateATodo)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 删除某一个待办事项</span></span><br><span class="line"> v1Group.DELETE(<span class="string">"/todo/:id"</span>, controller.DeleteATodo)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 注册好路由信息后,返回路由引擎</span></span><br><span class="line"> <span class="keyword">return</span> r</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>控制器(controller):负责实现控制路由中对应的各个函数,其中各个函数只调用具体的逻辑,而不进行实现。</p>
<figure class="highlight go"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 控制器:负责实现控制路由中对应的各个函数,其中各个函数只调用具体的逻辑,而不进行实现</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> url --> controller --> models</span></span><br><span class="line"><span class="comment"> 请求来了 --> 控制器 --> 模型层的增删改查</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">package</span> controller</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> (</span><br><span class="line"> <span class="string">"ginTodoList/models"</span></span><br><span class="line"> <span class="string">"net/http"</span></span><br><span class="line"></span><br><span class="line"> <span class="string">"github.com/gin-gonic/gin"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 下面的操作主要逻辑为:接收并处理前端请求,调用models层中的增删改查基础功能进行处理</span></span><br><span class="line"><span class="comment">// 创建一个待办事项</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">CreateTodo</span><span class="params">(c *gin.Context)</span></span> {</span><br><span class="line"> <span class="comment">// 定义一个结构体用于接收前端传来的信息</span></span><br><span class="line"> <span class="keyword">var</span> todo models.Todo</span><br><span class="line"> <span class="comment">// 将JSON信息绑定到结构体中</span></span><br><span class="line"> c.BindJSON(&todo)</span><br><span class="line"> <span class="comment">// 调用CreateATodo()函数,添加一个todo事项到todos表</span></span><br><span class="line"> err := models.CreateATodo(&todo)</span><br><span class="line"> <span class="comment">// 如有错误则返回错误信息</span></span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{</span><br><span class="line"> <span class="string">"error"</span>: err.Error(),</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 无错误则以JSON形式返回todo信息给前端</span></span><br><span class="line"> c.JSON(http.StatusOK, todo)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取所有待办事项</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">GetTodoList</span><span class="params">(c *gin.Context)</span></span> {</span><br><span class="line"> <span class="comment">// 调用模型层GetAllTodo()函数获取所有待办事项</span></span><br><span class="line"> todoList, err := models.GetAllTodo()</span><br><span class="line"> <span class="comment">// 如有错误则返回错误信息</span></span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{</span><br><span class="line"> <span class="string">"error"</span>: err.Error(),</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 无错误则以JSON形式返回todo列表给前端</span></span><br><span class="line"> c.JSON(http.StatusOK, todoList)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 更新一个待办事项</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">UpdateATodo</span><span class="params">(c *gin.Context)</span></span> {</span><br><span class="line"> <span class="comment">// 获取当前id对应的待办事项</span></span><br><span class="line"> id, ok := c.Params.Get(<span class="string">"id"</span>)</span><br><span class="line"> <span class="comment">// 如果没有传过来id,则返回错误</span></span><br><span class="line"> <span class="keyword">if</span> !ok {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{</span><br><span class="line"> <span class="string">"error"</span>: <span class="string">"无效的id"</span>,</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 调用GetATodo()函数,首先查询出该id对应的待办事项</span></span><br><span class="line"> todo, err := models.GetATodo(id)</span><br><span class="line"> <span class="comment">// 有错误则返回</span></span><br><span class="line"> <span class="keyword">if</span> err != <span class="literal">nil</span> {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{</span><br><span class="line"> <span class="string">"error"</span>: err.Error(),</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 将前端传来的json绑定到当前todo中</span></span><br><span class="line"> c.BindJSON(&todo)</span><br><span class="line"> <span class="comment">// 调用UpdateATodo()函数更新todo事项</span></span><br><span class="line"> <span class="keyword">if</span> err = models.UpdateATodo(todo); err != <span class="literal">nil</span> {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{</span><br><span class="line"> <span class="string">"error"</span>: err.Error(),</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 返回更新后的todo给前端(这里更新的主要是status字段,已完成或未完成)</span></span><br><span class="line"> c.JSON(http.StatusOK, todo)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除一个待办事项</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">DeleteATodo</span><span class="params">(c *gin.Context)</span></span> {</span><br><span class="line"> <span class="comment">// 获取当前id对应的待办事项</span></span><br><span class="line"> id, ok := c.Params.Get(<span class="string">"id"</span>)</span><br><span class="line"> <span class="comment">// 如果没有传过来id,则返回错误</span></span><br><span class="line"> <span class="keyword">if</span> !ok {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{<span class="string">"error"</span>: <span class="string">"无效的id"</span>})</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 调用DeleteATodo()函数删除todo,删除成功则返回deleted,否则返回错误</span></span><br><span class="line"> <span class="keyword">if</span> err := models.DeleteATodo(id); err != <span class="literal">nil</span> {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{<span class="string">"error"</span>: err.Error()})</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> c.JSON(http.StatusOK, gin.H{id: <span class="string">"deleted"</span>})</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
</ol>
]]></content>
<categories>
<category>Golang</category>
<category>后端开发</category>
</categories>
<tags>
<tag>MySQL</tag>
<tag>Golang</tag>
<tag>Project</tag>
<tag>Gin</tag>
<tag>Web</tag>
</tags>
</entry>
<entry>
<title>2021计算机保研面试经验与建议(中山、复旦、北航、浙大工程师、华东师、华工等院校)</title>
<url>/baoyan-experience.html</url>
<content><![CDATA[<h3 id="一、写在前面"><a href="#一、写在前面" class="headerlink" title="一、写在前面"></a>一、写在前面</h3><p>从大一下学期树立保研目标到现在,一眨眼已过去两年半了,这两年半的时间里获得了不少前辈们的帮助,如今也想将自己的保研经历记录下来,于是整理了这篇一万多字的计算机保研经验贴,希望能帮助更多有需要的学弟学妹们~</p>
<a id="more"></a>
<h3 id="二、疫情后的保研形势与高校情况"><a href="#二、疫情后的保研形势与高校情况" class="headerlink" title="二、疫情后的保研形势与高校情况"></a>二、疫情后的保研形势与高校情况</h3><p><strong>保研形势:</strong>由于下半年疫情仍然在不同地区出现反弹,因此大部分院校的夏令营和预推免都以<strong>线上</strong>的形式开展,当然也有一小部分院校是线下开展的,比如:中科大的夏令营、厦门大学的夏令营等等。线上推免考核方式的有利也有弊:</p>
<ul>
<li><strong>利</strong>:大大降低了学生的考核成本,足不出户即可参与大江南北的高校考核,甚至还可以在一天时间内<strong>多线程</strong>参与多个高校的考核</li>
<li><strong>弊</strong>:高校海收、学生海投,这种情况往往会导致想去的人无法通过梦校的初审,而各个院校却招收了一大批过于优秀的学生,最终导致被鸽</li>
</ul>
<p><strong>高校情况:</strong>由于今年是第二年以线上的形式开展招生,所以不少院校都学聪明了,为了满足自己的招生需求,选择超发 offer,但也有不少学校错误预估形式,导致最后一天被鸽穿临时招人,亦或是干脆接受了招不满的事实。我认为,今年所有高校的招生情况都可以分为以下这三个类型,当然也有一些高校可能处于这三类之间:</p>
<ul>
<li><strong>第一类,正常高校</strong>:这类高校一般实力较强(<strong>清华</strong>、<strong>北大</strong>、<strong>上交</strong>等等),招生方式和招生人数都与疫情前的线下招生差别不大,发放的 offer 数与实际的招生人数也较为相近。</li>
<li><strong>第二类,海王高校</strong>:这类高校今年较多,特征为在夏令营发放<strong>大量优营和候补优营</strong>,抑或是<strong>夏令营发放大量 offer 的前提仍然进行预推免</strong>。不过站在高校的角度来说,这也是能够理解的,毕竟高校的目标就是每年能招够人,现在线上招生形式风云莫测,只能通过海发 offer 的行为来保证自己的利益了。这类高校数目较多,有:<strong>南大</strong>(夏令营有大量候补的情况下还进行了预推免)、<strong>湖大</strong>(推免q群人数多达四位数、且招生办老师称一切皆有可能)、<strong>中南</strong>(夏令营超发)….</li>
<li><strong>第三类,被鸽高校</strong>:这类高校的特点为:<strong>夏令营或预推免初审 bar 太高,本地高校的同学招很少而招了很多不会来的大佬和鸽子</strong>;又或者是不忍心当海王超发 offer,最终被鸽王教训了。这类高校数目也不少,有:<strong>中山</strong>(门槛较高)、<strong>华工</strong>(门槛较高且招人少,我们学院夏令营一个没过,预推免初审只过了少数)、<strong>华科</strong>(门槛较高,很多 211 高校初审只过一个)等等……<br>中山和华工是填系统那一天被鸽了还在打电话叫人面试的,华科也在填报系统前的半小时给初审都没过的我发了个报名邮件。除了这些以外,我相信还有不少学校是被鸽的,比如门槛同样高的北理工、武大等等,只不过可能被鸽的人较少或者招生办比较佛系,九推不再额外招了。</li>
</ul>
<p><strong>我的背景与定位:</strong>既然选择了读研并保研,就必须要对自己有着清晰的定位,并根据此制定目标。(如何<strong>进行定位并制定目标</strong>?可以看文章的<strong>第五大点</strong>)我的目标就是争取拿到一个中九及以上院校的学硕,以后的选择也会多一点(进体制、读博等等)。我的基本条件如下:</p>
<ul>
<li><strong>背景:</strong>华南某211(非工科院校)、计算机科学与技术专业、GPA:4.00 / 5.00 (前六学期)、GPA 排名:4 / 227 (1.8 %)、推免排名:3 / 330 (全级人数)</li>
<li><strong>英语:</strong>四级 570+、六级 550+</li>
<li><strong>竞赛:</strong>国赛数模省一国二、中国高校计算机大赛省一国三、蓝桥杯省一国三、还有若干其他奖项</li>
<li><strong>科研:</strong>省级大创主持人(优秀结题并获奖)、大三上一段两个月的实验室经历(无产出)、大三下一段数月的科研经历(夏令营开始前一篇 SCI 二区论文在投、预推免开始前论文二审)</li>
</ul>
<p>根据学校里往届前辈的情况,我认为去中九应该是没问题的,但如果想要拿到中九学硕或中九以上学校的 offer 就要看自己的造化了。</p>
<hr>
<h3 id="三、院校申请情况"><a href="#三、院校申请情况" class="headerlink" title="三、院校申请情况"></a>三、院校申请情况</h3><p><strong>夏令营:</strong></p>
<ul>
<li>中南大数据研究院(通过,专硕)</li>
<li>华东师范大学软院(通过,并可以申请学硕)</li>
<li>中山大学计算机学院(通过,排名 51-100,最终拿到学硕)</li>
<li>中山大学系统科学与工程学院计科学硕(通过)</li>
<li>浙大工程师学院人工智能药学项目(通过、计算机专硕、工院只有专硕)</li>
<li>川大计算机学院、北理工网安学院、西工大软院(皆入营、但因个人选择和时间冲突没参加)</li>
</ul>
<p><strong>预推免:</strong></p>
<ul>
<li>中山大学网络空间安全学院(通过,学硕)</li>
<li>华南理工大学计算机学院(通过,学硕)</li>
<li>西交软院(没通过,老师发 offer 主要看你来的意愿,下面细说)</li>
<li>复旦大学计算机学院(大数据方向学硕候补第14)</li>
<li>中山大学人工智能学院(通过,学硕)</li>
<li>北航软院(候补第三,最后老师告知可以拿到专硕,但学硕差两名)</li>
<li>武大网安(通过初审,但因个人选择和时间冲突没参加)</li>
<li>北航计算机学院(通过初审,但因为和复旦时间冲突,且夏令营优营比较多所以预推免拿到学硕的机会小,遂放弃)</li>
</ul>
<p><strong>最终去向:</strong>中山大学计算机学院学硕</p>
<h3 id="夏令营"><a href="#夏令营" class="headerlink" title="夏令营"></a>夏令营</h3><h4 id="1、中南大数据研究院"><a href="#1、中南大数据研究院" class="headerlink" title="1、中南大数据研究院"></a>1、中南大数据研究院</h4><p>考核的方式为纯面试,面试的内容如下:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211001203006492-16340280427391.png" alt="image-20211001203006492"></p>
<p>提问的问题主要涉及<strong>英语问题和项目问题</strong>:</p>
<ul>
<li>一分钟自我介绍(英语)</li>
<li>说明深度学习和机器学习的区别(英语)</li>
<li>如何理解多模态分析(项目相关)</li>
<li>体质有多少种分类(项目相关)</li>
<li>中医和西医哪一个好(项目相关)</li>
<li>论文的主要工作(项目相关)</li>
</ul>
<p>因为这个学院主要是搞 <strong>AI+医疗</strong> 方面的,而我本科期间有两段科研经历正好是和这个主题相关的,因此提问主要集中在项目方面了。这种纯问项目的面试方式还是挺多的,毕竟研究生阶段主要也是发论文和搞项目为主,所以老师比较看重你的<strong>科研能力</strong>。当被问到项目相关问题的时候,不需要担心,只要把项目相关的<strong>算法、原理和流程</strong>等都复习一遍,就基本可以做到对答如流了。至于英语问题,如果某个问题自己不太答得上来的话,可以有意识地把陌生的概念<strong>转移</strong>到自己熟悉的概念,最后流利地表达出来。英语问题的答案一般不是最重要的,只要<strong>答得靠边</strong>即可,老师更想考察的是你的<strong>口语能力</strong>。</p>
<p>面试持续了十分钟左右就结束了,还算是比较简单轻松的,两周后出了结果,拿到了第一个优营。</p>
<h4 id="2、华东师软院"><a href="#2、华东师软院" class="headerlink" title="2、华东师软院"></a>2、华东师软院</h4><p>华东师软院考核的方式较为多元,需要<strong>联系导师看论文、做论文笔记,机试和面试</strong>,其中面试还需要介绍导师的一篇论文。</p>
<p><strong>导师考核:</strong></p>
<p>建议选择研究方向较为熟悉的教师,因为教师的考察一般以看论文、写论文笔记的形式开展。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211001215635454-16340281229683.png" alt="image-20211001215635454"></p>
<p><strong>机试:</strong></p>
<p>使用华东师本校的 OJ 平台进行考核,考核持续一个小时,题目有两题,打分的形式为分点给分。考核的题目难度适中,一题是无向图的多源最短路、另一题应该是动态规划。两题满分共 200,大概在 70 分以上便有机会进入复试(也有一部分分数较低的同学进入了复试,应该是导师考核的评价较好)。</p>
<p><strong>面试:</strong></p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211001220736263-16340281282975.png" alt="image-20211001220736263"></p>
<p>面试的内容除了上述的部分以外,还包括了一些英文问题,比如:介绍一本你喜欢的书。此外,还问到了你为什么选择来华东师?(这类问题是有窍门的,一律答学校很好、学科师资很强、学校学习氛围很好即可)剩下的问题都基本<strong>围绕教师的论文解读和自己的简历</strong>展开了,我当时面试的时候刚好我的考核老师也在,因此她问了我很多关于论文的问题,我也回答得比较好,最终拿到了优营。</p>
<h4 id="3、中山大学计算机学院"><a href="#3、中山大学计算机学院" class="headerlink" title="3、中山大学计算机学院"></a>3、中山大学计算机学院</h4><p>中山大学计算机学院的考核方式包括机考(<strong>不算入总得分,但作为参考</strong>)和面试,本年夏令营的安排请见下图:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211012165223457.png" alt="image-20211012165223457"></p>
<p><strong>机考:</strong></p>
<p>机考一共有十题,每题100分且按样例给分(编译通过也有10分)。题目的内容包括了数据结构、算法、<strong>C++面向对象</strong>方面的知识(继承、多态、虚函数等等)、使用的是学院自己的<span class="exturl" data-url="aHR0cHM6Ly9tYXRyaXguc3lzdS5lZHUuY24vbG9naW4=">oj平台<i class="fa fa-external-link-alt"></i></span>,在正式机考前会有一个模拟测试,可以借模拟的机会提前熟悉一下正式的考试环境。<strong>C++方面的题目有3题</strong>左右,提前准备一下还是比较好拿分的,机考的平均得分大概是300-400分左右的样子,如果考得比较高的话,那面试可能会相对轻松,如果机考发挥得不好,也不要灰心,面试好好表现的话影响也不会有什么问题的!(我就是机考发挥得一般般,但是成绩也比较靠前,最终拿到了学硕)</p>
<p>不过,不得不吐槽一下机考的平台和方式,平台<strong>没有调试功能</strong>,只能通过提交代码的方式来检验代码是否正确,而且因为夏令营参加的人数比较多,所以导致判题的时间比较长,一道题需要1-2分钟左右,有时候等了一两分钟才发现语法错误,就比较尴尬。而且机考的过程也不能用本地 IDE,必须使用平台,所以大家要提前做好心理准备,在模拟测试的时候提前适应一下。</p>
<p><strong>面试:</strong></p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211012172604985.png" alt="image-20211012172604985"></p>
<p>面试的内容主要涉及四个部分:<strong>PPT自我介绍、英语考核、简历提问、专业知识考核</strong>,PPT自我介绍的内容详见上图。</p>
<ul>
<li><strong>英语考核:</strong>英语自我(项目)介绍、你为什么要来中山大学?</li>
<li><strong>简历提问:</strong>项目为什么用机器学习而不用深度学习?项目的创新点等等…</li>
<li><strong>专业知识考核:</strong>1、人工智能领域的顶会期刊有什么?2、Oracle算法有听过吗?(没答出来,还以为是Oracle数据库…)3、看到你学过高数,那说一下你高数都学了什么内容?4、初等函数是什么?基本初等函数有哪几种?</li>
</ul>
<p>面试组里一共有六个人,整个面试流程还是比较紧凑的,数学方面的知识需要提前回顾一下。我应该只有 Oracle 算法那个问题没答出来,最后面试成绩是 88.2,最初排名70左右,最后候补到了前40左右,可以参考一下。</p>
<h4 id="4、中山大学系统科学与工程学院"><a href="#4、中山大学系统科学与工程学院" class="headerlink" title="4、中山大学系统科学与工程学院"></a>4、中山大学系统科学与工程学院</h4><p>中大系统院是一个比较新的学院,2019年才开始招生,所以夏令营报名的时候也没有多少人知道,招收80人,只有177人报名,学院的研究方向偏向硬件,有计科、数学和信通这三个专业,<strong>都是学硕</strong>。</p>
<p>夏令营的活动考核为两阶段面试:第一阶段为各方向学生分别进行<strong>自我介绍</strong>,介绍是当着所有人的面进行的,持续两分钟。第二阶段为正式面试,持续十分钟,前五分钟为<strong>PPT自我介绍</strong>,后五分钟为<strong>导师提问</strong>,提问的问题有以下几个:</p>
<ul>
<li>请用英文介绍一下你的项目</li>
<li>项目具体的工作和创新点</li>
<li>你本科期间参加了不少比赛,说一下你用到的印象最深的一个算法吧</li>
<li>说一下 c 和 c++ 的区别</li>
</ul>
<p>整个面试过程还是比较轻松的,尽量展示出自己<strong>健谈自信</strong>的一面就好了。面试结束一天后就公布了优营名单,成功通过了。不过学院的计科学硕招生名额只有6个,优营给了20个,预推免还继续进行招生,且最后学院也没有公布夏令营和预推免的考核分数,有一些夏令营优营的同学最终也没有被录取,所以学院还是<strong>比较海的</strong>,即便拿到优营也要小心。</p>
<h4 id="5、浙江大学工程师学院人工智能药学项目"><a href="#5、浙江大学工程师学院人工智能药学项目" class="headerlink" title="5、浙江大学工程师学院人工智能药学项目"></a>5、浙江大学工程师学院人工智能药学项目</h4><p>浙大工程师学院也是一个比较新的学院,<strong>整个学院都为专硕</strong>。学院以项目为单位进行招生,每个项目都与浙大的另外的专业学院进行合作培养,导师来自专业学院,学习工作则主要在工程师学院这边开展。学院和计算机相关的项目也挺多的,值得尝试。</p>
<p>浙大工院的考核只有面试,面试时间大概是15分钟,面试首先会让你分别进行<strong>中英文的自我简介</strong>,在后续的专业知识部分,我主要被问到的都是<strong>简历上项目相关的内容</strong>,没有被问到纯理论的专业知识。所以本科期间好好做项目并将其呈现在简历上是很重要的,同时在面试前也要好好准备项目方面的问题,才能做到对答如流,如果缺乏项目方面的经验,可能就需要多准备一下专业理论知识。面试结束一两天后便公布了结果,我很幸运地拿到了优营且排名第一。</p>
<p>最后,要不得不<strong>安利一下</strong>工程师学院,学院的招生流程公开透明,成绩公布也十分迅速,群里的学长和老师都<strong>非常热心</strong>,从夏令营开始到最后一天推免填报系统都一直在为同学们答疑解惑。而且,浙大工院的研究生也可以申请读专业学院(计算机学院)的博士,并且在硕士阶段也能找到部分浙大计算机学院的老师指导(夏令营优营后老师还会主动联系你)。另外,工院更看重个人的实践能力,所以即便是<strong>本科学校和绩点排名不这么占优</strong>的同学也有机会通过初筛。如果是想来浙里学计算机,又觉得浙里的计算机学院门槛太高的话,可以来尝试一下工程师学院!</p>
<h3 id="预推免"><a href="#预推免" class="headerlink" title="预推免"></a>预推免</h3><h4 id="1、中山大学网络安全学院"><a href="#1、中山大学网络安全学院" class="headerlink" title="1、中山大学网络安全学院"></a>1、中山大学网络安全学院</h4><p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013095302947.png" alt="image-20211013095302947"></p>
<p>中大网安院是今年新开的学院,考核的方式也是纯面试,主要的内容包括5-10分钟的PPT自我介绍,之后是加起来共十分钟的<strong>英语回答、简历提问和专业知识</strong>。</p>
<ul>
<li><strong>英语回答</strong>:用英文回答一下研究生阶段的重要性</li>
<li><strong>简历提问</strong>:Adaboost、SVM的原理,两者的区别;卷积核和卷积的原理;还有若干与项目相关的问题,问得比较深</li>
<li><strong>专业知识</strong>:并发和并行的区别?如何求圆周率?</li>
</ul>
<p>虽然网安学院是新开的,但面试给我的感觉是有一定难度的,有点像是压力面,会追着一个问题逐渐深入问下去,面对压力面一定不能认怂,老师的目的是检验你自身对项目的理解和抗压能力。</p>
<h4 id="2、华南理工大学计算机学院"><a href="#2、华南理工大学计算机学院" class="headerlink" title="2、华南理工大学计算机学院"></a>2、华南理工大学计算机学院</h4><p>华工的考核也只有面试这一项,面试的流程和中大网安类似,先进行5分钟之内的PPT自我介绍,其后抽题进行英文问题回答、文献中译英以及专业知识的考核和自由提问。</p>
<ul>
<li><strong>英文问题</strong>:在疫情隔离期间你在干什么?</li>
<li><strong>文献英译中</strong>:给了一段有关疫情期间机场如何进行特殊管控、使乘客保持距离的新闻,大概100多个词,要在两三分钟内完成翻译</li>
<li><strong>专业知识</strong>:描述一种扫地机器人或仓库机器人使用到的一种计算机技术</li>
<li><strong>自由提问</strong>:项目相关的问题、参与的项目和参加比赛之间的关系(参加比赛来发现项目可以改善的地方)</li>
</ul>
<h4 id="3、西交软院"><a href="#3、西交软院" class="headerlink" title="3、西交软院"></a>3、西交软院</h4><p>西交软院<strong>比较海也比较水</strong>(有计科学硕、软工学硕和专硕),负责的老师说招生名额最多有150人,群里一共有500+人,但到了最后一天还依然没有招满。考核的方式也非常简单,只需要进行一个5分钟以内的PPT自我介绍,随后老师针对介绍进行简单的提问。在我介绍完后,A老师让我说说我做过的项目的难点,之后B老师问了一下我的绩点情况,和我是哪里人,除了西交还有没有参加其他学校的推免考核(言下之意就是看看我是不是真的想来),我比较老实,说已经有其他offer了,但如果西交能给我学硕offer我也会考虑的。之后,C老师说了一下他们这里学硕和专硕的培养方式是一样的,遂结束面试。</p>
<p>最后,可能是我来的<strong>意愿并不是太坚决,所以没有拿到offer</strong>,不过同级的另外两位同学都拿到了候补。我认为西交软院的考核难度还是比较低的,如果你在面试中<strong>表达一下自己的强烈意愿</strong>,还是很大几率通过的。</p>
<h4 id="4、复旦计算机学院"><a href="#4、复旦计算机学院" class="headerlink" title="4、复旦计算机学院"></a>4、复旦计算机学院</h4><p>这应该是我夏令营和预推免参加的门槛最高的一个学院了,当时预推免报名的时候根本不觉得自己有机会过,没想到通过了,非常意外。</p>
<p>复旦cs的考核也是比较多步骤的,而且使用的是系统自己的考核平台,比较繁琐。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013102212165.png" alt="image-20211013102212165"></p>
<p>在通过了简历初筛后,进入复试还会让你重新填写一次志愿,<strong>这个才是最终录取的志愿</strong>,填写志愿的过程是动态变化且公开的,如下图所示:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013104610049.png" alt="image-20211013104610049"></p>
<p>硕士每个方向的比例最终都在<strong>1:3</strong>左右,直博会低一点。下面来分点说说考核的几个部分:</p>
<ul>
<li><p><strong>编程能力摸底</strong>:两个半小时做三道题(<strong>不设监考</strong>),完成之后需要提交代码和答题思路,不是实时评测的,写在PDF里发回邮箱即可。题目难度比较不算高,详情见下图:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013111158361.png" alt="image-20211013111158361"></p>
</li>
<li><p><strong>英语口试</strong>:在复旦的复试平台上进行,由所选择方向的老师一对一进行考核。当时复试平台非常卡顿,进到平台后基本是<strong>完全听不清也看不清</strong>那边的情况(没想到堂堂fdu也这样),后面硬着头皮面试听到有以下几个问题:英文自我介绍、谈谈你对人机交互的理解、谈谈你对复旦计算机学院的认识。</p>
</li>
<li><strong>综合面试:</strong>也是在复旦的复试平台上进行,不过网络情况比昨天的稍好,但依旧不算特别理想。面试的方式是群面,不过我当时面试临近尾段,可以看到几个老师都意兴阑珊了,一位老师打着哈欠,还有一位老师直接中途离开了。面试的过程也<strong>主要围绕项目展开</strong>(看了一天的专业知识结果一个没问),基本把项目的每个方面都问了一遍,最后再问了问昨天机考第二题的思路,遂结束。</li>
</ul>
<p>面试结束后,过了两天就在系统和邮件上公布了结果,我收到的结果是候补。后来询问联络员和招生办的老师也一直反馈说不知道具体的候补排名和面试分数,需要询问你自己联系的老师,所以暂时没有联系的老师就很难得知自己的排名了… 最后两天,找了几个同学和老师终于问到了自己候补排14,还得知有一些复旦本校的同学也在候补名单中,遂不抱希望。</p>
<h4 id="5、中山大学人工智能学院"><a href="#5、中山大学人工智能学院" class="headerlink" title="5、中山大学人工智能学院"></a>5、中山大学人工智能学院</h4><p>中大ai院也是一个比较年轻的学院,推免招生名额只有20人,夏令营通过的有70多人,预推免又招了几十人,但是到最后一天貌似还是没招满。ai院的考核方式也只有面试,分为中译英、PPT展示以及自由提问环节。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013113638177.png" alt="image-20211013113638177"></p>
<ul>
<li><strong>中译英</strong>:给出了一段数字图像处理的文献,主要是介绍各种图像噪声(椒盐噪声、白噪声等)的,给两分钟时间浏览一遍,期间可以用一张白纸记下关键词的翻译,随后开始进行中译英。</li>
<li><strong>自由提问</strong>:看你学过计组,说说冯诺依曼体系结构吧;看你学过人工智能哲学与伦理,说说目前人工智能面临什么哲学和伦理问题;之后全程问项目,问得比较深入。</li>
</ul>
<p>面试结束一天过后,就打电话通知我可以获得学硕,问我是否确定要填报,我回答了考虑一下再给答复(因为还想看看中大cs院的候补情况)。之后过了一个小时和我说只有专硕了,可能是看我来的意愿不算特别强烈。到了填报系统的前一天,又和我说如果确定来的话,可以填报学硕(反复横跳),最终在确认cs院那边能拿到学硕后,就和他们说我不来了。</p>
<h4 id="6、北航软件工程学院"><a href="#6、北航软件工程学院" class="headerlink" title="6、北航软件工程学院"></a>6、北航软件工程学院</h4><p>北航软院<strong>没有夏令营,只有预推免</strong>,所以拿到学硕的机会还是蛮大的,考核的方式也比较<strong>硬核</strong>,涉及了口语考核和专业知识考核(好几门专业课),还需要提前进行一个政治考核。专业知识的考核方式以抽题的形式进行,每个老师负责一类题目。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013114725128.png" alt="image-20211013114725128"></p>
<p>面试的流程大概是这样的,进入会议室首先让你进行身份检测,其后开始抽一个<strong>思想政治的问题</strong>,会先问你是否为党员(党员的要求会高一些),其后进行提问。提问完后进行<strong>英语口语考核和专业知识考核</strong>,专业知识考核也是以抽题的方式进行,先抽颜色决定科目,再抽数字决定题目。我当时抽了两道题但都回答得不算特别好,老师问我要不要抽第三道,我说要,因为希望第三道能回答得更好来弥补一下。抽到题目后,如果回答不出来,老师会进行提示和引导,回答后还有可能进行进一步的追问。</p>
<ul>
<li><strong>思想政治考核</strong>:十九大的内容、如何理解党员的先锋作用</li>
<li><strong>口语考核:</strong>英语自我介绍、谈谈你对人工智能技术的认识</li>
<li><strong>专业知识</strong>:<ul>
<li><strong>数据库</strong>:sql的几种子句是什么?顺序是什么?(有点忘记了,老师引导下答出来一半)</li>
<li><strong>操作系统</strong>:进程调度的衡量标准是什么?(又有点忘记了…只答出来一两个,后来老师提示了一下,让我再说一说进程调度算法,这个答出来了。)</li>
<li><strong>编程语言</strong>:C语言中二进制文件和文本文件的区别是什么?字符型和整型的大小是多少?</li>
<li><strong>项目类</strong>:说一说你项目的流程和创新点、谈一谈你对大数据和传统数据仓库的看法。</li>
</ul>
</li>
</ul>
<p>不得不说,北航的考核还是比较<strong>全面且硬核</strong>的,招生办的老师也<strong>很认真负责</strong>(一直打电话告诉我目前的排名情况)。考核结束的当晚便出了结果,我排专硕候补第三,第二天早上老师打电话说我可以候补到专硕,但是学硕可能要等。到了填报系统的那一天,老师也和我再次确认目前可能是拿不到学硕的,遂放弃。</p>
<hr>
<h3 id="四、关于推免考核准备的小建议"><a href="#四、关于推免考核准备的小建议" class="headerlink" title="四、关于推免考核准备的小建议"></a>四、关于推免考核准备的小建议</h3><p>自从疫情之后,90%以上的院校都转为了线上考核。因为线上考核有一定的局限性,所以很多学校选择只进行面试,当然也有一部分院校(南大、哈工深)仍然保留了<strong>考专业知识的笔试和考算法题的机试</strong>。为了充分地准备推免考核,我建议从<strong>大三上的寒假</strong>便开始准备,大概有半年左右的时间,准备的内容包括但不限于以下几点:</p>
<ul>
<li><p><strong>机考</strong>:</p>
<ul>
<li><strong>刷题</strong>:Leetcode每日一题、牛客网的研究生机考题库、以及注重对薄弱的部分进行练习</li>
<li><strong>书籍</strong>:王道机试指南、算法笔记</li>
<li><strong>课程</strong>:AcWing算法基础课</li>
</ul>
</li>
<li><p><strong>专业知识:</strong></p>
<p> 专业知识方面在面试中会被问到的面比较广,但不算特别深入,也很少会要求你进行计算(专业知识采用机考的除外)、所以复习的时候采用<strong>广度优先搜索</strong>更为合适。具体被问到的专业课可能有以下这些:<strong>408</strong>(数据结构、操作系统、计算机网络、计算机组成原理)、<strong>数学</strong>(高数、线代、概率论)、编程语言(C、C++)、人工智能相关知识、数据库、软件工程、编译原理和其他出现在你成绩单上的课程。</p>
<ul>
<li><strong>书籍</strong>:<strong>王道408</strong>、<strong>数学公式的奥秘</strong>(红色的小册子、包含了高数、线代和概率论的知识点)、其他科目(数据库、软件工程、编译原理)建议在网上找总结帖或用自己的期末复习课件来复习。</li>
<li><strong>面经</strong>:不同院校喜欢问的问题也不一样,所以在准备面试前提前找一找对应院校的面经(<del>临急抱佛脚</del>)也是十分有效的。</li>
</ul>
</li>
<li><p><strong>其他的内容:</strong></p>
<p> 其他的内容指在初审或者复试中需要用到的内容,包括以下两点:</p>
<ul>
<li><strong>基础资料</strong>:成绩单、绩点排名(尽量用高的)、身份证、学生证、四六级证明、各类科研竞赛证明、照片等。</li>
<li><strong>额外资料</strong>:个人陈述(500、1000、2000字的都准备好)、简历、导师推荐信(找老师要<strong>电子签名</strong>一劳永逸)、套磁信(联系导师用)、个人信息汇总(写到一个txt里面,填不同院校的系统时直接复制粘贴即可,<strong>省时省力</strong>)、自我介绍PPT(面试中常用,可以从<strong>个人背景、科研经历、竞赛经历、研究兴趣及读研规划</strong>这几方面展开)</li>
</ul>
</li>
</ul>
<hr>
<h3 id="五、关于个人定位和目标制定的小建议"><a href="#五、关于个人定位和目标制定的小建议" class="headerlink" title="五、关于个人定位和目标制定的小建议"></a>五、关于个人定位和目标制定的小建议</h3><p>在确定保研、并进行相关准备前,更重要的一件事是思考自己为什么读研,并进行个人定位。就我了解,读研选择计算机学科的原因大概有以下几种:</p>
<ul>
<li>想以后继续到高校、科研单位进行学术研究</li>
<li>本科并非为计算机专业,但以后想从事计算机相关的工作</li>
<li>工作导向、通过读研以寻求工作后的更多可能性(企业算法岗、进体制等)</li>
<li>本科阶段的学习不够充分、想利用研究生阶段深入学习</li>
<li>还没想好以后干什么或者没准备好出去工作,读研过渡一下</li>
<li>……</li>
</ul>
<p>我相信大部分人读研的理由都能在以上几点找到,这里面的原因有些较为合理,有些则可能不是最优解,下面再贴一个<span class="exturl" data-url="aHR0cHM6Ly93d3cuZ29ncmFkLm9yZy9ncmFkdWF0ZS1zY2hvb2wtZ3VpZGUtYm9vay8=">国外的读研指导网站<i class="fa fa-external-link-alt"></i></span>给出的读研理由(中文翻译版,国内外国情不同,仅供参考):</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013163920552.png" alt="image-20211013163920552" style="zoom:80%;"></p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013164007739.png" alt="image-20211013164007739" style="zoom: 80%;"></p>
<p>在找到自己的读研理由,并经过<strong>深思熟虑仍决定读研</strong>后,那么此时最好就不要再患得患失了,而应迅速将精力转入到读研准备中。根据读研理由的差异,推免的目标也有以下几种不同选择:</p>
<ol>
<li>有十分强烈的科研意愿并想读博,那么建议尽早联系感兴趣方向的科研强组,并可以考虑直接申请对应院校的<strong>直博</strong>。这时候对你来说,<strong>更重要的是课题组和导师,而不是学校的名气</strong>。</li>
<li>如果比较想当进行科学研究(意愿不如上一点强烈),但未来也有去工作的可能,那么建议选择比较好的院校的<strong>学硕</strong>,因为学硕以后申请硕博连读更方便,当然现在有些学校专硕也可以申请。</li>
<li>如果以后想到企业算法岗工作,但不想读博,那么可以考虑申请比较好的院校的<strong>学硕或专硕</strong>,目前来说,到企业工作的话,学硕和专硕的差别并不是很大。(事业单位、央企、国企可能有差别)</li>
<li>如果对算法岗不感兴趣,以后还是想到其他岗位工作,并且想有更多的实习机会。这时候更建议选择<strong>好的院校的专硕</strong>,因为未来不从事算法岗和科学研究,所以可以考虑选择一些门槛稍微低一点(对比同学校的其他学院)的学院的<strong>专硕</strong>。举个例子,某些学校系统中<strong>只能选择一个学院或者志愿分了优先级</strong>,那么这时候就可以选一些简单一点的学院,比如冲浙大title可以选浙大软院、工程师学院而不是cs院。</li>
<li>如果以后想以研究生的身份进入<strong>体制</strong>,获得更高起点的话,尽量选择<strong>双一流院校的学硕</strong>,这样无论是选调或者考公都有更多的岗位可选择。专硕在<strong>改为电子信息后</strong>体制内岗位减少,当然如果是清北人(华五也有可能)的专硕可以再斟酌一下,因为某些省份的机关单位可能会对这类院校<strong>开放绿色通道</strong>。这一点还是要提前考虑和了解清楚的(查看近年相关的政策文件)。在计算机专硕改为电子信息大类后,很有可能末流985的学硕考公选择<strong>远大于</strong>中上985甚至华五级别的专硕,所以这个问题需要注意一下。</li>
</ol>
<p>对推免的类型有了初步定位后,下一步就是要衡量自己的情况,对于计算机保研er来说,简历中<strong>最重要</strong>的是这几点:(突出的)论文或竞赛 ≈ 本科院校 > 绩点 ≈ 科研经历 > 竞赛经历 <strong>>>></strong> 社会实践经历</p>
<p>在进行推免申请和简历撰写的时候,可以按以上顺序来<strong>有重点地</strong>突出自己的能力。</p>
<p>在明确了自己的目标和个人情况后,应该怎么最大程度上避免海投,<strong>准确定位</strong>到心宜的院校呢?首先我们来看一下一张在计算机保研届广为流传的图片。(仅供参考)</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013173034960.png" alt="image-20211013173034960"></p>
<p>最直观的方法就是参考同学院<strong>近两年</strong>前辈的情况(最好是近两年、疫情前的推免形势和疫情后不同),对比一下个人情况和哪些前辈比较相似,从而参考这些前辈的推免申请情况来对自己进行院校的定位。用我自身举个例子吧,前一年和我情况类似的前辈们,大多拿到了中九或者中上985的offer,不过大部分是专硕。那么再结合我自身的意愿,我就将自己的目标定在了中九学硕这个位置上。</p>
<p>完成定位后,便可以进行有层次的推免申请了,总的来说分为三个层次:<strong>冲、稳、保</strong>。</p>
<ul>
<li><strong>冲</strong>:比自身定位要好的院校,假如我的定位是中九,那么清北华五人航开济等等院校就可以列入到冲这一部分。因为这部分院校水平比自身定位要高,所以如果不是特别在意学院的差别和学硕专硕的差别,可以选择投递<strong>这一类院校非热门学院的专硕</strong>。</li>
<li><strong>稳</strong>:和自身定位相近的院校,对于我来说就是所有的中九,这一类院校是自己最后大概率会去的学校,所以要全面考虑,<strong>投递最适合自己的学院和方向</strong>。</li>
<li><strong>保</strong>:比自身定位低一点的院校,对于我来说是次九和末九,这类院校可以<strong>放心投递最好的学院</strong>。</li>
</ul>
<p>在完成了个人定位和院校定位后,便可以开始进行有针对性地申请了。这时候又有一个新的问题,每个学校有这么多学院,而且专业代码都五花八门,<strong>怎么快速搜集信息呢</strong>?这里有一些比较好的方法:</p>
<ul>
<li><p>首先搜集一下所有院校的汇总信息,可以关注一下计算机考研的公众号(如灰灰考研),这些公众号每年都会整理计算机相关院校的考研情况(见下图),可以参考该表进行对应院校的投递</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013200850635.png" alt="image-20211013200850635"></p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013201037923.png" alt="image-20211013201037923"></p>
</li>
</ul>
<ul>
<li>关注保研公众号(<strong>计算机保研岛</strong>、保研岛、我们的武平工作室)、小程序(夏令营一键查询、预推免一键查询等)</li>
<li>关注<strong>计算机保研交流群(仓库中有群号)</strong>及其相关<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL2hpdC10aHVzei1Sb29raWVDSi9DU1N1bW1lckNhbXAyMDIx">GitHub仓库<i class="fa fa-external-link-alt"></i></span>(强烈安利、各地计算机保研er自建的交流组织)</li>
</ul>
<p>保研其实也是一场<strong>信息战</strong>,特别是在最后填报系统的那两天,有比较多院校会因为被鸽而临时发布补招生的通知,<strong>可以进行捡漏</strong>。常规的招生信息通常会在<strong>六、七月(夏令营),九月份(预推免及九推)</strong>井喷式更新,这几个月要特别注意各处的信息,以免错过报名。由于大三下学期还是有一定学业压力的,所以我建议使用某种工具对想报名的院校信息进行记录,选一种你选择的工具就好(笔记工具、Excel、甚至是Word)。我用的是<span class="exturl" data-url="aHR0cHM6Ly93d3cubm90aW9uLnNvLw==">Notion<i class="fa fa-external-link-alt"></i></span>,体验感还是挺不错的(见下图),唯一缺点是需要科学上网。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20211013203114415.png" alt="image-20211013203114415"></p>
<hr>
<h3 id="六、写在后面"><a href="#六、写在后面" class="headerlink" title="六、写在后面"></a>六、写在后面</h3><p>不知不觉,这篇总结已写(水)满 1w 字了。一开始只是想分享一下各个院校的面试情况与面试经验,以便后人参考,没想到最后将保研这大半年的所有经验与积累都记录下来了,这也算是自己对保研这一阶段的一个回顾与总结吧~</p>
<p>当然,所写的帖子也有一部分没有阐明(如何尽早增强自己的竞争力、如何联系导师等),而且这也只是我的个人经验,难免有不准确的地方,因此仅作参考。如果发现有什么写得不准确的地方、或者是对其它方面还有疑问的地方,也欢迎和我讨论交流~</p>
<p><strong>最后,感谢你的时间,希望我的分享对你有帮助!~</strong></p>
]]></content>
<categories>
<category>经验分享</category>
</categories>
<tags>
<tag>保研</tag>
</tags>
</entry>
<entry>
<title>图论算法总结(搜索、拓扑排序、最短路、最小生成树、二分图)</title>
<url>/graph-algorithms-summary.html</url>
<content><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近在 <span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9hY3Rpdml0eS9jb250ZW50LzExLw==">AcWing<i class="fa fa-external-link-alt"></i></span> 学习图论算法,包含了图的存储、搜索、拓扑排序、最短路算法、最小生成树算法、二分图算法等知识。因为涉及到的内容和代码较多,所以将这些内容汇总成笔记,以便日后回顾。<br><a id="more"></a></p>
<h2 id="树的存储和图的存储"><a href="#树的存储和图的存储" class="headerlink" title="树的存储和图的存储"></a>树的存储和图的存储</h2><ul>
<li><p><strong>邻接矩阵</strong></p>
<p>空间开销为点的数目的平方,当图较为稠密时使用,稀疏图的话会有很多矩阵中的元素没有值,浪费掉了。存储方式使用一个二维矩阵即可。</p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> N;</span><br><span class="line"><span class="keyword">int</span> graph[N][N];</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>邻接表</strong></p>
<p>空间开销为边的数目,当图较为稀疏的时候使用,存储方式为一个多层链表,即每个顶点都有一个专属的链表,每个链表中存着与当前顶点有边的其他顶点。初始化的代码如下所示:</p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="keyword">int</span> N, M;</span><br><span class="line"><span class="comment">// 下面几个变量存储的分别为各个顶点的链表头,当前边的终点,当前边的权值,下一条边的编号,和当前边的编号 </span></span><br><span class="line"><span class="keyword">int</span> h[N], e[M], w[M], ne[M], idx = <span class="number">0</span>;</span><br><span class="line"><span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b, <span class="keyword">int</span> c)</span> </span>{</span><br><span class="line"> <span class="comment">// 更新当前边的终点,当前边的权值,当前边下一条边的序号和头插法更新当前顶点连接的第一条边</span></span><br><span class="line"> e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="树与图的搜索"><a href="#树与图的搜索" class="headerlink" title="树与图的搜索"></a>树与图的搜索</h2><ul>
<li><p><strong>深度优先搜索</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> u)</span> </span>{</span><br><span class="line"> st[u] = <span class="literal">true</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[u]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="keyword">if</span> (!st[j]) dfs(j);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9hY3Rpdml0eS9jb250ZW50L3Byb2JsZW0vY29udGVudC85MDkv">例题:树的重心<i class="fa fa-external-link-alt"></i></span></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>, M = <span class="number">2</span> * N;</span><br><span class="line"><span class="keyword">int</span> ans = N;</span><br><span class="line"><span class="keyword">int</span> n;</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"><span class="keyword">int</span> h[N], e[M], ne[M], idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> e[idx] = b, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> u)</span> </span>{</span><br><span class="line"> st[u] = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>, sum = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[u]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="keyword">if</span> (st[j]) <span class="keyword">continue</span>;</span><br><span class="line"> <span class="keyword">int</span> s = dfs(j);</span><br><span class="line"> res = max(res, s);</span><br><span class="line"> sum += s;</span><br><span class="line"> }</span><br><span class="line"> res = max(res, n - sum);</span><br><span class="line"> ans = min(ans, res);</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span> ,<span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="built_in">cin</span> >> n;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i < n; i++) {</span><br><span class="line"> <span class="keyword">int</span> a, b;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b;</span><br><span class="line"> add(a, b);</span><br><span class="line"> add(b, a);</span><br><span class="line"> }</span><br><span class="line"> dfs(<span class="number">1</span>);</span><br><span class="line"> <span class="built_in">cout</span> << ans << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p><strong>宽度优先搜索</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="built_in">queue</span><<span class="keyword">int</span>> q;</span><br><span class="line">st[<span class="number">1</span>] = <span class="literal">true</span>;</span><br><span class="line">q.push(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (q.size()) {</span><br><span class="line"> <span class="keyword">int</span> t = q.front();</span><br><span class="line"> q.pop();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[t]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> st[j] = <span class="literal">true</span>;</span><br><span class="line"> q.push(j);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODQ5Lw==">例题:图中点的层次<i class="fa fa-external-link-alt"></i></span></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> h[N], e[N], ne[N], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> d[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> e[idx] = b, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="comment">// init the distance and the adj list</span></span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="built_in">memset</span>(d, <span class="number">-1</span>, <span class="keyword">sizeof</span>(d));</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b;</span><br><span class="line"> add(a, b);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">queue</span><<span class="keyword">int</span>> q;</span><br><span class="line"> <span class="comment">// push the starting point</span></span><br><span class="line"> q.push(<span class="number">1</span>);</span><br><span class="line"> d[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(q.size()) {</span><br><span class="line"> <span class="keyword">int</span> u = q.front();</span><br><span class="line"> q.pop();</span><br><span class="line"> <span class="comment">// iterate all the linked point of current point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[u]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="comment">// push the point into the queue</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> st[j] = <span class="literal">true</span>;</span><br><span class="line"> q.push(j);</span><br><span class="line"> <span class="comment">// update the distance</span></span><br><span class="line"> d[j] = d[u] + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">cout</span> << d[n] << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="拓扑排序"><a href="#拓扑排序" class="headerlink" title="拓扑排序"></a>拓扑排序</h2><ul>
<li><p><strong>思想:</strong>邻接表存图,存完之后看看有没有入度为0的点,有的话将其加入队列,之后利用BFS的过程,每次遍历一个点,将其导出的边取消掉,从而获得新的入度为0的点,直至所有点都被遍历到。</p>
</li>
<li><p><a href="https://www.acwing.com/problem/content/850/" target="_blank" rel="noopener">例题<strong>:</strong>拓扑排序</a></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>, M = <span class="number">2</span> * N;</span><br><span class="line"><span class="keyword">int</span> h[N], e[M], ne[M], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> d[N];</span><br><span class="line"><span class="keyword">int</span> top[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> e[idx] = b, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m, a, b, cnt = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="keyword">while</span>(m--) {</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b;</span><br><span class="line"> add(a, b);</span><br><span class="line"> d[b]++;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">queue</span><<span class="keyword">int</span>> q;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!d[i]) q.push(i);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">while</span>(q.size()) {</span><br><span class="line"> <span class="keyword">int</span> cur = q.front();</span><br><span class="line"> q.pop();</span><br><span class="line"> top[cnt++] = cur;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[cur]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> d[j]--;</span><br><span class="line"> <span class="keyword">if</span> (!d[j]) {</span><br><span class="line"> q.push(j);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (cnt != n) <span class="built_in">cout</span> << <span class="number">-1</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) {</span><br><span class="line"> <span class="built_in">cout</span> << top[i] << <span class="string">" "</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">cout</span> << <span class="built_in">endl</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="最短路算法"><a href="#最短路算法" class="headerlink" title="最短路算法"></a>最短路算法</h2><h3 id="Dijkstra-算法(单源最短路)"><a href="#Dijkstra-算法(单源最短路)" class="headerlink" title="Dijkstra 算法(单源最短路)"></a>Dijkstra 算法(单源最短路)</h3><ul>
<li><p><strong>思想:</strong>每次找到当前距离源点最近的一个点,将其序号记录下来,同时以该点为中转点,更新(松弛)其他点到源点的距离。重复上述的过程 n - 1 次,即可更新所有点,获得答案。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODUxLw==">例题:最短路<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接矩阵版本(复杂度:O(N * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">505</span>;</span><br><span class="line"><span class="keyword">int</span> g[N][N];</span><br><span class="line"><span class="keyword">int</span> dist[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="comment">// init the distance and adj matrix</span></span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> <span class="built_in">memset</span>(g, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(g));</span><br><span class="line"> <span class="comment">// build the adj matrix and remove the repeated edge</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> g[a][b] = min(g[a][b], c);</span><br><span class="line"> }</span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// dijkstra, iterate n - 1 times</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="keyword">int</span> t = <span class="number">-1</span>;</span><br><span class="line"> <span class="comment">// search the nearest and unvisited point to the start</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">1</span>; j <= n; j++) {</span><br><span class="line"> <span class="keyword">if</span> (!st[j] && (t == <span class="number">-1</span> || dist[j] < dist[t])) {</span><br><span class="line"> t = j;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// update the other points through current point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> dist[i] = min(dist[i], dist[t] + g[t][i]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// mark visited</span></span><br><span class="line"> st[t] = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (dist[n] > <span class="number">0x3f3f3f3f</span> / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="number">-1</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << dist[n] << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<ul>
<li><p><strong>邻接表版本(复杂度:O(MN))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">505</span>, M = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> h[N], e[M], ne[M], w[M], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> dist[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b, <span class="keyword">int</span> c)</span> </span>{</span><br><span class="line"> e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="comment">// init the distance and adj list</span></span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> add(a, b, c);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// dijkstra: iterate n - 1 times</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="keyword">int</span> t = <span class="number">-1</span>;</span><br><span class="line"> <span class="comment">// search the nearest and unvisited point to the start</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!st[i] && (t == <span class="number">-1</span> || dist[t] > dist[i])) {</span><br><span class="line"> t = i;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> st[t] = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">// update the other points through current point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[t]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> dist[j] = min(dist[j], dist[t] + w[i]); </span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (dist[n] > <span class="number">0x3f3f3f3f</span> / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="number">-1</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << dist[n] << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<ul>
<li><p><strong>堆优化邻接表版本(复杂度:O(MlogN))</strong></p>
<p><strong>思想:</strong>用pair小根堆存储与源点最近的点的编号,以及该点到源点的距离。之后小根堆堆顶的邻接表,如松弛了某个别的点,则将该点加入到优先队列中,直至优先队列为空。</p>
<p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODUyLw==">例题:堆优化最短路<i class="fa fa-external-link-alt"></i></span></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">typedef</span> pair<<span class="keyword">int</span>, <span class="keyword">int</span>> PII;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e6</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> h[N], e[N], w[N], ne[N], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> dist[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b, <span class="keyword">int</span> c)</span> </span>{</span><br><span class="line"> e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f3f3f3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> add(a, b, c);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// init and push the start point</span></span><br><span class="line"> priority_queue<PII, <span class="built_in">vector</span><PII>, greater<PII>> q;</span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> q.push({<span class="number">0</span>, <span class="number">1</span>});</span><br><span class="line"> <span class="comment">// iterate the priority_queue</span></span><br><span class="line"> <span class="keyword">while</span>(q.size()) {</span><br><span class="line"> <span class="comment">// get the nearest point</span></span><br><span class="line"> <span class="keyword">auto</span> p = q.top();</span><br><span class="line"> q.pop();</span><br><span class="line"> <span class="comment">// judge visited or not</span></span><br><span class="line"> <span class="keyword">int</span> cur = p.second;</span><br><span class="line"> <span class="keyword">if</span> (st[cur]) <span class="keyword">continue</span>;</span><br><span class="line"> st[cur] = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">// update all the other points throught current point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[cur]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="keyword">if</span> (dist[j] > dist[cur] + w[i]) {</span><br><span class="line"> dist[j] = dist[cur] + w[i];</span><br><span class="line"> q.push({dist[j], j});</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (dist[n] > <span class="number">0x3f3f3f3f</span> / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="number">-1</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << dist[n] << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="Bellman-ford-算法(单源最短路)"><a href="#Bellman-ford-算法(单源最短路)" class="headerlink" title="Bellman-ford 算法(单源最短路)"></a>Bellman-ford 算法(单源最短路)</h3><ul>
<li><p><strong>思想:</strong>该算法可以处理存在负权边(负权回路)的图,进行 n-1 次循环,每次循环将所有的边松弛一下,循环结束后,所有点到源点的距离即为最短距离。同时,可以更改循环次数,从而获得最多经过 k 条边的最短距离。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODU1Lw==">例题:有边数限制的最短路<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>直接存边版本(复杂度:O(M * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">505</span>, M = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> INF = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">int</span> dist[N], last[N];</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">edge</span> {</span></span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line">}edges[M];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m, k;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m >> k;</span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> edges[i] = {a, b, c};</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// iterate k times and update all the edges</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < k; i++) {</span><br><span class="line"> <span class="built_in">memcpy</span>(last, dist, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">auto</span> e = edges[i];</span><br><span class="line"> <span class="keyword">if</span> (dist[e.b] > last[e.a] + e.c) {</span><br><span class="line"> dist[e.b] = last[e.a] + e.c;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (dist[n] > INF / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="string">"impossible"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << dist[n] << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="SPFA-算法(单源最短路)"><a href="#SPFA-算法(单源最短路)" class="headerlink" title="SPFA 算法(单源最短路)"></a>SPFA 算法(单源最短路)</h3><ul>
<li><p><strong>思想:</strong>该算法可以处理存在负权边的图,但不能处理负权回路。数据结构采用邻接表,算法思路采用 BFS,利用队列存储距离和顶点号,每次取出队头的顶点后,松弛与该点相连的所有其他点。在BFS的过程中,弹出一个点标记为false,已在队列中的点则不需要重复加入,只需松弛即可。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODUzLw==">例题:spfa求最短路<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接表版本(复杂度:有优化,但最坏仍会达到O(M * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">typedef</span> pair<<span class="keyword">int</span>, <span class="keyword">int</span>> pii;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> e[N], w[N], ne[N], h[N], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> dist[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b, <span class="keyword">int</span> c)</span> </span>{</span><br><span class="line"> e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> dist[<span class="number">1</span>] = <span class="number">0</span>;</span><br><span class="line"> <span class="built_in">queue</span><pii> q;</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> add(a, b, c);</span><br><span class="line"> }</span><br><span class="line"> q.push({<span class="number">0</span>, <span class="number">1</span>});</span><br><span class="line"> st[<span class="number">1</span>] = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">// BFS</span></span><br><span class="line"> <span class="keyword">while</span>(q.size()) {</span><br><span class="line"> <span class="keyword">auto</span> p = q.front();</span><br><span class="line"> q.pop();</span><br><span class="line"> <span class="keyword">int</span> cur = p.second;</span><br><span class="line"> st[cur] = <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">// update all the adjacent point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[cur]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="keyword">if</span> (dist[j] > dist[cur] + w[i]) {</span><br><span class="line"> dist[j] = dist[cur] + w[i];</span><br><span class="line"> <span class="comment">// if marked, then no need to push</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> st[j] = <span class="literal">true</span>;</span><br><span class="line"> q.push({dist[j], j});</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (dist[n] > <span class="number">0x3f3f3f3f</span> / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="string">"impossible"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << dist[n] << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="SPFA-算法(判断负环)"><a href="#SPFA-算法(判断负环)" class="headerlink" title="SPFA 算法(判断负环)"></a>SPFA 算法(判断负环)</h3><ul>
<li><p><strong>思想:</strong>该算法可以处理存在负权边的图,但不能处理负权回路。数据结构采用邻接表,算法思路采用 BFS,利用队列存储距离和顶点号,每次取出队头的顶点后,松弛与该点相连的所有其他点。在BFS的过程中,弹出一个点标记为false,已在队列中的点则不需要重复加入,只需松弛即可。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvZGVzY3JpcHRpb24vODU0Lw==">例题:SPFA判断负环<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接表版本(复杂度:有优化,但最坏仍会达到O(M * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> e[N], w[N], ne[N], h[N], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> dist[N];</span><br><span class="line"><span class="keyword">int</span> cnt[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b, <span class="keyword">int</span> c)</span> </span>{</span><br><span class="line"> e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="keyword">bool</span> flag = <span class="literal">true</span>;</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> <span class="built_in">queue</span><<span class="keyword">int</span>> q;</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> add(a, b, c);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="comment">// set all the point as start point</span></span><br><span class="line"> q.push(i);</span><br><span class="line"> st[i] = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// BFS</span></span><br><span class="line"> <span class="keyword">while</span>(q.size() && flag) {</span><br><span class="line"> <span class="keyword">int</span> cur = q.front();</span><br><span class="line"> q.pop();</span><br><span class="line"> st[cur] = <span class="literal">false</span>;</span><br><span class="line"> <span class="comment">// update all the adjacent point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[cur]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="keyword">if</span> (dist[j] > dist[cur] + w[i]) {</span><br><span class="line"> dist[j] = dist[cur] + w[i];</span><br><span class="line"> <span class="comment">// update the length of the edge</span></span><br><span class="line"> cnt[j] = cnt[cur] + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">if</span> (cnt[j] >= n) {</span><br><span class="line"> flag = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// if marked, then no need to push</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> st[j] = <span class="literal">true</span>;</span><br><span class="line"> q.push(j);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (!flag) <span class="built_in">cout</span> << <span class="string">"Yes"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << <span class="string">"No"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="Floyd-算法(多源最短路)"><a href="#Floyd-算法(多源最短路)" class="headerlink" title="Floyd 算法(多源最短路)"></a>Floyd 算法(多源最短路)</h3><ul>
<li><p><strong>思想:</strong>邻接矩阵存图的距离(不再需要dist数组),自身距离初始化为0,其余距离初始化为正无穷,算法的核心为不断利用中间点更新源点与汇点之间的最短距离,实现方式为三层循环,最外一层循环为中间点,两层内层循环分别为源点和汇点。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODU2Lw==">例题:Floyd求最短路<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接矩阵版本(复杂度:O(N <em> N </em> N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">205</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> INF = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">int</span> g[N][N];</span><br><span class="line"><span class="keyword">int</span> dist[N][N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m, k;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m >> k;</span><br><span class="line"> <span class="comment">// initialize the adjacent matrix</span></span><br><span class="line"> <span class="built_in">memset</span>(g, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(g));</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> g[i][i] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// read the edge and keep the min edge</span></span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> g[a][b] = min(g[a][b], c);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// floyd</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> k = <span class="number">1</span>; k <= n; k++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">1</span>; j <= n; j++)</span><br><span class="line"> g[i][j] = min(g[i][j], g[i][k] + g[k][j]);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> (k--) {</span><br><span class="line"> <span class="keyword">int</span> x, y;</span><br><span class="line"> <span class="built_in">cin</span> >> x >> y;</span><br><span class="line"> <span class="keyword">if</span> (g[x][y] > INF / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="string">"impossible"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << g[x][y] << <span class="built_in">endl</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="最小生成树算法"><a href="#最小生成树算法" class="headerlink" title="最小生成树算法"></a>最小生成树算法</h2><h3 id="Prim-算法(找最近点,适合点较少的稠密图)"><a href="#Prim-算法(找最近点,适合点较少的稠密图)" class="headerlink" title="Prim 算法(找最近点,适合点较少的稠密图)"></a>Prim 算法(找最近点,适合点较少的稠密图)</h3><ul>
<li><p><strong>思想:</strong>邻接矩阵存图,维持一个连通分量,与 dijkstra 算法类似,每次找到与该连通分量最近的点(dijkstra是找与源点最近的点),将其加入连通分量中,累加其距离,并借助该点更新其他点与连通分量的距离。循环该过程 n 次后,即找到当前的最小生成树。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODYwLw==">例题:Prim算法求最短生成树<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接表版本(复杂度:O(N * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">505</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> INF = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="keyword">int</span> g[N][N];</span><br><span class="line"><span class="keyword">int</span> dist[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="comment">// initialize the matrix</span></span><br><span class="line"> <span class="built_in">memset</span>(g, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(g));</span><br><span class="line"> <span class="built_in">memset</span>(dist, <span class="number">0x3f</span>, <span class="keyword">sizeof</span>(dist));</span><br><span class="line"> <span class="built_in">memset</span>(st, <span class="number">0</span>, <span class="keyword">sizeof</span>(st));</span><br><span class="line"> <span class="comment">// read the edge and remove the repeated edege</span></span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> g[a][b] = g[b][a] = min(g[a][b], c);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// iterate n times and add the new point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < n; i++) {</span><br><span class="line"> <span class="keyword">int</span> t = <span class="number">-1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">1</span>; j <= n; j++) {</span><br><span class="line"> <span class="keyword">if</span> (!st[j] && (t == <span class="number">-1</span> || dist[j] < dist[t]))</span><br><span class="line"> t = j;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (i && dist[t] == INF) {</span><br><span class="line"> res = INF;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// calculate the distance and mark visited </span></span><br><span class="line"> <span class="keyword">if</span> (i) res += dist[t];</span><br><span class="line"> st[t] = <span class="literal">true</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// update the distance of the other points</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">1</span>; j <= n; j++) {</span><br><span class="line"> <span class="keyword">if</span> (!st[j] && dist[j] > g[t][j]) {</span><br><span class="line"> dist[j] = g[t][j];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (res > INF / <span class="number">2</span>) <span class="built_in">cout</span> << <span class="string">"impossible"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << res << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="Kruskal-算法(找最短边,适合点较多的稀疏图)"><a href="#Kruskal-算法(找最短边,适合点较多的稀疏图)" class="headerlink" title="Kruskal 算法(找最短边,适合点较多的稀疏图)"></a>Kruskal 算法(找最短边,适合点较多的稀疏图)</h3><ul>
<li><p><strong>思想:</strong>边结构体存图,并查集存顶点。首先按照边的距离排序后遍历所有边,每次获取边的两个顶点,如果两个顶点不在同一连通分量,则合并,并将该边加入最小生成树,如果最后加入边的距离不等于 n - 1,则代表当前图没有最小生成树。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODYxLw==">例题:Kruskal算法求最小生成树<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接矩阵版本(复杂度:O(MlogM))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>, M = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> p[N];</span><br><span class="line"><span class="class"><span class="keyword">struct</span> <span class="title">edge</span> {</span></span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line">}edges[M];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">cmp</span> <span class="params">(edge ea, edge eb)</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> ea.c < eb.c;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (p[x] != x) p[x] = find(p[x]);</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">return</span> x;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">int</span> a, b, c;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b >> c;</span><br><span class="line"> edges[i] = {a, b, c};</span><br><span class="line"> }</span><br><span class="line"> sort(edges, edges + m, cmp);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) p[i] = i;</span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>, cnt = <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// iterate all the edges and merge the new point to the set</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < m; i++) {</span><br><span class="line"> <span class="keyword">auto</span> e = edges[i];</span><br><span class="line"> <span class="keyword">int</span> a = e.a, b = e.b, c = e.c;</span><br><span class="line"> a = find(a), b = find(b);</span><br><span class="line"> <span class="keyword">if</span> (a != b) {</span><br><span class="line"> p[a] = b;</span><br><span class="line"> res += c;</span><br><span class="line"> <span class="comment">// calculate the current size of edges</span></span><br><span class="line"> cnt++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (cnt != n - <span class="number">1</span>) <span class="built_in">cout</span> << <span class="string">"impossible"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << res << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="二分图算法"><a href="#二分图算法" class="headerlink" title="二分图算法"></a>二分图算法</h2><h3 id="染色法判定二分图(DFS)"><a href="#染色法判定二分图(DFS)" class="headerlink" title="染色法判定二分图(DFS)"></a>染色法判定二分图(DFS)</h3><ul>
<li><p><strong>思想:</strong>邻接表存图,采用DFS遍历所有与当前点相连的点,如相连点未被标记(未标记为0,其余两个颜色为1和2),则标记为不同的颜色,若已标记,则判断颜色是否相等,相等则返回false。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODYyLw==">例题:染色法判定二分图<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接表版本(复杂度:O(M * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">10</span>, M = <span class="number">2</span> * N;</span><br><span class="line"><span class="keyword">int</span> h[N], e[M], ne[M], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> e[idx] = b, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// dfs</span></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">dfs</span><span class="params">(<span class="keyword">int</span> u, <span class="keyword">int</span> color)</span> </span>{</span><br><span class="line"> st[u] = color;</span><br><span class="line"> <span class="comment">// iterate all the adjacent point</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[u]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="comment">// if not visited, give it another color</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> <span class="keyword">if</span> (!dfs(j, <span class="number">3</span> - color)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// if visited, judge the color</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (st[j] == color) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="keyword">bool</span> flag = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b;</span><br><span class="line"> add(a, b);</span><br><span class="line"> add(b, a);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// iterate all the points</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!st[i]) {</span><br><span class="line"> <span class="keyword">if</span> (!dfs(i, <span class="number">1</span>)) {</span><br><span class="line"> flag = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (flag) <span class="built_in">cout</span> << <span class="string">"Yes"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << <span class="string">"No"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="染色法判定二分图(BFS)"><a href="#染色法判定二分图(BFS)" class="headerlink" title="染色法判定二分图(BFS)"></a>染色法判定二分图(BFS)</h3><ul>
<li><p><strong>思想:</strong>邻接表存图,采用BFS从起点开始访问,同时查询所有与当前点相连的点,如相连点未被标记(未标记为0,其余两个颜色为1和2),则标记为不同的颜色,若已标记,则判断颜色是否相等,相等则返回false。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODYyLw==">例题:染色法判定二分图<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接表版本(复杂度:O(M * N))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">typedef</span> pair<<span class="keyword">int</span>, <span class="keyword">int</span>> PII;</span><br><span class="line"><span class="keyword">int</span> n, m;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">1e5</span> + <span class="number">5</span>, M = <span class="number">2e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> h[N], e[M], ne[M], idx = <span class="number">0</span>;</span><br><span class="line">PII q[N];</span><br><span class="line"><span class="keyword">int</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> e[idx] = b, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">bool</span> <span class="title">bfs</span><span class="params">(<span class="keyword">int</span> u)</span> </span>{</span><br><span class="line"> <span class="comment">// add the start point</span></span><br><span class="line"> <span class="keyword">int</span> hh = <span class="number">0</span>, tt = <span class="number">-1</span>;</span><br><span class="line"> q[++tt] = {u, <span class="number">1</span>};</span><br><span class="line"> st[u] = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">while</span> (hh <= tt) {</span><br><span class="line"> <span class="keyword">auto</span> t = q[hh++];</span><br><span class="line"> <span class="keyword">int</span> cur = t.first, c = t.second;</span><br><span class="line"> <span class="comment">// iterate all the adjacent points</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[cur]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="comment">// if not visited, give it another color</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> st[j] = <span class="number">3</span> - c;</span><br><span class="line"> q[++tt] = {j, <span class="number">3</span> - c};</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// if visited, judge the color duplicate or not</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (st[j] == c) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">cin</span> >> n >> m;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span> h);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (m -- )</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">int</span> a, b;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b;</span><br><span class="line"> add(a, b), add(b, a);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">bool</span> flag = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n; i ++ )</span><br><span class="line"> <span class="keyword">if</span> (!st[i])</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (!bfs(i))</span><br><span class="line"> {</span><br><span class="line"> flag = <span class="literal">false</span>;</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (flag) <span class="built_in">cout</span> << <span class="string">"Yes"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="built_in">cout</span> << <span class="string">"No"</span> << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="匈牙利算法求最大匹配"><a href="#匈牙利算法求最大匹配" class="headerlink" title="匈牙利算法求最大匹配"></a>匈牙利算法求最大匹配</h3><ul>
<li><p><strong>思想:</strong>邻接表存图,遍历第一部分的点,统计最大匹配数。统计的过程为遍历第二部分的点,找到所有相邻点,并尝试与第一部分当前正在遍历的点进行匹配,如果第二部分的点未被匹配或者匹配的第一部分点可以找到其他类型的匹配,则<strong>优先匹配当前这一对</strong>。</p>
</li>
<li><p><span class="exturl" data-url="aHR0cHM6Ly93d3cuYWN3aW5nLmNvbS9wcm9ibGVtL2NvbnRlbnQvODYzLw==">例题:二分图的最大匹配<i class="fa fa-external-link-alt"></i></span></p>
<p><strong>邻接表版本(复杂度:O(MN))</strong></p>
<figure class="highlight cpp"><table><tr><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">include</span> <span class="meta-string"><bits/stdc++.h></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> <span class="built_in">std</span>;</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">int</span> N = <span class="number">505</span>, M = <span class="number">1e5</span> + <span class="number">5</span>;</span><br><span class="line"><span class="keyword">int</span> h[N], e[M], ne[M], idx = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> match[N];</span><br><span class="line"><span class="keyword">bool</span> st[N];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">int</span> a, <span class="keyword">int</span> b)</span> </span>{</span><br><span class="line"> e[idx] = b, ne[idx] = h[a], h[a] = idx++;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// find the couple</span></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span> x)</span> </span>{</span><br><span class="line"> <span class="comment">// find all the possible match</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = h[x]; i != <span class="number">-1</span>; i = ne[i]) {</span><br><span class="line"> <span class="keyword">int</span> j = e[i];</span><br><span class="line"> <span class="comment">// if not visited</span></span><br><span class="line"> <span class="keyword">if</span> (!st[j]) {</span><br><span class="line"> <span class="comment">// mark visited</span></span><br><span class="line"> st[j] = <span class="literal">true</span>;</span><br><span class="line"> <span class="comment">// if not match or if we can find a new couple</span></span><br><span class="line"> <span class="comment">// match it</span></span><br><span class="line"> <span class="keyword">if</span> (match[j] == <span class="number">0</span> || find(match[j])) {</span><br><span class="line"> match[j] = x;</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">int</span> n1, n2, m;</span><br><span class="line"> <span class="built_in">cin</span> >> n1 >> n2 >> m;</span><br><span class="line"> <span class="built_in">memset</span>(h, <span class="number">-1</span>, <span class="keyword">sizeof</span>(h));</span><br><span class="line"> <span class="keyword">while</span> (m--) {</span><br><span class="line"> <span class="keyword">int</span> a, b;</span><br><span class="line"> <span class="built_in">cin</span> >> a >> b;</span><br><span class="line"> add(a, b);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i <= n1; i++) {</span><br><span class="line"> <span class="comment">// clear the state and find some new couples</span></span><br><span class="line"> <span class="built_in">memset</span>(st, <span class="literal">false</span>, <span class="keyword">sizeof</span>(st));</span><br><span class="line"> <span class="keyword">if</span> (find(i)) res++;</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">cout</span> << res << <span class="built_in">endl</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
</ul>
]]></content>
<categories>
<category>数据结构与算法</category>
</categories>
<tags>
<tag>Notes</tag>
<tag>Algorithm</tag>
<tag>Data Structrue</tag>
<tag>Graph theory</tag>
</tags>
</entry>
<entry>
<title>KG2Text-Notes</title>
<url>/kg2text-Notes.html</url>
<content><![CDATA[<h3 id="Paper-Information"><a href="#Paper-Information" class="headerlink" title="Paper Information"></a>Paper Information</h3><hr>
<p><strong>Title</strong>: Modeling Global and Local Node Contexts for Text Generation from Knowledge Graphs<br><strong>Links</strong>: <span class="exturl" data-url="aHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzIwMDEuMTEwMDM=">https://arxiv.org/abs/2001.11003<i class="fa fa-external-link-alt"></i></span><br><strong>Date</strong>: 2020.06.22<br><strong>Comments</strong>: Transactions of the Association for Computational Linguistics (TACL)<br><strong>Subjects</strong>: KG、AI<br><strong>Index Terms</strong>: Knowledge Graphs, Text Generation<br><strong>Authors</strong>: Leonardo F. R. Ribeiro, Yue Zhang, Claire Gardent, Iryna Gurevych<br><a id="more"></a></p>
<h3 id="Notes"><a href="#Notes" class="headerlink" title="Notes"></a>Notes</h3><hr>
<h3 id="Abstract"><a href="#Abstract" class="headerlink" title="Abstract"></a>Abstract</h3><ul>
<li>融合了 Global Node Encode 和 Local Node Encode 来构造新的神经网络,从而更好地学习上下文节点嵌入。</li>
<li>运用 <span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL3Jpa2R6L0dyYXBoV3JpdGVyL3RyZWUvbWFzdGVyL2RhdGE=">AGENDA<i class="fa fa-external-link-alt"></i></span> 数据集和 <span class="exturl" data-url="aHR0cHM6Ly93ZWJubGctY2hhbGxlbmdlLmxvcmlhLmZyL2NoYWxsZW5nZV8yMDE3Lw==">WEBNLG<i class="fa fa-external-link-alt"></i></span> 数据集进行实验,各项评价指标得到了提升。</li>
</ul>
<hr>
<h3 id="Introduction"><a href="#Introduction" class="headerlink" title="Introduction"></a>Introduction</h3><ul>
<li>图到文本的生成指的是根据输入的图结构生成对应的自然语言文本,图可以指的是语义表示或知识图谱,文本包括单一的句子或包含多行句子的完整文本,<strong>而本文的任务是根据知识图谱生成完整文本。</strong></li>
<li><p>Encode:</p>
<ul>
<li>Global Node Encode:优点为考虑了大量的上下文节点,但因为将所有节点都看作和其他节点简单相连而忽略了图的拓扑结构。</li>
<li>Local Node Encode:将每个节点的相邻节点情况,即图的拓扑结构考虑到其中,但其缺点是难以构造图中相距较远节点的关系。<img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310214058323.png" alt="image-20210310214058323"></li>
</ul>
</li>
<li><p>主要贡献</p>
<ul>
<li>首次融合了 Global Node Encode 和 Local Node Encode 来构建 graph-to-text 模型。</li>
<li>首次提出了一个将 Global Node Encode 和 Local Node Encode 进行组合的 GAT 架构。</li>
</ul>
</li>
</ul>
<hr>
<h3 id="Related-Work"><a href="#Related-Work" class="headerlink" title="Related Work"></a>Related Work</h3><ul>
<li><strong>AMR-to-Text</strong>:AMR 代表 Abstract Meaning Representation Graphs,是图的其中一种,具体的例子可参照下图,将 AMR 转成文本的已经有多个研究,使用的方法包括但不限于:GNN、GCN、LSTM 等。</li>
</ul>
<p> <img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210306154921113.png" alt="image-20210306154921113" style="zoom: 60%;"></p>
<ul>
<li><p><strong>KG-to-Text</strong>:知识图谱和 AMR 相比更加稀疏,有着更庞大数量的关系且没有固定的拓扑结构;不同数据集的知识图谱会有着较大的差异,这使得生成文本的过程更加困难。该任务常用的方法包括但不限于:LSTM/GRU、GNN、GCN、Transformer。</p>
</li>
<li><p><strong>加入图的全局信息</strong>:为了更好地完成 graph-to-text 的工作,越来越多的研究加入了全局的节点信息,大部分的工作都是通过扩展图的结构,在图中加入一个全局节点来完成的。</p>
</li>
</ul>
<hr>
<h3 id="Graph-to-Text-Model"><a href="#Graph-to-Text-Model" class="headerlink" title="Graph-to-Text Model"></a>Graph-to-Text Model</h3><p>论文的这一部分包含以下内容:</p>
<ol>
<li>如何将输入的数据转换成关系图。</li>
<li>描述如何使用 GAT 构建 graph encoders。</li>
<li>描述将 global encoder 和 local encoder 结合的方法。</li>
<li>描述 decode 和训练模型的过程。</li>
</ol>
<h4 id="Graph-Preparation"><a href="#Graph-Preparation" class="headerlink" title="Graph Preparation"></a>Graph Preparation</h4><p>每个 KG 都是一个由带权边组成的有向图,它的表达形式为:$G_e = (V_e, ε_e, R)$,其实体节点的表示为 $e∈V_e$,其带权边集为 $(e_h, r, e_t)∈ε_e$,代表了实体 $e_h$ 和 $e_t$ 存在关系 $r∈R$。</p>
<p>有一点和其他方法不同的是,这里将图中的<strong>实体集看成是一组节点的集合</strong>,其中每个组成实体的符号 (token) 都是一个节点。例如,KG 中有一个实体是 “node embedding”,那么该实体则由两个节点组成,分别为 node 和 embedding。各节点之间的边的关系对应着其所属实体之间的边的关系,即边 $(u,r,v)∈ε$ 当且仅当存在一条边 $(e<em>h, r, e_t)∈ε_e$,且 $u∈e_h$, $v∈e_t$ 。节点 $v$ 可以表示为一个嵌入 (embedding):$h_v^0∈R^{d</em>{v}}$。</p>
<p>这一种图的表示方法有着很好的表达能力,但它也有一个副作用,便是消去了原实体中的单词顺序信息,为了避免该种影响,应该在 embedding 中同时加入对应 token 的位置信息。</p>
<h4 id="Graph-Neural-Networks-GNN"><a href="#Graph-Neural-Networks-GNN" class="headerlink" title="Graph Neural Networks (GNN)"></a>Graph Neural Networks (GNN)</h4><p>GNN 的工作原理为:通过学习节点的上下文节点表示和其边信息,通过信息传播机制来迭代更新当前节点的 embedding。第 $l$ 层 GNN 关于 $v$ 的上下文节点表示的公式为:<br><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310215230970.png" alt="image-20210310215230970"></p>
<p>其中 $AGGR^{l}(.)$ 是 $l$ 层上的聚合函数 (aggregation function),$r<em>{uv}$ 代表了 $u$ 和 $v$ 之间的关系,$N(v)$ 是 $v$ 的所有上下文节点集合,即那些与 $v$ 相邻的节点。我们可以将得到$h</em>{N_{(v)}}^{(l)}$ 用于更新第 $l$ 层节点 $v$ 的表示,公式为:<br><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310215245017.png" alt="image-20210310215245017"></p>
<p>在 $L$ 次迭代后,一个节点的表示包含了当前迭代中的上下文节点信息。$AGGR^{l}(.)$ 和 $COMBINE^{l}(.)$ 函数的选择根据 GNN 的不同而不同,常见的 $AGGR^{l}(.)$ 是对 $N(v)$ 求和,而$COMBINE^{l}(.)$ 函数则通常对表示向量进行拼接 (concatenation) 。</p>
<h4 id="Global-Graph-Encoder"><a href="#Global-Graph-Encoder" class="headerlink" title="Global Graph Encoder"></a>Global Graph Encoder</h4><p>全局图编码器在更新每个节点的表示时需要考虑全图的节点,这里采用了注意力机制作为消息传递机制,并将其扩展成为 GAT 结构。该编码器的公式如下所示:</p>
<script type="math/tex; mode=display">
h_{N_{(V)}} = \sum_{u∈V} a_{vu}W_{g}h_u</script><p>其中 $W<em>g$ 是模型的参数,注意力权重 $a</em>{vu}$ 的公式为:</p>
<script type="math/tex; mode=display">
a_{vu} = \frac {exp(e_{vu})} {\sum_{k∈V}exp(e_{vk})}</script><p>其中 $e_{vu}$ 用于权衡节点 $u$ 对 $v$ 的重要性,其公式为:</p>
<script type="math/tex; mode=display">
e_{vu} = ((W_qH_v)\top(W_kh_u))/d_z</script><p>为了捕捉到节点之间的不同关系,一共设计了 $K$ 个独立的全局卷积,将其计算完毕后进行拼接,有</p>
<script type="math/tex; mode=display">
\hat{h}{_{N(v)}} = {||}_{k=1}^K h_{N(v)}^{(k)}</script><p>最后,$COMBINE^{l}(.)$ 函数由 LayerNorm 和 FFN 结构组成,其公式推导为:</p>
<script type="math/tex; mode=display">
\hat{h_v}=LayerNorm(\hat{h}{_{N(v)}}+h_v),</script><script type="math/tex; mode=display">
h_v^{global} = FFN(\hat{h_v}+\hat{h}_{N(v)}+h_v)</script><h4 id="Local-Graph-Encoder"><a href="#Local-Graph-Encoder" class="headerlink" title="Local Graph Encoder"></a>Local Graph Encoder</h4><p>全局编码层中没有考虑到节点之间的边的信息和图的结构,为了补充这些信息,需要结合局部编码器到模型当中。其 $AGGR^{l}(.)$ 函数为:</p>
<script type="math/tex; mode=display">
h_{N(v)} = \sum_{u∈N(v)}a_{vu}W_rh_u</script><p>其中 $W<em>r$ 代表了节点 $u$ 和 $v$ 之间的关系,注意力函数 $a</em>{vu}$ 的计算公式为:</p>
<script type="math/tex; mode=display">
a_{vu} = \frac {exp(e_{vu})} {\sum_{k∈N(v)}exp(e_{vk})}</script><p>$e_{vu}$ 的计算公式为:</p>
<script type="math/tex; mode=display">
e_{vu} = σ(a\top[W_vh_v||W_rh_u])</script><p>其中 σ 是激活函数,$a$ 和 $W<em>v$ 是模型参数。同样地,这里将 $K$ 个拼接起来,得到 $\hat{h}{</em>{N(v)}}$。那么$COMBINE^{l}(.)$ 函数的定义为:</p>
<script type="math/tex; mode=display">
h_v^{local} = RNN (h_v,\hat{h}_{N(v)})</script><h4 id="Combining-Global-and-Local-Encodings"><a href="#Combining-Global-and-Local-Encodings" class="headerlink" title="Combining Global and Local Encodings"></a>Combining Global and Local Encodings</h4><p>直观地来说,合并两种编码器一般有两种方法,第一种方法是并行结构,也就是将全局和局部节点得到的表示进行拼接。第二种方法是级联的结构,首先得到一个全局编码器的表示,随后将其作为局部编码器的输入。</p>
<p>这两种方法的每一层都只包含单一的一种编码器,现在提出设想:将两种编码器在层内进行合并,再重复一定的次数从而得到最终表示。层内合并的方法也是类似的,有并行合并和级联合并两种方法,因此总共有四种模型结构,它们如下图所示:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210307195339134.png" alt="image-20210307195339134"></p>
<h4 id="Decoder-and-Training"><a href="#Decoder-and-Training" class="headerlink" title="Decoder and Training"></a>Decoder and Training</h4><p>Decoder 用于根据 Encoder 所学习到的图表示来生成对应的文本,这里的 Decoder 使用的是著名论文 <span class="exturl" data-url="aHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzE3MDYuMDM3NjI=">“Attention is all you need”<i class="fa fa-external-link-alt"></i></span> 中的 Decoder 结构。在训练中的其中一个挑战是需要生成包含多行文本的输出,因此在训练过程加入了 length penalty。</p>
<hr>
<h3 id="Data-and-Preprocessing"><a href="#Data-and-Preprocessing" class="headerlink" title="Data and Preprocessing"></a>Data and Preprocessing</h3><h4 id="AGENDA"><a href="#AGENDA" class="headerlink" title="AGENDA"></a>AGENDA</h4><p>该数据集包含了12个顶级 AI 会议的论文摘要,每个样本都包含了论文的标题、论文摘要和其对应的知识图谱。在预处理中同时将标题中的每个 token 当作一个 node,和知识图谱中的所有 node 合并构成图。</p>
<h4 id="WebNLG"><a href="#WebNLG" class="headerlink" title="WebNLG"></a>WebNLG</h4><p>该数据集包含了从 DBPedia 抽取出来的知识图谱,该数据集中包含了较多的边数目,为了防止维度爆炸,作者使用了正则化来定义模型的关系权重。此外,作者还用到了 Levi Transformation,将关系边转化为一个结点,将关系对应的两个节点与其相连。</p>
<p>下图是这两个数据集的概览:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210309210539790.png" alt="image-20210309210539790"></p>
<hr>
<h3 id="Experiments"><a href="#Experiments" class="headerlink" title="Experiments"></a>Experiments</h3><p>项目使用 PyTorch Geometric 和 OpenNMT-py 进行实验,Adam 优化器的参数选取分别为 $β_1 = 0.1$ 和 $β_2 =0.2$ ,学习率随着训练的轮次而逐渐上升。作者采用了 byte pair encoding 的方法,从而使实体词汇变成更小的 sub-words。</p>
<p>在评估方面,使用的 metrics 有:BLEU、METEOR、CHRF++ 等。在层次结构模型中,层次的选择从 {2,4,6} 中选择,在普通的并行和级联模型中,全局编码层和局部编码层的层数分别从 {2,4,6} 和 {1,2,3} 中选择,隐藏编码器的维度从 {256,384,448} 中选择。</p>
<h4 id="Results-on-AGENDA"><a href="#Results-on-AGENDA" class="headerlink" title="Results on AGENDA"></a>Results on AGENDA</h4><p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310115529028.png" alt="image-20210310115529028"></p>
<h4 id="Results-on-WebNLG"><a href="#Results-on-WebNLG" class="headerlink" title="Results on WebNLG"></a>Results on WebNLG</h4><p>从 AGENDA 数据集的实验可以看出 CGE 的模型效果更好,因此在 WebNLG 实验中没有再用到 PGE。</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310115637199.png" alt="image-20210310115637199"></p>
<h4 id="Development-Experiments"><a href="#Development-Experiments" class="headerlink" title="Development Experiments"></a>Development Experiments</h4><p>经过实验,作者发现了以下几个结论:</p>
<ul>
<li>编码器的层数越多,效果越好</li>
<li>编码器的向量维度并不是越高越好,因不同模型而异</li>
<li>对于一些模型来说,进一步增加编码器的层数和参数数量能进一步提升模型性能</li>
</ul>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310155325418.png" alt="image-20210310155325418"></p>
<h4 id="Ablation-Study"><a href="#Ablation-Study" class="headerlink" title="Ablation Study"></a>Ablation Study</h4><p>在这一部分中,作者将模型中的某些部分消去,从而查看去除这些部分的模型结果是怎样的。具体的结果如下图所示:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310161944010.png" alt="image-20210310161944010"></p>
<h4 id="Impact-of-the-Graph-Structure-and-Ouput-Length"><a href="#Impact-of-the-Graph-Structure-and-Ouput-Length" class="headerlink" title="Impact of the Graph Structure and Ouput Length"></a>Impact of the Graph Structure and Ouput Length</h4><ul>
<li>在 AGENDA 数据集中,图的规模越大,模型的效果越好;而 WebNLG 的结果则恰恰相反,规模越大,效果反而越差。</li>
<li>当生成的句子越长的时候,模型的效果会下降,尽管加入了 length penalty,生成得到的文本长度与原文本仍有一定的差距,这也是作者后续所要优化的方面。</li>
</ul>
<h4 id="Human-Evaluation"><a href="#Human-Evaluation" class="headerlink" title="Human Evaluation"></a>Human Evaluation</h4><p>为了进一步评估生成文本的质量,作者聘请了人类对 baseline、本文提出的模型所生成的文本和原来的文本进行评估,评估的两个方面为文本的适当性和流畅性,结果如下图所示,并得出了以下的结论:</p>
<ul>
<li>本文提出的模型的人工评测效果在各类的样本中都比 baseline 要好。</li>
<li>当图的规模增大时,模型的评测效果变差。</li>
</ul>
<p>下图是其中一个 KG 和其对应的三种文本:</p>
<p><img src="https://charfole-blog.oss-cn-shenzhen.aliyuncs.com/image/image-20210310205641461.png" alt="image-20210310205641461"></p>
<h4 id="Additiional-Experiments"><a href="#Additiional-Experiments" class="headerlink" title="Additiional Experiments"></a>Additiional Experiments</h4><p>在这一部分,作者对模型的一些其他部分进行进一步的探究,探究所得出的结论如下所示:</p>
<ul>
<li><strong>Sharing vocabulary</strong> 策略对<strong>小数据集</strong>更为重要,而 <strong>length penalty</strong> 则对<strong>大数据集</strong>更为重要。</li>
<li>全局编码器中注意力权重最大的点往往是较远的结点,而局部编码器则是较近的结点,这和认知是相符的。</li>
</ul>
<hr>
<h3 id="Conclusion"><a href="#Conclusion" class="headerlink" title="Conclusion"></a>Conclusion</h3><p>在本论文中,作者提出了一个结合注意力机制的 KG-to-Text 神经网络结构,同时在模型中结合了全局编码器和局部编码器来改善文本的生成。经过实验分析,在级联结构下构建层次模型结合两类编码器,可以达到 state-of-the-art。同时,作者提出了以下几点未来的研究方向:</p>
<ol>
<li>进一步研究全局和局部编码器的组合方式来提升效果。</li>
<li>尝试在图中结合预训练的上下文词嵌入。</li>
<li>继续探究长文本生成和原文本差距较大的原因。</li>
</ol>
<h3 id="Reproduction"><a href="#Reproduction" class="headerlink" title="Reproduction"></a>Reproduction</h3><p>论文作者已提供了该研究的<span class="exturl" data-url="aHR0cHM6Ly9naXRodWIuY29tL1VLUExhYi9rZzJ0ZXh0">代码<i class="fa fa-external-link-alt"></i></span>,但仓库中依然缺少数据集。</p>
<p>本人基于作者的指引,下载了AGENDA数据集进行试验与debug,使其能够在<span class="exturl" data-url="aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9jaGFyZm9sZS9rZzJ0ZXh0LW5vdGVib29r">Kaggle平台<i class="fa fa-external-link-alt"></i></span>上成功运行。</p>
]]></content>
<categories>
<category>科研</category>
</categories>
<tags>
<tag>Paper</tag>
<tag>Notes</tag>
</tags>
</entry>
<entry>
<title>蓝桥杯踩坑题</title>
<url>/lanqiao-review.html</url>
<content><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天又是一年一度的蓝桥(<del>baoli</del>)杯大赛,小 J 同学经过了两年的参赛,对于前三题秉承着能 baoli 就不要思考的出发点来做题,结果他今天在第三题填空题用暴力法踩坑了,比赛中卡了一小时居然还没做出来。睡了一觉下午清醒之后,用三分钟想到了一个简单的做法,写下了这篇博文。</p>
<a id="more"></a>
<h2 id="题面"><a href="#题面" class="headerlink" title="题面"></a>题面</h2><p>想不起完整的题目了,下面大概复述一下。</p>