-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
418 lines (207 loc) · 321 KB
/
atom.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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>红豆zza</title>
<subtitle>有趣的blog</subtitle>
<link href="https://hongdouzza.github.io/atom.xml" rel="self"/>
<link href="https://hongdouzza.github.io/"/>
<updated>2024-12-18T14:58:18.508Z</updated>
<id>https://hongdouzza.github.io/</id>
<author>
<name>红豆zza</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Hello World</title>
<link href="https://hongdouzza.github.io/posts/4a17b156.html"/>
<id>https://hongdouzza.github.io/posts/4a17b156.html</id>
<published>2024-12-18T14:58:18.508Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for more info. If you get any problems when using Hexo, you can find the answer in <a href="https://hexo.io/docs/troubleshooting.html">troubleshooting</a> or you can ask me on <a href="https://github.com/hexojs/hexo/issues">GitHub</a>.</p><h2 id="Quick-Start"><a href="#Quick-Start" class="headerlink" title="Quick Start"></a>Quick Start</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/writing.html">Writing</a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/server.html">Server</a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/generating.html">Generating</a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure><p>More info: <a href="https://hexo.io/docs/one-command-deployment.html">Deployment</a></p>]]></content>
<summary type="html"><p>Welcome to <a href="https://hexo.io/">Hexo</a>! This is your very first post. Check <a href="https://hexo.io/docs/">documentation</a> for</summary>
</entry>
<entry>
<title>同步与互斥</title>
<link href="https://hongdouzza.github.io/posts/b30fed39.html"/>
<id>https://hongdouzza.github.io/posts/b30fed39.html</id>
<published>2024-12-18T14:57:05.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="OS复习:同步与互斥"><a href="#OS复习:同步与互斥" class="headerlink" title="OS复习:同步与互斥"></a>OS复习:同步与互斥</h1><p>[TOC]</p><h2 id="同步与互斥概念"><a href="#同步与互斥概念" class="headerlink" title="同步与互斥概念"></a>同步与互斥概念</h2><p>在多道程序下,继承是并发执行,不同进程之间存在着不的相互制约关系;为协调进程之间的相互制约关系,引入进程同步概念,当我们计算1+2*3,加法进程一定是在乘法后的,但是OS具有异步性。</p><h3 id="临界资源"><a href="#临界资源" class="headerlink" title="临界资源"></a>临界资源</h3><p>我们将一次只能给一个进程服务的资源称为临界资源</p><p>分为四部分:进入区、临界区、退出区、剩余区</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">while(true){</span><br><span class="line"> entry section //进入区:进入临界区资源,在进入区要检查可否进入临界区,若能进入临界区,则应设置正在访问临界区的标志,以阻止其他进程同时进入临界区</span><br><span class="line"> critical section //临界区: 进程访问临界资源的那段代码,又称临界段</span><br><span class="line"> esxit section //退出区: 将正在访问临界区的标志清除</span><br><span class="line"> remainder section //剩余区</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="同步"><a href="#同步" class="headerlink" title="同步"></a>同步</h3><p>同步也称<strong>直接制约关系</strong>,指为完成某种人物而建立的两个或多个进程,这些进程因为要协调它们的运行词语而等待、传递信息所产生的制约关系。同步关系源于进程之间的相互合作。</p><h3 id="互斥"><a href="#互斥" class="headerlink" title="互斥"></a>互斥</h3><p>互斥是<strong>间接制约关系</strong>。当一个进程进入临界区使用临界资源,另一个进程必须等待;</p><p>在一台打印机系统中,两个进程A和B,当A需要打印时,系统已将打印机分配给进程B,则进程A必须阻塞。一旦进程B将打印机释放,系统便将A唤醒,将其由阻塞态变为就绪态</p><h3 id="临界区互斥的准则"><a href="#临界区互斥的准则" class="headerlink" title="临界区互斥的准则"></a>临界区互斥的准则</h3><p>为禁止两个进程同时进入临界区,同步机制应该遵循以下原则:</p><ul><li>空闲让进:临界区空闲,允许一个请求进入临界区</li><li>忙则等待:已有进程进入临界区,其他试图进入的进程必须等待</li><li>有限等待: 对请求访问的进程,应保证能在有限时间内进入临界区,防止进程无限等待</li><li>让权等待:当进程不能进入临界区时,应立即释放处理器,防止进程忙等待</li></ul><h2 id="实现临界区互斥的基本方法"><a href="#实现临界区互斥的基本方法" class="headerlink" title="实现临界区互斥的基本方法"></a>实现临界区互斥的基本方法</h2><h3 id="软件实现方法"><a href="#软件实现方法" class="headerlink" title="软件实现方法"></a>软件实现方法</h3><p>在进入区设置一些标志来标明是否由进程在临界区,若已有进程在临界区,则在进入区通过循环检查进行等待,进程离开临界区后再退出区修改标志</p><h4 id="单标志法"><a href="#单标志法" class="headerlink" title="单标志法"></a>单标志法</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">进程P0;</span><br><span class="line">While(turn!=0);</span><br><span class="line">critical section;</span><br><span class="line">turn=1;</span><br><span class="line">remainder section;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">进程P1:</span><br><span class="line">while(turn!=1);</span><br><span class="line">critical section;</span><br><span class="line">turn=0;</span><br><span class="line">remainder section</span><br></pre></td></tr></table></figure><p>这样的问题是:当P0进入临界区并离开了,此时turn=1;但是P1不打算进入临界区了,那么之后P0就一直无法拿到临界资源。也就是说,两个进程必须交替进入临界区,很可能造成违背<strong>空闲让进</strong>的原则</p><h4 id="双标志先检查法"><a href="#双标志先检查法" class="headerlink" title="双标志先检查法"></a>双标志先检查法</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">//我们用flag[2]数组,标记各个进程想进入临界区的意愿</span><br><span class="line">进程P0</span><br><span class="line">while(flag[1]); 1</span><br><span class="line">flag[0]=true; 2 // p0想进入临界区</span><br><span class="line">critical section;</span><br><span class="line">flag[0]=false;</span><br><span class="line">remainder section</span><br><span class="line"></span><br><span class="line">进程P1</span><br><span class="line">while(flag[0]); 3</span><br><span class="line">flag[1]=true; 4</span><br><span class="line">critical section;</span><br><span class="line">flag[1]=false;</span><br><span class="line">remainder section;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>优点:可以不交替进入了</p><p>缺点:当执行顺序是1324的时候,在检查对方标识和设置自己的标识前可能发生进程转换,违背了<strong>忙则等待</strong></p><h4 id="双标志后检查法"><a href="#双标志后检查法" class="headerlink" title="双标志后检查法"></a>双标志后检查法</h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//进程P0</span></span><br><span class="line">flag[<span class="number">0</span>]=<span class="literal">true</span>; <span class="number">1</span></span><br><span class="line"><span class="keyword">while</span>(flag[<span class="number">1</span>]); <span class="number">2</span></span><br><span class="line">critical section;</span><br><span class="line">flag[<span class="number">0</span>]=<span class="literal">false</span>;</span><br><span class="line">remainder section</span><br><span class="line"></span><br><span class="line"><span class="comment">//进程P1</span></span><br><span class="line">flag[<span class="number">1</span>]=<span class="literal">true</span>; <span class="number">3</span></span><br><span class="line"><span class="keyword">while</span>(flag[<span class="number">0</span>]); <span class="number">4</span></span><br><span class="line">critical section;</span><br><span class="line">flag[<span class="number">1</span>]=<span class="literal">false</span>;</span><br><span class="line">remainder section;</span><br></pre></td></tr></table></figure><p>这里我们先设置自己的标志后检查,但是1342的顺序会导致双方都竞争进入临界区,违背了<strong>空闲让进</strong>的原则,也会导致两个进程产生饥饿</p><h4 id="Peterson算法"><a href="#Peterson算法" class="headerlink" title="Peterson算法"></a>Peterson算法</h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//进程P0</span></span><br><span class="line">flag[<span class="number">0</span>]=<span class="literal">true</span>; </span><br><span class="line">turn=<span class="number">1</span></span><br><span class="line"><span class="keyword">while</span>(flag[<span class="number">1</span>]&&turn==<span class="number">1</span>); </span><br><span class="line">critical section;</span><br><span class="line">flag[<span class="number">0</span>]=<span class="literal">false</span>;</span><br><span class="line">remainder section</span><br><span class="line"></span><br><span class="line"><span class="comment">//进程P1</span></span><br><span class="line">flag[<span class="number">1</span>]=<span class="literal">true</span>; </span><br><span class="line">turn=<span class="number">0</span></span><br><span class="line"><span class="keyword">while</span>(flag[<span class="number">0</span>]&&turn==<span class="number">0</span>); </span><br><span class="line">critical section;</span><br><span class="line">flag[<span class="number">1</span>]=<span class="literal">false</span>;</span><br><span class="line">remainder section;</span><br></pre></td></tr></table></figure><p>我们用flag解决互斥访问,用turn解决饥饿问题;</p><p>当两个进程的flag都为true,此时turn会决定临界区分配给哪个进程:turn值为1时,P1拿到临界资源;当P1退出临界资源时,将flag[1]设置为flase,允许P0进入临界区;如果P1不想进入临界区了,flag[1]是false,P0也能拿到临界资源</p><p>可见Peterson满足了空闲让进、忙则等待、有限等待,但是没有满足让权等待</p><p>因为在while循环中使用了忙等待(busy waiting)机制。当进程i发现无法进入临界区时,它并不会主动释放CPU,而是不断循环检查条件。CPU时间被浪费在无效的循环检查上</p><h3 id="硬件方法"><a href="#硬件方法" class="headerlink" title="硬件方法"></a>硬件方法</h3><h4 id="中断屏蔽方法"><a href="#中断屏蔽方法" class="headerlink" title="中断屏蔽方法"></a>中断屏蔽方法</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line">关中断;</span><br><span class="line">临界区;</span><br><span class="line">开中断;</span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>注意:关中断是关闭中断</p><p>之前的软件算法会违背了一些原则,是因为在程序运行时可能会发生进程切换,而进程切换会发生中断</p><p>那我们只需要在进程拿临界资源时关中断就好了,这样不会被进程切换扰乱</p><p>当然有些缺点:</p><ul><li>限制CPU交替执行程序能力,系统效率降低</li><li>对于内核,若将关中断权限给用户是不安全的,若一个进程关中断后不再开中断,系统可能会终止</li><li>不适用多CPU系统</li></ul><h4 id="TestAndSet指令"><a href="#TestAndSet指令" class="headerlink" title="TestAndSet指令"></a>TestAndSet指令</h4><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">bool</span> <span class="title function_">TestAndSet</span><span class="params">(boolean *lock)</span>{</span><br><span class="line"> <span class="type">bool</span> old;</span><br><span class="line"> old=*lock; <span class="comment">//old存放lock旧值</span></span><br><span class="line"> *lock=<span class="literal">true</span>; <span class="comment">//无论之前是否加锁,lock都为true</span></span><br><span class="line"> <span class="keyword">return</span> old; <span class="comment">//返回Lock旧值</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以上是TS指令,它是原子操作。功能是读出标志后将该标志设置为真;</p><p>于是,我们可以用TS做互斥</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> <span class="title function_">TestAndSet</span><span class="params">(&lock)</span>;</span><br><span class="line">进程的临界区代码;</span><br><span class="line">lock=<span class="literal">false</span>;</span><br><span class="line">进程的其他代码;</span><br></pre></td></tr></table></figure><p>我们用TS指令管理临界区,为每个临界资源设置一个共享值lock,表欧式该资源两种状态,ture上锁,false为加锁</p><p>相比软件实现,TS将加锁和检查用硬件方式变成了原子操作,不会被打断;相比关中断,lock是共享的,使用多CPU</p><p>缺点: 暂时无法进入临界区的进程会占用CPU循环,跟Peterson算法一样,违背<strong>让权等待</strong></p><h4 id="Swap指令"><a href="#Swap指令" class="headerlink" title="Swap指令"></a>Swap指令</h4><p>Swap顾名思义,交换两个字节的内容:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Swap (<span class="type">bool</span> *a, <span class="type">bool</span> *b){</span><br><span class="line"> <span class="type">bool</span> temp=*a;</span><br><span class="line"> *a=*b;</span><br><span class="line"> *b=temp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>Swap不能被中断</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//lock是共享的</span></span><br><span class="line"><span class="type">bool</span> key=<span class="literal">true</span>;<span class="comment">//key是局部的,用于和lock交互信息</span></span><br><span class="line"><span class="keyword">while</span>(key!=<span class="literal">false</span>){</span><br><span class="line"> Swap(&lcok,&key);</span><br><span class="line">}<span class="comment">//key为false时,说明临界区没有进程占用了</span></span><br><span class="line">进程的临界区代码;</span><br><span class="line">lock=<span class="literal">false</span>;</span><br><span class="line">进程其他代码</span><br><span class="line"> </span><br></pre></td></tr></table></figure><p>硬件实现互斥的优点:</p><ul><li>简单,支持多CPU;</li><li>支持系统有多个临界区,只需为每个临界区设置一个布尔变量</li></ul><p>缺点:</p><ul><li>进入临界区进程会占用CPU执行While循环,违背“让权等待”</li><li>从等待进程随机挑一个进入临界区,可能会造成某些进程饥饿</li></ul><h2 id="互斥锁-mutex-lock"><a href="#互斥锁-mutex-lock" class="headerlink" title="互斥锁(mutex lock)"></a>互斥锁(mutex lock)</h2><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">acquire(){ <span class="comment">//获得锁</span></span><br><span class="line"> <span class="keyword">while</span>(!available); <span class="comment">//忙则等待</span></span><br><span class="line"> available=<span class="literal">false</span>; <span class="comment">//获得锁</span></span><br><span class="line">}</span><br><span class="line">release(){ <span class="comment">//释放锁定义</span></span><br><span class="line"> available=<span class="literal">true</span>; /释放锁</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>进程进入临界区调acquire获得锁,退出临界区调release,释放锁;当进程获取不可用的锁,会被阻塞</p><p>acquire和release都是原子操作,通常用硬件实现</p><p>上面的mutex lock也称<strong>自旋锁</strong>,缺点是忙等待;当多个进程共享一个CPU时,连续循环浪费了CPU周期,因此Mutex lock多用于多CPU系统,一个线程可以在一个处理器上旋转,而不影响其他线程的执行。自旋锁优点是,进程在等待锁期间,没有上下文切换,若上锁时间短,则等待代价不高</p><h2 id="PV-信号量"><a href="#PV-信号量" class="headerlink" title="PV 信号量"></a>PV 信号量</h2><p>两个标准原语wait()和signal()访问,简写为P(),V()</p><h3 id="整型信号量"><a href="#整型信号量" class="headerlink" title="整型信号量"></a>整型信号量</h3><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">wait(S){</span><br><span class="line"> <span class="keyword">while</span>(S<=<span class="number">0</span>);</span><br><span class="line"> S=S<span class="number">-1</span>;</span><br><span class="line">}</span><br><span class="line">signal(S){</span><br><span class="line"> S=S+<span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在整型信号量机制中的wait操作,只要信号量S<=0,就会不断循环测试,没有让权等待</p><h3 id="记录型信号量"><a href="#记录型信号量" class="headerlink" title="记录型信号量"></a>记录型信号量</h3><p>记录型就不存在忙等;采用记录型的数据结构:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//记录型信号量</span></span><br><span class="line"><span class="keyword">typedef</span> <span class="class"><span class="keyword">struct</span>{</span></span><br><span class="line"> <span class="type">int</span> value;</span><br><span class="line"> <span class="class"><span class="keyword">struct</span> <span class="title">process</span> *<span class="title">L</span>;</span></span><br><span class="line">} semaphore;</span><br><span class="line"></span><br><span class="line"><span class="comment">//对信号量S一次P操作,表示进程请求一个资源,因此value--;系统可供分配的资源数减少,当value小于0,表示资源分配完毕,调用BLOCK进行自我阻塞,主动放弃CPU,并插入该类资源的等待队列,遵循了让权等待</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">wait</span><span class="params">(semaphore S)</span>{</span><br><span class="line"> S.value--;</span><br><span class="line"> <span class="keyword">if</span>(S.value<<span class="number">0</span>){</span><br><span class="line"> add this process to S.L;</span><br><span class="line"> block(S.L);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">//做一次V操作,表示进程释放了一资源,;加1后,value任然小于等于0,说明有进程在等待这个资源;要调用wakeup将S.L第一个进程唤醒(到就绪态)</span></span><br><span class="line"><span class="type">void</span> <span class="title function_">Signal</span><span class="params">(semaphore S)</span>{</span><br><span class="line"> S.value++;</span><br><span class="line"> <span class="keyword">if</span>(S.value<=<span class="number">0</span>){</span><br><span class="line"> remove this process P from S.L;</span><br><span class="line"> wakeup(P);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="信号量实现进程互斥"><a href="#信号量实现进程互斥" class="headerlink" title="信号量实现进程互斥"></a>信号量实现进程互斥</h3><p>为了多个进程能互斥访问某个临界资源,对临界资源设置一个互斥信号量S</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">semaphore S=<span class="number">1</span>;</span><br><span class="line">P1(){</span><br><span class="line"> ...</span><br><span class="line"> P(S);</span><br><span class="line"> 进程P1临界区;</span><br><span class="line"> V(S);</span><br><span class="line"> ... </span><br><span class="line">}</span><br><span class="line">P2(){</span><br><span class="line"> ...</span><br><span class="line"> P(S);</span><br><span class="line"> 进程P2的临界区;</span><br><span class="line"> V(S);</span><br><span class="line"> ...</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//S取值范围(-1,0,1)</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><ul><li>对不同临界资源要设置不同的互斥信号量</li><li>PV必须成对出现,少P无法保证互斥访问临界资源,少V使得临界资源无法被释放,使等待该资源的进程无法被唤醒</li><li>有多少资源就将信号量设置为多少</li></ul><h3 id="利用信号量实现同步"><a href="#利用信号量实现同步" class="headerlink" title="利用信号量实现同步"></a>利用信号量实现同步</h3><p>我们假设情形:Y一定在X后执行</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">semaphore S=<span class="number">0</span>;</span><br><span class="line">P1(){</span><br><span class="line"> X;</span><br><span class="line"> V(S); <span class="comment">//告诉进程P2,语句x已经完成</span></span><br><span class="line"> ... <span class="comment">//S=1</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">P2(){</span><br><span class="line"> ...</span><br><span class="line"> P(S);<span class="comment">//检查x是否允许完成,操作完P不执行block</span></span><br><span class="line"> Y; </span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>总而言之,在某个行为会提供某种资源,则这个行为后V;当某个行为要用这个资源,执行P;在互斥问题中,PV操作要夹紧临界资源的行为,中间不能有其他冗余代码</p><h3 id="信号量实现前驱关系"><a href="#信号量实现前驱关系" class="headerlink" title="信号量实现前驱关系"></a>信号量实现前驱关系</h3><p>信号量也可以用来描述语句之间的前驱关系;</p><p>我们可以把每队前驱关系都是一个同步问题,因此为每对前驱关系设置一个同步信号量,初始值均为0;</p><p>具体过程不赘述,非常好理解</p><p>用信号量实现同步和互斥是常考点,分析步骤如下:</p><ul><li>关系分析:找出进程数,分析它们的同步和互斥关系</li><li>整理思路:根据进程流程,确定大致PV操作</li><li>设置信号量</li></ul><h2 id="经典同步问题"><a href="#经典同步问题" class="headerlink" title="经典同步问题"></a>经典同步问题</h2><h3 id="生产者-消费者问题"><a href="#生产者-消费者问题" class="headerlink" title="生产者-消费者问题"></a>生产者-消费者问题</h3><p>producers 和 consumers共享一个初始为空、大小为n的缓冲区。操作逻辑与管道相同,只有当缓冲区未满,producer才能将消息放入缓冲;只有当缓冲区未空,consumer才能从缓冲区读消息;除此之外,都要被阻塞;由于缓冲区是临界资源,必须互斥访问</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">semaphore mutex=<span class="number">1</span>; <span class="comment">//临界区互斥信号量</span></span><br><span class="line">semaphore empty=n; <span class="comment">//空闲缓冲区</span></span><br><span class="line">semaphore full=<span class="number">0</span>; <span class="comment">//缓冲区初始化为空</span></span><br><span class="line">producer(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> produce an item in nextp;</span><br><span class="line"> P(empty); <span class="comment">//获取缓冲区单元</span></span><br><span class="line"> P(mutex); <span class="comment">//进入临界区</span></span><br><span class="line"> add nextp to buffer;</span><br><span class="line"> V(mutex); <span class="comment">//离开临界区,释放互斥信号</span></span><br><span class="line"> V(full); <span class="comment">//满缓冲区+1</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">consumer(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> P(full); <span class="comment">//获取缓冲区单元</span></span><br><span class="line"> P(mutex); <span class="comment">//进入临界区</span></span><br><span class="line"> remove an item from buffer;</span><br><span class="line"> V(mutex); <span class="comment">//离开临界区,释放互斥信号</span></span><br><span class="line"> V(empty); <span class="comment">//空闲缓冲区+1</span></span><br><span class="line"> consume the item;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>注意对empty和full的P操作一定是在P(mutex)之前的</p><p>如果先P(mutex)会发生死锁:</p><p>当procuser 将buffer放满,此时empty=0; consumer未取走item,下一次调producer先执行了p(mutex), <strong>mutex被producer锁了</strong>,但是遇到p(empty)阻塞了;然而consumer无法V(empty),因为mutex被Producer锁了,产生死锁现象;同理,当consumer将buffer读完,full=0;producer生产item,还是调consumer ,那么mutex被consumer锁了,full=0 ,consumer自身阻塞;producer因为mutex被锁了无法进入临界区,也是死锁</p><h3 id="读者-写者问题"><a href="#读者-写者问题" class="headerlink" title="读者-写者问题"></a>读者-写者问题</h3><p>读者和写着两组并发进程,共享一个文件;要求</p><ul><li>允许多个读者同时对文件执行读操作</li><li>只允许一个写者往文件写信息;</li><li>任意一个写者在完成写操作前不允许其他读者或写者工作</li><li>writer执行写操作前,应让已有reader和writer全部退出</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> count=<span class="number">0</span>; <span class="comment">//记录当前读者数量</span></span><br><span class="line">semaphore mutex=<span class="number">1</span>; <span class="comment">//用于保护更新count变量的互斥</span></span><br><span class="line">semaphore rw=<span class="number">1</span>; <span class="comment">//保证读者和写者互斥访问文件</span></span><br><span class="line">writer(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> P(rw);</span><br><span class="line"> writing;</span><br><span class="line"> V(rw);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">reader(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> P(mutex);</span><br><span class="line"> <span class="keyword">if</span>(count==<span class="number">0</span>){ <span class="comment">//第一个读者来,要阻止writer</span></span><br><span class="line"> P(rw);</span><br><span class="line"> }</span><br><span class="line"> count++; <span class="comment">//reader 数量+1</span></span><br><span class="line"> V(mutex); <span class="comment">//已经更新完count了,释放mutex</span></span><br><span class="line"> reading;</span><br><span class="line"> P(mutex); <span class="comment">//读完了,这个读者要退出了,所以我们要更新count,先上锁</span></span><br><span class="line"> count--;</span><br><span class="line"> <span class="keyword">if</span>(count==<span class="number">0</span>){</span><br><span class="line"> V(rw);</span><br><span class="line"> }</span><br><span class="line"> V(mutex);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样的话,reader优先,很可能造成writer饥饿</p><p>如果我们写完writer优先,可以增加两对PV操作:</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> count=<span class="number">0</span>; <span class="comment">//记录当前读者数量</span></span><br><span class="line">semaphore mutex=<span class="number">1</span>; <span class="comment">//用于保护更新count变量的互斥</span></span><br><span class="line">semaphore rw=<span class="number">1</span>; <span class="comment">//保证读者和写者互斥访问文件</span></span><br><span class="line">semaphore W=<span class="number">1</span>; <span class="comment">//实现写进程优先</span></span><br><span class="line">writer(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> P(W);</span><br><span class="line"> P(rw);</span><br><span class="line"> writing;</span><br><span class="line"> V(rw);</span><br><span class="line"> V(w);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">reader(){</span><br><span class="line"> <span class="keyword">while</span>(<span class="number">1</span>){</span><br><span class="line"> P(w);</span><br><span class="line"> P(mutex);</span><br><span class="line"> <span class="keyword">if</span>(count==<span class="number">0</span>){ <span class="comment">//第一个读者来,要阻止writer</span></span><br><span class="line"> P(rw);</span><br><span class="line"> }</span><br><span class="line"> count++; <span class="comment">//reader 数量+1</span></span><br><span class="line"> V(mutex); <span class="comment">//已经更新完count了,释放mutex</span></span><br><span class="line"> V(w); <span class="comment">//恢复对共享文件的访问,这里如果writer进来了,前一个读进程结束了,writer执行,而不是这个reader;实现writer优先</span></span><br><span class="line"> reading;</span><br><span class="line"> P(mutex); <span class="comment">//读完了,这个读者要退出了,所以我们要更新count,先上锁</span></span><br><span class="line"> count--;</span><br><span class="line"> <span class="keyword">if</span>(count==<span class="number">0</span>){</span><br><span class="line"> V(rw);</span><br><span class="line"> }</span><br><span class="line"> V(mutex);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="哲学家进餐问题"><a href="#哲学家进餐问题" class="headerlink" title="哲学家进餐问题"></a>哲学家进餐问题</h3><p>懒,图不放了</p><p>五个人围着圆桌坐,每两个人中间都有一根筷子。只有他同时拿到了两根筷子才能进餐;</p><p>如果让他们试图同时拿两根筷子,所有人都拿左手边时,发现右手的筷子被强了,死锁</p><p>so</p><p>我们规定:当一名哲学家左右手的筷子都可用时,才能抓筷子</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">semaphore chopstick[<span class="number">5</span>]={};</span><br><span class="line">semaphore mutex=<span class="number">1</span>;</span><br><span class="line">Pi(){</span><br><span class="line"> <span class="keyword">do</span>{</span><br><span class="line"> P(mutex);</span><br><span class="line"> P(chopstick[i]);</span><br><span class="line"> P(chopsticl[(i+<span class="number">1</span>)%<span class="number">5</span>]);</span><br><span class="line"> V(mutex);</span><br><span class="line"> eat;</span><br><span class="line"> V(chopstick[i]);</span><br><span class="line"> v(chopsticl[(i+<span class="number">1</span>)%<span class="number">5</span>]);</span><br><span class="line"> think;</span><br><span class="line"> }<span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我们关键时加了互斥锁mutex,告诉你,我要拿筷子了,其他人不许动!</p><p>这样能避免死锁</p><h3 id="管程"><a href="#管程" class="headerlink" title="管程"></a>管程</h3><p>管程能保证进程互斥,无需用户自己实现互斥.</p><p>管程(Monitor)是一种用于多线程互斥访问共享资源的程序结构。管程由四部分组成</p><ul><li>管程的名称</li><li>局部于管程内部的共享数据结构说明;</li><li>对该数据结构进行操作的一组过程</li><li>对局部与管程内部的共享数据设置初始值的语句</li></ul><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">monitor Demo{ <span class="comment">//定义名称为Demo的管程</span></span><br><span class="line"> <span class="comment">//定义共享数据结构,对应系统中某种共享资源</span></span><br><span class="line"> 共享数据结构S;</span><br><span class="line"> <span class="comment">// 对共享数据结构初始化的语句</span></span><br><span class="line"> init_code(){</span><br><span class="line"> S=<span class="number">5</span>;</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> take_away(){</span><br><span class="line"> 对共享数据结构x的一系列处理</span><br><span class="line"> S--</span><br><span class="line"> }</span><br><span class="line"> give_back(){</span><br><span class="line"> 对共享数据结构x的一系列处理;</span><br><span class="line"> S++;</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>管程很像一个类</p><ul><li>管程将共享资源的操作封装,共享数据结构只能被管程内的过程访问。一个进程只有通过调用管程内的过程,才能进入管程访问共享资源。对于上例,外部进程只能通过take_away()过程来申请一个资源</li><li>每次只允许一个进程进入管程,从而实现进程互斥。若多个进程同时调用take_away(),give_back(),则只有某个进程运行完它调用的过程后,下一个进程才能开始它调用的过程</li></ul><p>当一个进程进入管程被阻塞,在此期间,如果该进程不释放管程,那么其他进程无法进入管程。为此,将阻塞原因定义为条件变量;每个条件变量保存一个等待队列,用于记录因该条件变量而阻塞的所有进程。对条件变量只能两种操作,wait(),signal()</p><p>x是条件变量</p><p>x.wait: 当x对应的条件不满足时,正在调用管程的进程调用x.wait将自己插入x条件等大地队列,并释放管程</p><p>x.signal: 当x对应条件发生变化,调用x.signal,唤醒一个因x阻塞的进程</p><figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">monitor Demo{</span><br><span class="line"> 共享数据结构 S;</span><br><span class="line"> condition x;</span><br><span class="line"> init_code(){</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> take_away(){</span><br><span class="line"> <span class="keyword">if</span>(S<=<span class="number">0</span>) x.wait(); <span class="comment">//资源不够,在条件变量x上阻塞等待</span></span><br><span class="line"> 资源足够,分配资源</span><br><span class="line"> }</span><br><span class="line"> give_back(){</span><br><span class="line"> 归还资源</span><br><span class="line"> <span class="keyword">if</span>(有进程在等待) x.signal() <span class="comment">//唤醒一个阻塞进程</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>条件变量 compare 信号量</p><ul><li>条件变量的wait和signal类似信号量的PV,实现进程的阻塞/唤醒</li><li>条件变量没有值,仅仅实现排队等待功能;而信号量有值,反映剩余资源数,在管程中,剩余资源数由共享数据结构来记录</li></ul>]]></content>
<summary type="html"><h1 id="OS复习:同步与互斥"><a href="#OS复习:同步与互斥" class="headerlink" title="OS复习:同步与互斥"></a>OS复习:同步与互斥</h1><p>[TOC]</p>
<h2 id="同步与互斥概念"><a href="#同</summary>
</entry>
<entry>
<title>OS复习-CPU调度</title>
<link href="https://hongdouzza.github.io/posts/4a3bb5f.html"/>
<id>https://hongdouzza.github.io/posts/4a3bb5f.html</id>
<published>2024-12-17T04:48:12.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="CPU调度"><a href="#CPU调度" class="headerlink" title="CPU调度"></a>CPU调度</h1><ul><li>为什么要进行CPU调度</li><li>调度算法?</li></ul><p>这里我们重点了解一些调度算法</p><h2 id="调度概念"><a href="#调度概念" class="headerlink" title="调度概念"></a>调度概念</h2><p>进程对于CPU个数,资源不足,那么进程之间就会争夺CPU资源。怎么分配资源合理呢?</p><p>我们要求按照公平、高效的原则,选择进程将CPU分配给它</p><h3 id="调度层次"><a href="#调度层次" class="headerlink" title="调度层次"></a>调度层次</h3><p>我们有三级调度层次:作业调度(高级)、内存调度(中级)、进程调度(低级)</p><ul><li>作业调度:从外存尚处于后备队列的作业挑选一些,给它们分配内存、IO设备资源,并建立相应的进程;简而言之,作业调度是内存和辅存间的调度;每个作业只出入一次;一般多道批处理系统有作业调度,其他不需要</li><li>内存调度:将暂时不能运行的进程调至外存等待,进程处于<strong>挂起态</strong>。进程们此时已有运行条件,当内存稍有空闲就决定将哪些挂起进程重新调入内存,将其状态改为就绪态。目的为了提高内存利用率和系统吞吐量</li><li>进程调度:选择CPU分配,频率很高,几十ms一次</li></ul><p>三者之间关系如下:</p><ul><li>作业调度为进程活动准备</li><li>内存调度处于作业调度和进程调度之间</li><li>作业调度次数最少,内存调度其次,进程调度频率最高</li><li>进程调度最基本,不可或缺</li></ul><h3 id="调度实现"><a href="#调度实现" class="headerlink" title="调度实现"></a>调度实现</h3><p>调度器:就是实现调度CPU的程序,三部分组成</p><ul><li>排队器:将所有就进程组织成一个或多个就绪队列;当有进程变为就绪态时,排队器就将它插入就绪队列</li><li>分派器:根据调度算法,从就绪队列挑选进程,分配CPU</li><li>上下文切换器:在CPU切换时,会发生两对上下文切换操作;第一对,当前进程上下文保存到PCB中,在装入分派程序的上下文,以便分派程序运行;第二对,移除分派程序的上下文,将新选进程的CPU现场信息装入CPU各个相应寄存器</li></ul><p>上下文切换时,有大量的load和store指令,保存寄存器的内容;会花费较多时间</p><h3 id="调度时机,切换过程"><a href="#调度时机,切换过程" class="headerlink" title="调度时机,切换过程"></a>调度时机,切换过程</h3><p>先请求调度,调度新就绪队列,发生进程切换;我们期望这样顺序执行进行调度,然而事实上有很多其他情况,不一定能顺序执行;因此我们需要明确CPU调度的事件:</p><ul><li>创建新进程,父子进程都处于就绪态,调度程序可以合法决定其中哪个先执行</li><li>进程结束后,必须从就绪队列选某个程序运行,若没了就绪队列,则运行一个OS提供的<strong>闲逛进程</strong></li><li>进程因请求其他资源时,IO设备、信号量,被阻塞时,必须调度其他进程</li><li>IO设备完成后,发出IO中断,原先等待IO设备的进程变为就绪态;要决定时是新就绪态进程拿CPU,还是让中断发生时处于运行的进程继续执行</li><li>某些更紧急任务(更高优先级)、时间片用完</li></ul><p>不能进行进程调度切换的情况:</p><ul><li>在处理中断事件。</li><li>需要完全屏蔽中断的原子操作:加锁、解锁、中断现场保护、恢复现场</li></ul><h3 id="进程调度方式"><a href="#进程调度方式" class="headerlink" title="进程调度方式"></a>进程调度方式</h3><ul><li><p>非抢占式:保证正在执行的进程不被其他进程打断,直到该进程完成(正常结束或者异常终止),或者进入阻塞态;优点是系统开销小,但不能用于分时和实时系统</p></li><li><p>抢占式:当一个进程A在CPU执行,运行调度程序根据某种原则让新来的更重要进程B,打断A,抢夺CPU资源;好处是提高了系统吞吐率和响应效率,主要有<strong>时间片、优先权、短进程有先等原则</strong></p></li></ul><p>还有闲逛进程,之前我们已经提到过,这是当就绪队列为空时,OS调闲逛程序;它的PID时0,优先级最低,只需要CPU资源,其他不需要</p><p>当我们存在内核级线程时,OS选择特定的线程分配cpu,这个过程需要完整的上下文切换、修改内存映像、高速缓存失效、导致若干数量级延迟。</p><h2 id="调度目标"><a href="#调度目标" class="headerlink" title="调度目标"></a>调度目标</h2><p>我们怎么衡量调度算法的性能?</p><ul><li><p>CPU利用率:</p><script type="math/tex; mode=display">CPU利用率=\frac{CPU有效时间}{其有效工作时间+其空闲时间}</script></li><li><p>系统吞吐量:单位时间CPU完成作业的数量;长作业会消耗CPU时间很多,会降低系统吞吐量。</p></li><li><p>周转时间:从作业提交到作业完成的总时间,是指作业在等待、就绪队列中排队、在CPU运行及IO操作花费的时间,公式如下</p><script type="math/tex; mode=display">周转时间=作业完成时间-作业提交时间</script><script type="math/tex; mode=display">带权周转时间=\frac{作业运行时间}{作业实际运行时间}</script></li></ul><ul><li><p>等待时间:进程处于等待CPU时间之和</p></li><li><p>响应时间:指用户提交请求到系统首次产生响应所用的时间。交互系统中周转时间不是最好的评价,一般采用响应时间</p></li></ul><h2 id="进程切换"><a href="#进程切换" class="headerlink" title="进程切换"></a>进程切换</h2><p>我们来看看CPU调度时进程切换发生了什么?</p><h3 id="上下文切换"><a href="#上下文切换" class="headerlink" title="上下文切换"></a>上下文切换</h3><p>上下文用PCB表示,包括CPU寄存器的值、进程状态和内存管理信息等</p><ul><li>先挂起一个进程,将CPU上下文保存到PCB ,包括程序计数器和其他寄存器</li><li>将PCB移入队列,就绪、在某事件阻塞等队列</li><li>选择另一个进程执行,并更新其PCB</li><li>恢复新进程的CPU上下文</li><li>跳转到新进程PCB中的程序计数器所指向的位置执行</li></ul><p>上下文切换要耗费大量的CPU事件</p><p>模式切换和上下文切换是不同的:模式切换在用户态和内核态之间,上下文切换只发生在内核;</p><h2 id="经典调度算法"><a href="#经典调度算法" class="headerlink" title="经典调度算法"></a>经典调度算法</h2><h3 id="FCFS(先来先服务)"><a href="#FCFS(先来先服务)" class="headerlink" title="FCFS(先来先服务)"></a>FCFS(先来先服务)</h3><p>作业调度中,FCFS每次从后备队列中挑选最先进入队列的一个或几个作业,调入内存,分配资源,创建相应的进程</p><p>进程调度中,FCFS同理,直到运行完进程或者发生阻塞结束</p><p> <strong>FCFS属于不可剥夺算法</strong> ,当一个长作业先来系统,后面短作业要等很长事件,因此不能作为分时和实时系统的主要调度策略。</p><p>优点:有利于CPU繁忙型作业,对长作业有利,算法简单</p><p>缺点: 效率低,对短作业不友好,不利于IO繁忙的作业</p><h3 id="SJF(短作业优先)和SPF-调度算法"><a href="#SJF(短作业优先)和SPF-调度算法" class="headerlink" title="SJF(短作业优先)和SPF 调度算法"></a>SJF(短作业优先)和SPF 调度算法</h3><p>SJF挑选运行事件最短的作业,将它们调入内存;在调度进程也是如此,运行时间短的进程优先</p><p>缺点:</p><ul><li>对长作业不利,系统优先调度短作业,可能导致长作业长期不被调度,造成<strong>饥饿</strong></li><li>该算法没有考虑作业的紧迫程度,不能及时处理紧迫作业</li><li>作业的长短是根据用户提供的估计执行时间而定,而不是真正的运行时间</li></ul><p>SPF(短进程优先)默认是非抢占式,当然也可以是抢占式的</p><p>SJF的平均等待时间、平均周转时间是最优的</p><h3 id="高相应比优先调度算法"><a href="#高相应比优先调度算法" class="headerlink" title="高相应比优先调度算法"></a>高相应比优先调度算法</h3><p>这是对FCFS和SJF算法的平衡,同时考虑了每个作业的等待时间和估计的运行时间。自然是挑选响应比最高的作业投入内存:</p><script type="math/tex; mode=display">响应比=\frac{等待时间+要求服务时间}{要求服务时间}</script><p>特点:</p><ul><li>作业等待时间相同,那么服务时间短,相应比高,利于短作业,SJF</li><li>服务时间相同,等待时间越长,响应比越高,FCFS</li><li>长作业,响应比随时间增加而提高,克服<strong>饥饿</strong>现象</li></ul><h3 id="优先级调度算法"><a href="#优先级调度算法" class="headerlink" title="优先级调度算法"></a>优先级调度算法</h3><ul><li>非抢占式优先级:</li><li>抢占式优先级:</li><li>静态优先级:优先级在创建进程确定,在进程整个运行期间保持不变;优先级依据为进程类型、进程对资源要求、用户要求;简单易行,开销小;不够精确</li><li>动态优先级:优先级会随时间变化,比如等待时间越长,优先级越高</li></ul><p>一般的,系统进程>用户进程,交互进程>非交互进程,IO进程(频繁使用IO)>计算型进程(频繁使用CPU)</p><h3 id="RR(时间轮转)"><a href="#RR(时间轮转)" class="headerlink" title="RR(时间轮转)"></a>RR(时间轮转)</h3><p>分时系统里,OS将所有就绪进程按FCFS拍成就绪队列,然后比如每隔30ms产生一次时钟中断,激活调度程序,将CPU分配给队列队首 的进程。进程事情没做完,时间片到了,也停止</p><p>特点:</p><ul><li>时间片大小对系统性能影响很大</li><li>时间片很大,那就跟FCFS没区别</li><li>时间片很小,频繁切换进程,CPU开销增大,用于处理进程本身的CPU时间占比减少</li></ul><h3 id="多级队列调度算法"><a href="#多级队列调度算法" class="headerlink" title="多级队列调度算法"></a>多级队列调度算法</h3><p>我们可以在OS里设置多个就绪队列,不同性质的进程分配到不同就绪队列,每个队列可以采用不同的调度算法。</p><p>队列本身也可以设置优先级。在多CPU系统中给,可以为每个CPU设置一个单独的就绪队列</p><h3 id="多级反馈队列调度"><a href="#多级反馈队列调度" class="headerlink" title="多级反馈队列调度"></a>多级反馈队列调度</h3><p>我们主要实现多级反馈队列调度的方式如下:</p><ul><li>设置多个就绪队列,每个队列有不同的优先级</li><li>赋予各个队列的进程时间片大小各不相同,优先级越高的队列,每个进程时间片越小</li><li>每个队列采用FCFS。新进程来到内存后,先来队列1末尾等待。到我的小进程A了,欸,在队列1的时间片没运行完;怎么办?A只能跑到队列2的末尾,如果队列2的时间片里还没运行完;A就跑到队列3里,依次类推</li><li>按队列优先级调度;只有队列1空了,才调度队列2;若CPU在执行队列i 中的进程B,发现前面的空的队列突然又有什么大佬人物(进程)来了,我们CPU只好把刚刚的进程B推到队列I的末尾,赶忙去服务那位大佬了</li></ul><p>可见多级反馈队列调度结合了时间片轮转和优先级调度,有以下优势</p><ul><li>终端型作业用户:短作业优先</li><li>短批处理作业用户:周转时间较短</li><li>长批处理作业用户:经过前面几个队列得到部分执行,不会饥饿</li></ul>]]></content>
<summary type="html"><h1 id="CPU调度"><a href="#CPU调度" class="headerlink" title="CPU调度"></a>CPU调度</h1><ul>
<li>为什么要进行CPU调度</li>
<li>调度算法?</li>
</ul>
<p>这里我们重点了解一些调</summary>
</entry>
<entry>
<title>进程与线程</title>
<link href="https://hongdouzza.github.io/posts/49e59b62.html"/>
<id>https://hongdouzza.github.io/posts/49e59b62.html</id>
<published>2024-12-15T00:29:41.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="OS复习:进程与线程"><a href="#OS复习:进程与线程" class="headerlink" title="OS复习:进程与线程"></a>OS复习:进程与线程</h1><p>进程的所有内容都非常重要,本文尽可能详细梳理一遍</p><p>这一章包括以下内容,本文只整理进程与线程:</p><ul><li>进程与线程</li><li>CPU调度</li><li>同步与互斥</li><li>死锁</li></ul><h2 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h2><p>什么是是进程?为什么要有进程?进程如何解决我们考虑的问题</p><h3 id="什么是进程"><a href="#什么是进程" class="headerlink" title="什么是进程"></a>什么是进程</h3><p>在多道程序下,多个程序并发执行,为描述和控制程序的并发执行,我们引入进程的概念(这样解释还是抽象)</p><p>我们让每个程序独立运行,给他配备一个专门数据结构,PCB(Process Control Block),用来描述进程的基本情况和运行状态;因此,创建进程就是创建PCB;撤销进程,就是撤销PCB</p><p>呃</p><p>进程还可以有如下解释:</p><ul><li>正在执行程序的实例(好比你的代码是菜谱,我现在进程就是要根据菜谱正在做的菜)</li><li>是程序及其数据从磁盘加载到内存,在CPU上执行的过程</li><li>是具有独立功能程序在一个数据集合上运行过程</li></ul><p>它也是进程实体的运行过程,是系统进行资源分配和调度的基本单位</p><p>这里资源是值CPU、存储器、其他设备服务某个进程的<strong>时间</strong>,强调的是时间</p><p>它有以下一些特征:</p><ul><li>动态:有一定生命周期,是动态产生、变化和消亡</li><li>并发</li><li>独立</li><li>异步:</li></ul><h3 id="进程里有哪些东西"><a href="#进程里有哪些东西" class="headerlink" title="进程里有哪些东西"></a>进程里有哪些东西</h3><h4 id="PCB"><a href="#PCB" class="headerlink" title="PCB"></a>PCB</h4><p>进程控制块。进程创建时,OS为它新建一个PCB,之后该结构都在内存,任何时刻都可以被存取,并在进程结束的时候删除。PCB是进程实体一部分,是它唯一标识</p><p>PCB里又有啥呢</p><ul><li><p>进程描述信息:PID, UID</p></li><li><p>进程控制和管理信息:当前状态,作为CPU分配调度的依据;进程优先级,描述进程抢占CPU优先级;</p></li><li>资源分配清单:关于内存地址空间或者虚拟地空间状况,所打开文件列表和IO设备信息</li><li>处理机相关信息: CPU上下文,因为当CPU切换进程时,我们需要保存当前进程的现场信息,所以我们要将CPU状态信息保存在PCB中,之后重新执行能够从断点继续执行</li></ul><p>我们组织各个进程的PCB方式有链接方式和索引方式,这两种方式也会贯穿之后也会多次出现;文件逻辑结构就会用到类似的组织结构</p><h4 id="程序段"><a href="#程序段" class="headerlink" title="程序段"></a>程序段</h4><p>程序可以由多个进程共享</p><p>相当于一份菜谱,不同厨师当然可以按照同一份菜谱炒同样的菜</p><h4 id="数据段"><a href="#数据段" class="headerlink" title="数据段"></a>数据段</h4><p>可以是原始数据,也可以是中间数据</p><h3 id="进程状态转换"><a href="#进程状态转换" class="headerlink" title="进程状态转换"></a>进程状态转换</h3><p>状态转换非常重要</p><p>进程通常有五个状态:</p><ul><li>运行态:在单CPU,同一时刻只能有一个进程处于运行</li><li>就绪态:获得除CPU以外所有资源。我有菜谱,也买到菜了,菜也切好了,就差起锅了;一旦进程拿到CPU资源,就可 以立即运行,转为运行态;处于就绪态的有多个进程,我们把他们组织成就绪队列</li><li>阻塞态:进程因为某一事件暂停运行,等待某个资源可用(除CPU)。我烧着发现油盐突然不够了,就停止烧菜。所以即便CPU空闲,该进程也无法运行;同样,我们也把多个处于阻塞态的进程组织成阻塞队列</li><li>创建态: 指的是还未创建完,没有处于就绪态。我们来看看具体创建进程的过程:申请空白PCB,向PCB填写用于控制和管理进程的信息;然后未进程分配运行所需的资源;最后进入就绪队列;但是如果创建进程资源不够,比如内存不足,创建工作没完成,则处于创建态</li><li>终止态: 进程需要结束时,先把进程设置为终止态,然后进一步处理资源释放和回收</li></ul><p>关键点我们区分就绪和阻塞:</p><ul><li>就绪仅仅缺少CPU,拿到CPU就立刻转为运行态</li><li>阻塞还缺除CPU以外其他资源</li></ul><p>都是缺少资源,为什么还要区分就绪和阻塞?</p><p>因为基于时间片轮转机制,进程在实际运行中是频繁切换到就绪态的,而且进程得到CPU时间很短;这是我们期望的</p><p>然而,其他资源使用和分配相对时间就很长,转换到阻塞态次数很少;这不是我们期望的</p><p>所以,阻塞态和就绪态是生命周期完全不同的两个状态</p><p><img src="E:\QQ\Tencent Files\1747480774\nt_qq\nt_data\Pic\2024-12\Ori\518194f9f4e07eb83f166ba4a9465f99.png" alt="518194f9f4e07eb83f166ba4a9465f99"></p><p>注意:运行态到阻塞态是主动的,而进程从阻塞态到就绪态需要其他进行协助</p><h3 id="进程控制"><a href="#进程控制" class="headerlink" title="进程控制"></a>进程控制</h3><p>进程控制就是对系统所有进程管理,包括创建、撤销、实现进程状态转换。用于进程转换的程序段一般是原语</p><h4 id="创建"><a href="#创建" class="headerlink" title="创建"></a>创建</h4><p>我们允许一个父进程创建一个子进程;类似java 中的类,子进程可以继承父进程的资源;当子进程被撤销,它的资源会还给父进程。</p><p>在撤销父进程时,也会撤销其所有的子进程</p><p>什么时候会要创建进程?</p><p>终端登录系统、作业调度、系统提供服务、用户程序的应用请求</p><p>创建过程?</p><ul><li>为新进程申请唯一PID,并申请PCB;</li><li>分配其运行所需的资源,从OS或者父进程那儿拿</li><li>初始化PCB,包括初始化标志信息、CPU状态信息、CPU控制信息,设置优先级</li><li>当就绪队列还能接纳,那么放入新进程,等待被调度.</li></ul><h4 id="终止"><a href="#终止" class="headerlink" title="终止"></a>终止</h4><p>什么时候发生终止?</p><ul><li>正常结束,进程完成任务</li><li>异常错误,比如存储区越界、保护错、非法指令、特权指令错、运行超时、算数运算错、IO故障</li><li>外界干预</li></ul><p>过程:</p><ul><li>根据被终止进程的标识符,检索进程的PCB,读取进程状态</li><li>进程处于运行的话,终止其执行,将CPU资源分配给其他进程</li><li>若进程还有子进程,则将所有子进程终止</li><li>将进程所有的资源归还给父进程或者OS</li><li>将PCB从队列/链表中删除</li></ul><h4 id="阻塞和唤醒"><a href="#阻塞和唤醒" class="headerlink" title="阻塞和唤醒"></a>阻塞和唤醒</h4><p>当进程期待的事情未发生,比如请求系统资源失败、等待某种操作完成,新数据未到达,都可能调用阻塞原语。</p><p>阻塞是进程的主动行为,所以只有处于运行态的进程才能主动进入阻塞态;</p><p>阻塞具体发生过程如下:</p><ul><li>找到被阻塞进程PID,对应的PCB</li><li>若进程在运行态,保护现场,将其转为阻塞态,停止运行</li><li>将该PCB插入相应事件的等待队列,将CPU资源调度给其他就绪队列</li></ul><p>那阻塞了的进程怎么被唤醒呢?</p><p>之前我们提到阻塞态到就绪态是由其他进程协助自身完成的,比如当IO操作完成后,由释放该IO设备的进程唤醒原来的进程</p><p>唤醒原语执行过程如下:</p><ul><li>在该事件的等待队列找到相应进程的PCB</li><li>将其从等待队列移出,设置其状态为就绪态</li><li>将PCB插入就绪态,等待调度程序调度</li></ul><p>Block原语和Wakeup原语是一对作用相反的原语,要成对使用。</p><p>也就是说,我们在A进程调了Block原语,必须在相关的进程B安排一条Wakeup原语;否则,A进程就似了(永远处于阻塞态)</p><h3 id="进程间的通信"><a href="#进程间的通信" class="headerlink" title="进程间的通信"></a>进程间的通信</h3><p>有些进程间需要交换数据,我们就需要考虑进程间的通信问题</p><p>PV操作是低级通信,高级通信方式是有以较高效率传输大量数据的通信方式,有三种:共享存储、消息传递、管道通信</p><h4 id="共享存储"><a href="#共享存储" class="headerlink" title="共享存储"></a>共享存储</h4><p>进程间有一块直接访问的共享空间,那么进程们就对共享空间进行写/读操作就行;那我们关注的重点就是怎么实现同步互斥让进程安全的对共享空间读写,之后章节后具体讲述;</p><p>低级方式的共享存储基于数据结构;而高级共享基于存储区的共享。OS指负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换要由用户自己安排读写</p><p>进程内的空间是独立的,当然进程内的线程是共享其进程空间的;</p><p>因此这种方式下,两个进程只能从共享空间交换物品,但不能直接去对方家里;</p><h4 id="消息传递"><a href="#消息传递" class="headerlink" title="消息传递"></a>消息传递</h4><p>没有共享空间的情况下,进程必须用OS提供的消息传递方法实现通信</p><p>进程间的数据交换以格式化的信息(Message)为单位,调用OS的发送、接收原语</p><p>在微内核与服务器通信就采用了消息传递机制,这样能够较好支持多CPU系统、分布式系统和计算机网络</p><p>消息传递也有两种方式</p><ul><li>直接通信: A进程将消息挂在B进程的缓冲队列上</li><li>简介通信: 进程将消息发送到一个中间实体,“信箱”</li></ul><h4 id="管道通信"><a href="#管道通信" class="headerlink" title="管道通信"></a>管道通信</h4><p>管道pipe 文件,数据在管道先进先出。</p><p>管道让两个进程按生产者-消费者进行通信,只要管道不满,写进程就能向管道另一端写入数据</p><p>为协调通信,管道必须提高三种协调能力:</p><ul><li>互斥,一个进程对管道读/写,其他进程必须等待</li><li>同步,写进程向管道写入一定数量数据后,写进程阻塞,直到读进程取走数据后才将写进程唤醒</li><li>读进程将管道数据读空后,读进程阻塞,同理,需要写进程写入数据才能被唤醒</li><li>要确定读写双方是存在的</li></ul><p>Linux里频繁使用Pipe,管道就是一种文件,它比一般的通信方式有些好处,克服了两个问题:</p><ul><li>限制管道大小。管道文件时固定大小的缓冲区,LInux中缓冲区大小为4KB;使用单个缓冲区也会带来问题,再写管道时可能变满;(这里没懂)</li><li>读进程也可能比写进程的快,读写是异步的;当管道的数据被读取时,管道变空。这种情况下,一个随后的read调用将被阻塞,等待某些数据写入</li></ul><p>管道只能由创建进程访问,当父进程创建一个管道后,管道是种特殊文件,子进程会继承父进程的打开文件,会因此子进程也进程父进程的管道,可以与父进程通信</p><p><strong>从管道读数据是一次性操作,数据一旦被读取,就释放空间以便些更多数据;普通管道只允许单向通信,若有两个进程相互通信,要起两个pipe</strong></p><p>:sweat: 管道通信还不是很理解,它对比消息传递,共享存储到底优势在哪?更小,更轻量?它是要走内核的缓冲区实现的,而共享存储区不需要走内核</p><h3 id="线程和多线程"><a href="#线程和多线程" class="headerlink" title="线程和多线程"></a>线程和多线程</h3><p>进程引入为了实现多道程序并发;</p><p>引入线程为了减小程序在并发所付出的时空开销,提高OS并发性能</p><p>线程可以理解为<strong>轻量级进程</strong> ,是基本的CPU执行单元,也是程序执行流最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成</p><p>线程自己没有系统资源,但它可与同属一个进程的其他线程共享进程所有资源</p><p>一个线程可以创建撤销另一个线程,同一个进程下的线程可以并发执行;线程间相互制约,拥有就绪、阻塞、运行态</p><p>引入线程后,进程是<strong>除CPU外系统资源的分配单元</strong>,线程是<strong>CPU分配单元</strong></p><p>线程的特点是不拥有系统资源,只有少量的必要资源,但它可以访问其进程资源;这样以来,我们在同一进程下切换线程就只要开销极小的时空</p><h4 id="线程的属性"><a href="#线程的属性" class="headerlink" title="线程的属性"></a>线程的属性</h4><p>多线程OS中,进程不是基本的执行实体,这里进程处于运行态,指其线程处于允许:</p><ul><li>线程是个轻量的实体,有唯一的标识符和线程控制块,记录其执行的寄存器和栈等现场状态</li><li>不同线程可以执行相同程序,同个服务程序被不同用户调用,OS将他们创建为不同线程</li><li>同个进程下线程间共享资源</li></ul><h4 id="线程池技术"><a href="#线程池技术" class="headerlink" title="线程池技术"></a>线程池技术</h4><ul><li>通过<strong>线程池</strong>,操作系统可以高效地管理线程的生命周期。在传统的进程模型中,创建和销毁进程需要较大的开销,而线程池则通过复用线程来减少线程创建和销毁的成本。</li><li>线程池可以让线程在任务间共享,避免了频繁创建和销毁线程的性能开销,并且能够控制同时运行的线程数,从而在不同负载下优化资源使用。</li></ul><p>线程状态转换类似进程,不赘述</p><h4 id="线程组织和控制"><a href="#线程组织和控制" class="headerlink" title="线程组织和控制"></a>线程组织和控制</h4><p>TCB,线程控制块。TCB包括:</p><ul><li>线程标识符</li><li>一组寄存器,成程序计数器、状态寄存器、通用寄存器</li><li>线程运行状态</li><li>优先级、</li><li>线程专有存储区,用于保护线程切换现场</li><li>堆栈指针,用于过程调用保存局部变量以及返回地址</li></ul><p>同一进程下的线程可以完全共享进程的地址空间和全局变量,各个线程都可以访问进程的地中间每个单元;所以一个线程可以读写、清除另一个线程</p><ul><li>线程创建:OS有创建线程和终止线程的函数,用户程序启动时,通常仅有一个成为初始化线程的线程正在执行,只要功能是用于创建新线程。创建进程函数,提供指向主程序的入口指针、堆栈大小和优先级,返回一个线程标识符</li><li>进程终止:由线程终止函数完成,但是系统线程一旦被建立,就不会被终止。线程被终止后并不立即释放它的资源,只有进程其他线程执行了分离函数,被终止线程与资源分离。被终止但尚未释放资源的线程任然可以被其他线程调用,使其重新恢复运行</li></ul><h4 id="线程实现方式"><a href="#线程实现方式" class="headerlink" title="线程实现方式"></a>线程实现方式</h4><p>线程实现分两类:用户级和内核级</p><h5 id="用户级线程(ULT)"><a href="#用户级线程(ULT)" class="headerlink" title="用户级线程(ULT)"></a>用户级线程(ULT)</h5><p>线程管理所有工作都在用户态完成,无需请求OS;内核对ULT是没有意识的,我们可以通过线程库实现多线程程序</p><p>对于ULT的系统,调度单位是进程;也就是说A进程100个线程,B进程1个线程,那么B的一个线程资源是A的100倍</p><p>所以优缺点显而易见:</p><p>优势:</p><ul><li>线程切换无需到内核空间,减少时空开销</li><li>调度算法是进程专用,不同进程可以对自己的线程选择不同调度算法</li><li>ULT与OS平台无关,用户来管理线程</li></ul><p>劣势:</p><ul><li>当线程执行一个系统调用,不仅该线程被阻塞,所有线程都被阻塞(如上文所说,OS只能意识到是一个进程在请求,而非其中一个线程,所以进程下的所有线程被阻塞了)</li><li>无法发挥多CPU优势,内核每次分配给一个进程只有一个CPU,进程一个时刻只能有一个线程能执行</li></ul><p>所以可见,ULT主要问题在于和OS交互时,OS无法意识到进程里的线程</p><h5 id="内核级别-KLT"><a href="#内核级别-KLT" class="headerlink" title="内核级别(KLT)"></a>内核级别(KLT)</h5><p>OS 为每个内核级线程设置一个TCB,内核根据TCB感知线程的存在</p><p>优势:</p><ul><li>发挥多CPU,内核能在同一时刻调度同一进程多个线程</li><li>一个线程的阻塞不会影响其他线程</li><li>内核支持线程具有很小的数据结构和堆栈,线程切换开销小、快</li><li>内核可采用多线程技术</li></ul><p>劣势:</p><ul><li>同一进程的线程切换,需要从用户态到核心态进行,系统开销较大(因为用户进程的线程在用户态执行,线程调度管理都在内核完成)</li></ul><h5 id="组合:ULT-KLT"><a href="#组合:ULT-KLT" class="headerlink" title="组合:ULT+KLT"></a>组合:ULT+KLT</h5><p> 我们尝试将一些内核线程对应多个用户级别线程,这是ULT通过时分多路复用内核级实现的(啥玩意儿?)</p><p>线程库的实现(为用户提供创建和管理线程的API):</p><ul><li>在用户空间提供一个没有内核支持的库;</li><li>实现由OS直接支持的内核级库</li></ul><p>Windows API是KLT库</p><p>Java线程API允许线程在JAVA程序中直接创建管理,由于JVM实例允许在宿主OS上,java线程API通常采用宿主系统的线程库来实现。</p><h5 id="多模型线程"><a href="#多模型线程" class="headerlink" title="多模型线程"></a>多模型线程</h5><p>根据ULT和KLT连接方式不同,我们分为</p><ul><li>多对一 : 多个ULT映射到一个KLT, 每个进程只分配一个内核级线程,线程调度和管理由用户空间完成;仅仅当ULT需要访问内核时,才需要映射到一个KLT,但每次只允许一个线程进行映射</li></ul><p>优点: 线程管理在用户空间,无需切换到内核,效率高</p><p>缺点: 一个线程发生阻塞,进程内的所有线程都阻塞;因为任何时刻都只有一个线程访问内核</p><ul><li>一对一: 每个ULT映射到一个KLT,线程切换由内核完成,要切换到内核态</li></ul><p>优点: 一个线程阻塞不会影响其他线程,因为一对一的关系</p><p>缺点:每创建一个ULT,都要创建与之对应的KLT,开销较大</p><ul><li>多对多: 将n个ULT映射到m个KLT,不是一一对应的关系;要求n>=m,那自然既克服了多对一并发不高的缺点,也克服了一对一创建KLT开销太大的缺点,算是一种折中?</li></ul><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><ul><li><p>为什么要引入进程:为描述程序动态执行过程,支持和管理多道程序的并发执行</p></li><li><p>进程由什么组成?:程序段、数据段、PCB</p></li><li><p>进程是怎么解决问题的:将识别程序运行状态的一些变量存放在PCB,通过系统变量了解程序状况,并在适当时机进行进程的转换,避免资源浪费;以及可以划分到更小的调度单位:线程</p></li></ul>]]></content>
<summary type="html"><h1 id="OS复习:进程与线程"><a href="#OS复习:进程与线程" class="headerlink" title="OS复习:进程与线程"></a>OS复习:进程与线程</h1><p>进程的所有内容都非常重要,本文尽可能详细梳理一遍</p>
<p>这一章包括以</summary>
</entry>
<entry>
<title>OS概述</title>
<link href="https://hongdouzza.github.io/posts/1996e6d0.html"/>
<id>https://hongdouzza.github.io/posts/1996e6d0.html</id>
<published>2024-12-13T10:47:24.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="OS复习:OS概述"><a href="#OS复习:OS概述" class="headerlink" title="OS复习:OS概述"></a>OS复习:OS概述</h1><h2 id="目录"><a href="#目录" class="headerlink" title="目录"></a>目录</h2><p>虚拟化、并发化、持久化</p><ul><li>OS概述:概念、特征、批处理、分时实时、中断和系统调用</li><li>进程:</li><li>内存管理:</li><li>文件系统:</li><li>IO设备:</li></ul><p>鉴于本人大二上在没有先修计组的前提下,稀里糊涂的选了操作系统,所以基础和考研跨考的同学相当。离期末考还有一个月,选择速通</p><p>若想认真学习OS的同学,建议先修计组,参考jyy老师的课</p><h2 id="第一章:-OS基本概述"><a href="#第一章:-OS基本概述" class="headerlink" title="第一章: OS基本概述"></a>第一章: OS基本概述</h2><h3 id="1-1-基本概念"><a href="#1-1-基本概念" class="headerlink" title="1.1 基本概念"></a>1.1 基本概念</h3><p>OS是控制管理系统软硬件,合理阻止调度计算机的工作与资源分配,为用户和软件提供方便接口与环境的程序几何、</p><p>OS也是状态机</p><h4 id="特征"><a href="#特征" class="headerlink" title="特征"></a>特征</h4><p>并发和共享是操作系统基本特征;</p><p><strong>并发</strong>:同一时间间隔执行多个任务;通过CPU分时实现,多道程序交替运行</p><p><strong>共享</strong>:互斥共享、同时访问(分时共享)</p><p><strong>虚拟</strong>: 将逻辑上的映射到计算机硬件实体,常见有虚拟处理器,虚拟存储器(内存管理),虚拟设备(IO设备)</p><p><strong>异步</strong> :由于资源有限,进程的执行并不是一贯到底,走走停停,拥有不可预知速度</p><h4 id="目标和功能:"><a href="#目标和功能:" class="headerlink" title="目标和功能:"></a>目标和功能:</h4><h5 id="作为资源管理者"><a href="#作为资源管理者" class="headerlink" title="作为资源管理者"></a>作为资源管理者</h5><p>我们OS接下来四章学习内容就是围绕以下展开</p><ul><li><p>进程管理:运行多个进程时,进程何时创建?何时撤销?何时管理?如何避免冲突?如何合理共享?所以我们要解决的问题包括:进程控制、同步、通信、死锁的处、处理机的调度</p></li><li><p>存储器管理:为了多道程序提供良好环境,提高内存利用率;我们要考虑:内存分配与回收,地址映射、内存保护与共享和内存扩充</p></li><li>文件管理:计算机信息以文件形式存在,我们需要考虑:文件存储空间怎么管理?目录管理?文件读写的管理和保护</li><li>IO设备管理:设备管理要完成用户IO请求,方便用户使用各种设备,提高设备利用率;主要包括:缓冲管理,设备分配,设备处理和虚拟设备</li></ul><p>以上工作都有OS完成,用户无需关注</p><h5 id="作为用户与计算机硬件系统的接口"><a href="#作为用户与计算机硬件系统的接口" class="headerlink" title="作为用户与计算机硬件系统的接口"></a>作为用户与计算机硬件系统的接口</h5><p>用户想要操纵硬件运行程序,OS也相应提供了接口:命令接口和程序接口</p><h6 id="命令接口"><a href="#命令接口" class="headerlink" title="命令接口"></a>命令接口</h6><p>联机控制和脱机控制方式:联机就是用户和OS强交互,你说一句,计算机反馈一句;脱机又称批处理接口,你列清单,计算机逐条完成并反馈</p><h6 id="程序接口"><a href="#程序接口" class="headerlink" title="程序接口"></a>程序接口</h6><p>GUI最终就是通过调用程序接口实现的</p><h4 id="实现对资源的扩充"><a href="#实现对资源的扩充" class="headerlink" title="实现对资源的扩充"></a>实现对资源的扩充</h4><p>OS将裸机改造成功能更强、使用更方便的机子</p><h3 id="1-2-OS的分类"><a href="#1-2-OS的分类" class="headerlink" title="1.2 OS的分类"></a>1.2 OS的分类</h3><ol><li>手工操作:</li><li>批处理:单道批处理和多道批处理</li></ol><h4 id="单道批处理"><a href="#单道批处理" class="headerlink" title="单道批处理"></a>单道批处理</h4><p>将一批作业以脱机方式输入磁带,并在系统上配上监督程序</p><p>内存始终保持一道作业,是自动的(无需人工干预),有序的,单道性</p><p>但是这样造成高速CPU等待低速IO完成,CPU将会摸鱼很长一段时间。这是我们不允许的,应该狠狠压榨CPU,所以为了让CPU提高利用率和吞吐量,我们走多道程序</p><h4 id="多道批处理"><a href="#多道批处理" class="headerlink" title="多道批处理"></a>多道批处理</h4><p>提交的所有作业放在外存上排成一个队列,我们采用一定算法调度这些作业放入内存。这些作业将穿插得共享CPU和IO设备:比方说A进程请求IO操作暂停运行,我们让CPU跑B进程,通过中断机制实现;</p><p>总而言之,我们要让计算机各部门都忙起来,不能有空闲时间</p><h4 id="分时OS"><a href="#分时OS" class="headerlink" title="分时OS"></a>分时OS</h4><p>将处理器分成很短时间片,按时间片轮流将处理器分配给各联机作业使用。</p><p>A进程我给你了5单位的时间片,没完成?不用做了,下学期再来吧;我们让B进程来</p><p>分时OS可以让多个用户通过终端同时共享一台机子;所以我们要确保独立性,不能A修改了B的内容,不能A发了请求,反馈了B的结果</p><p>分时OS不同多道批处理,这里我们更强调人机交互</p><h4 id="实时OS"><a href="#实时OS" class="headerlink" title="实时OS"></a>实时OS</h4><p>当你要发射导弹了,你不能导弹任务没完成,时间片切到另一个任务。这种情况下,我们需要硬实时系统,这些紧急任务不需要时间片排队,这就是实时操作系统</p><h4 id="网路OS和分布式OS"><a href="#网路OS和分布式OS" class="headerlink" title="网路OS和分布式OS"></a>网路OS和分布式OS</h4><p>网络OS为了让网络中各个计算机结合;、</p><p>分布式OS:分布性和并行性。与网络OS不同的是,分布式OS中若干计算机相互协同完成同一任务</p><h3 id="1-3-OS运行环境"><a href="#1-3-OS运行环境" class="headerlink" title="1.3 OS运行环境"></a>1.3 OS运行环境</h3><h4 id="CPU运行模式"><a href="#CPU运行模式" class="headerlink" title="CPU运行模式"></a>CPU运行模式</h4><p>CPU执行两种程序:内核程序,用户应用程序;那么执行这两种性质程序,需要不同级别的指令</p><p>特权指令:IO、关中断、内存清零,存取用于内存保护的寄存器,送PSW到程序状态字寄存器等的指令</p><p>非特权指令:用户直接使用,不涉及内核</p><p>又把CPU运行分为用户态和核心态(尼玛的,为什么翻译不统一,什么目态,管态,内核态都有:sweat_smile:);注意应用程序向操作系统请求服务时通过访管指令,访管指令是非特权的</p><p>OS最底层是一些和硬件关联特紧密的,时钟管理,中断处理,设备驱动;其次则是进程管理,存储器管理和设备管理</p><h5 id="时钟管理"><a href="#时钟管理" class="headerlink" title="时钟管理"></a>时钟管理</h5><p>时钟第一功能就是计时,向用户提供标准时间</p><p>第二呢,就是通过时钟中断机制,实现进程的切换;比方说,在分时OS中,采用时间片轮转调度,那我们就需要给任务设置DDL;而批处理系统通过时钟管理来衡量一个作业的运行程度</p><h5 id="中断机制"><a href="#中断机制" class="headerlink" title="中断机制"></a>中断机制</h5><p>我们之前提到为了提高CPU利用率,要让CPU在IO操作期间执行其他指令,这就需要中断;但现代OS远不止于此,不做详细介绍</p><p>中断机制有一部分属于内核,负责保护和恢复中断现场信息,转移控制权到相关处理程序。这样可以减少中断处理时间,提高系统并行处理能力</p><h5 id="原语"><a href="#原语" class="headerlink" title="原语"></a>原语</h5><p>没懂为啥这么翻译,经典看不懂中文,来看英格利什,atomic Operation:原子操作?</p><p>就像物质一直分割到最小单位一样,计算机程序也一样分割到最小的程序就是原语。原语是一气呵成的,不能中断</p><p>所以重点在atomic,原子性,不可分的</p><ul><li><p>它是最接近底层,硬件的部分</p></li><li><p>具有原子性,操作只能一气呵成</p></li><li><p>运行时间短暂,调用频繁</p></li></ul><p>如何定义原语?关中断。什是关中断?让某段程序不可分割完成后才能进行中断;(关中断应该是关闭中断的意思,服了)</p><h4 id="中断和异常"><a href="#中断和异常" class="headerlink" title="中断和异常"></a>中断和异常</h4><p>我们想从用户态走向内核态时,只能通过中断或者异常</p><p><strong>中断(Interruption)</strong>: </p><ul><li><p>也称外中断,来自CPU执行指令外部的事件。通常用于信息输入和输出;比如IO结束中断和时钟中断;</p></li><li><p>中断分为可屏蔽的,不可屏蔽;前者通过INTR线发出中断请求,改变屏蔽字可以实现多重中断,使中断更加灵活;后者使通过NMI线发出中断请求,通常是紧急的硬件故障;</p></li></ul><p><strong>异常(Exception)</strong>: </p><ul><li><p>内中断,CPU执行指令内部事件。比如程序非法操作码,地址越界,运算溢出,虚拟系统缺页以及专门的陷入指令;异常自然是不可屏蔽的</p></li><li><p>Exception分为故障(Fault)、自陷(Trap)和终止(Abort)。故障比如非法操作码,缺页故障,除数为0,运算溢出;自陷,用户态调用内核程序;终止,使得CPU无法继续执行硬件故障,控制器出错、存储器校验错;、</p></li></ul><p>中断和异常是怎么处理的?</p><p>用户i条指令-> 检测异常-> 打断执行异常或中断处理->若解决,回到i+1或i指令;若无法解决,终止程序</p><p>注意中断程序和主程序是独立的,没有从属关系</p><h4 id="系统调用"><a href="#系统调用" class="headerlink" title="系统调用"></a>系统调用</h4><p>按功能分:</p><ul><li>设备管理</li><li>文件管理</li><li>进程控制</li><li>进程通信</li><li>内存管理</li></ul><p>系统调用处理过程:</p><ol><li>将系统调用号和参数压入堆栈;调用指令,执行陷入指令,从用户态转向内核态,再由系统内核和硬件保护被中断现场,将PC和PSW以及通用寄存器内容压入堆栈</li><li>通过分析系统调用类型,转入相应系统调用子程序,通过系统调用号找到system call 的处理子程序入口</li></ol><p>陷入指令,将CPU使用权主动交给内核程序,内核调用相应处理;</p><p>总而言之,用户不能直接执行对系统影响很大的操作,一定是要请求操作系统代理执行内核操作,这样保证了系统稳定性和安全性</p><h3 id="1-4-操作系统结构"><a href="#1-4-操作系统结构" class="headerlink" title="1.4 操作系统结构"></a>1.4 操作系统结构</h3><p>单内核:系统主要功能模块作为一个整体运行</p><p>微内核:内核最基本功能留在内核,其余就移动到用户态;移出的操作系统代码根据分层划分为若干服务程序,相互独立,交互要走位内核通信。</p><p>它里面有啥?</p><ul><li>与硬件相关的</li><li>基本功能</li><li>客户和服务器间的通信</li></ul><p>原本存放在内核中 的进程管理服务,虚拟存储器管理等待都被放在微内核外的一组服务器中实现。他们在微内核架构里 ,是作为进程的,运行在用户态,客户与服务器通过微内核的消息传递机制实现交互</p><p>这样好处是:可靠,其中在核外的某一操作系统模块崩溃,不会影响另一模块;实现解耦</p><p>特点:</p><ul><li>扩展和灵活</li><li>可靠和安全</li><li>可移植</li><li>分布式计算</li></ul><p>然而微内核需要频繁在用户态和核心态转换,执行开销较大,性能不高。</p><h3 id="1-4-操作系统引导"><a href="#1-4-操作系统引导" class="headerlink" title="1.4 操作系统引导"></a>1.4 操作系统引导</h3><p>笔者在折腾Linux深受bios苦,经常调引导</p><ol><li>激活CPU;CPU读取ROM里Boot程序,将指令寄存器设置为BIOS第一条指令,开始执行BIOS</li><li>硬件自检:BIOS在内存最开始空间构建中断向量表。然后通电自检,硬件是否有故障</li><li>加载带有OS的因公安</li><li>加载主引导记录</li><li>扫描硬盘分区表,加载硬盘活动分区</li><li>加载分区引导记录(PBR)</li><li>加载启动管理器:此时,MBR中的引导程序将控制权交给GRUB(如果使用的是GRUB作为启动管理器)。GRUB将展示一个启动菜单,允许用户选择要启动的操作系统(如Linux、Windows等)。GRUB也可以在启动前修改内核参数等设置。</li><li>加载操作系统</li></ol>]]></content>
<summary type="html"><h1 id="OS复习:OS概述"><a href="#OS复习:OS概述" class="headerlink" title="OS复习:OS概述"></a>OS复习:OS概述</h1><h2 id="目录"><a href="#目录" class="headerlink" </summary>
</entry>
<entry>
<title>离散报告</title>
<link href="https://hongdouzza.github.io/posts/fbe9dd7f.html"/>
<id>https://hongdouzza.github.io/posts/fbe9dd7f.html</id>
<published>2024-06-05T22:19:32.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h2 id="题选:图的最短路径算法,A-算法"><a href="#题选:图的最短路径算法,A-算法" class="headerlink" title="题选:图的最短路径算法,A*算法"></a>题选:图的最短路径算法,A*算法</h2><p>求解最短路径的算法有很多,而A*算法无疑是其中最高效率、适用性更强的算法之一。</p><p>概括来说 A*算法=贪心最优搜索+BFS+优先队列=贪心+Dijkstra+优先队列</p><p>A*算法的核心在于估价函数 f=g+h,其效率取决于H函数的设计</p><h3 id="1-贪心最优搜索"><a href="#1-贪心最优搜索" class="headerlink" title="1.贪心最优搜索"></a>1.贪心最优搜索</h3><p>贪心是启发式算法,在寻找图中最短路径时能高效找到局部最优解,但无法保证全局最优解</p><p>具体的实现思想:从起点s出发,寻找i点(i点是距离终点t最近的邻居节点)</p><p>如何找距离终点最近的邻居节点i?如何估计i到t的距离?</p><p>BFS+优先队列;我们用曼哈顿距离估算最短距离。</p><p>然而,这样的贪心算法无法确保我们假定的最优解存在。我们分情况讨论:</p><ul><li>在无障碍网格中,贪心结果是最优解。因为曼哈顿距离一定存在</li><li>在有障碍网格图中,曼哈顿距离不一定存在,可能i节点在走曼哈顿距离时碰壁,回头绕路。因此,在有障碍网格图中,我们不能确保贪心得到存在的最优解。</li></ul><p>然而,无障碍网格图要求这是个连通图,这样大量图无法适用于贪心法。</p><p>总结来看:贪心最优搜索,<strong>只看终点,不看起点</strong>,错了不能改正,没有回头路,而且曼哈顿距离无法绕过障碍</p><h3 id="2-Dijkstra(BFS)算法"><a href="#2-Dijkstra(BFS)算法" class="headerlink" title="2.Dijkstra(BFS)算法"></a>2.Dijkstra(BFS)算法</h3><p>优先队列的Dijkstra 算法能够高校球的起点s到终点t的距离,但是Dijkstra有bfs通病:没有方向,一股脑一层一层往下。但我们需求只有求一条s到t的最短路径,而不是s到图的任意节点的最短路径,这样会遍历大量的节点空间造成浪费。</p><p>总结来看:Dijkstra,<strong>只看起点,不看终点</strong></p><h3 id="A-算法"><a href="#A-算法" class="headerlink" title="A*算法"></a>A*算法</h3><p>其实很多高效复杂的算法都是针对同一种问题基本的不同算法的杂交。举个例子,在探究图的连通性时,我们会用到加权快速并集和路径压缩快速并集,很自然地,将两者结合,我们就会想到Weighted quick-union with path compression</p><p>同理,A<em>算法也是两种基本解决最短路径算法的结合,贪心和Dijkstra,总结来看,<em>*既看起点,也看终点</em></em>,这恰好互补了两种基本算法的缺陷。</p><p>我们用曼哈顿方法作为计算距离的方法 算法代码实例(c++)<br><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><algorithm></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cmath></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><queue></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><string></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><vector></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">define</span> N 10 <span class="comment">// 地图的阶数</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">typedef</span> <span class="keyword">struct</span> <span class="title class_">NODE</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> x, y; <span class="comment">// 节点所在位置</span></span><br><span class="line"> <span class="type">int</span> F, G, H; <span class="comment">// G:从起点开始,沿着产的路径,移动到网格上指定方格的移动耗费。</span></span><br><span class="line"> <span class="comment">// H:从网格上那个方格移动到终点B的预估移动耗费,使用曼哈顿距离。</span></span><br><span class="line"> <span class="comment">// F = G + H</span></span><br><span class="line"> <span class="built_in">NODE</span>(<span class="type">int</span> a, <span class="type">int</span> b) { x = a, y = b; }</span><br><span class="line"> <span class="comment">// 重载操作符,使优先队列以F值大小为标准维持堆</span></span><br><span class="line"> <span class="type">bool</span> <span class="keyword">operator</span><(<span class="type">const</span> NODE &a) <span class="type">const</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> F == a.F ? G > a.G : F > a.F;</span><br><span class="line"> }</span><br><span class="line">} Node;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义方向</span></span><br><span class="line"><span class="comment">//const int next_position[8][2] = {{-1, -1}, {-1, 0}, {-1, 1}, {0, -1}, {0, 1}, {1, -1}, {1, 0}, {1, 1}};</span></span><br><span class="line"><span class="type">const</span> <span class="type">int</span> next_position[<span class="number">4</span>][<span class="number">2</span>] = {{<span class="number">-1</span>, <span class="number">0</span>}, {<span class="number">0</span>, <span class="number">-1</span>}, {<span class="number">0</span>, <span class="number">1</span>}, {<span class="number">1</span>, <span class="number">0</span>}};</span><br><span class="line">priority_queue<Node> open; <span class="comment">// 优先队列,就相当于open表</span></span><br><span class="line"><span class="comment">// 棋盘</span></span><br><span class="line"><span class="type">int</span> map[N][N] = {{<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>},</span><br><span class="line"> {<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>}};</span><br><span class="line"><span class="type">bool</span> close[N][N]; <span class="comment">// 访问情况记录,close列表</span></span><br><span class="line"><span class="type">int</span> valueF[N][N]; <span class="comment">// 记录每个节点对应的F值</span></span><br><span class="line"><span class="type">int</span> pre[N][N][<span class="number">2</span>]; <span class="comment">// 存储每个节点的父节点</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">Manhattan</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y, <span class="type">int</span> x1, <span class="type">int</span> y1)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">return</span> (<span class="built_in">abs</span>(x - x1) + <span class="built_in">abs</span>(y - y1)) * <span class="number">10</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isValidNode</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y, <span class="type">int</span> xx, <span class="type">int</span> yy)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (x < <span class="number">0</span> || x >= N || y < <span class="number">0</span> || y >= N)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 判断边界</span></span><br><span class="line"> <span class="keyword">if</span> (map[x][y] == <span class="number">1</span>)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 判断障碍物</span></span><br><span class="line"> <span class="comment">// 两节点成对角型且它们的公共相邻节点存在障碍物,在8方向时用</span></span><br><span class="line"> <span class="keyword">if</span> (x != xx && y != yy && (map[x][yy] == <span class="number">1</span> || map[xx][y] == <span class="number">1</span>))</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</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="type">void</span> <span class="title">Astar</span><span class="params">(<span class="type">int</span> x0, <span class="type">int</span> y0, <span class="type">int</span> x1, <span class="type">int</span> y1)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// 起点加入open列表</span></span><br><span class="line"> <span class="function">Node <span class="title">node</span><span class="params">(x0, y0)</span></span>;</span><br><span class="line"> node.G = <span class="number">0</span>;</span><br><span class="line"> node.H = <span class="built_in">Manhattan</span>(x0, y0, x1, y1);</span><br><span class="line"> node.F = node.G + node.H;</span><br><span class="line"> valueF[x0][y0] = node.F;</span><br><span class="line"> open.<span class="built_in">push</span>(node);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (!open.<span class="built_in">empty</span>())</span><br><span class="line"> {</span><br><span class="line"> Node node_current = open.<span class="built_in">top</span>(); <span class="comment">//取优先队列头元素,即周围单元格中代价最小的点</span></span><br><span class="line"> open.<span class="built_in">pop</span>(); <span class="comment">//从open列表中移除</span></span><br><span class="line"> close[node_current.x][node_current.y] = <span class="literal">true</span>; <span class="comment">// 访问该点,加入close列表</span></span><br><span class="line"> <span class="keyword">if</span> (node_current.x == x1 && node_current.y == y1) <span class="comment">// 到达终点</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 遍历node_top周围的4个位置,如果是next_position有8,那么就需要遍历周围8个点</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < <span class="number">4</span>; i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="function">Node <span class="title">node_next</span><span class="params">(node_current.x + next_position[i][<span class="number">0</span>], node_current.y + next_position[i][<span class="number">1</span>])</span></span>; <span class="comment">// 创建一个node_top周围的点</span></span><br><span class="line"> <span class="comment">// 该节点坐标合法 且没有被访问</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">isValidNode</span>(node_next.x, node_next.y, node_current.x, node_current.y) && !close[node_next.x][node_next.y])</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 计算从起点并经过node_top节点到达该节点所花费的代价</span></span><br><span class="line"> node_next.G = node_current.G + <span class="built_in">int</span>(<span class="built_in">sqrt</span>(<span class="built_in">pow</span>(next_position[i][<span class="number">0</span>], <span class="number">2</span>) + <span class="built_in">pow</span>(next_position[i][<span class="number">1</span>], <span class="number">2</span>)) * <span class="number">10</span>);</span><br><span class="line"> <span class="comment">// 计算该节点到终点的曼哈顿距离</span></span><br><span class="line"> node_next.H = <span class="built_in">Manhattan</span>(node_next.x, node_next.y, x1, y1);</span><br><span class="line"> <span class="comment">// 从起点经过node_top和该节点到达终点的估计代价</span></span><br><span class="line"> node_next.F = node_next.G + node_next.H;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// node_next.F < valueF[node_next.x][node_next.y] 说明找到了更优的路径,进行更新</span></span><br><span class="line"> <span class="comment">// valueF[node_next.x][node_next.y] == 0 说明该节点还未加入open表中,则加入</span></span><br><span class="line"> <span class="keyword">if</span> (node_next.F < valueF[node_next.x][node_next.y] || valueF[node_next.x][node_next.y] == <span class="number">0</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 保存该节点的父节点</span></span><br><span class="line"> pre[node_next.x][node_next.y][<span class="number">0</span>] = node_current.x;</span><br><span class="line"> pre[node_next.x][node_next.y][<span class="number">1</span>] = node_current.y;</span><br><span class="line"> valueF[node_next.x][node_next.y] = node_next.F; <span class="comment">// 修改该节点对应的valueF值</span></span><br><span class="line"> open.<span class="built_in">push</span>(node_next);</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><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">PrintPath</span><span class="params">(<span class="type">int</span> x1, <span class="type">int</span> y1)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">if</span> (pre[x1][y1][<span class="number">0</span>] == <span class="number">-1</span> || pre[x1][y1][<span class="number">1</span>] == <span class="number">-1</span>)</span><br><span class="line"> {</span><br><span class="line"> cout << <span class="string">"no path to get"</span> << endl;</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> x = x1, y = y1;</span><br><span class="line"> <span class="type">int</span> a, b;</span><br><span class="line"> <span class="keyword">while</span> (x != <span class="number">-1</span> || y != <span class="number">-1</span>)</span><br><span class="line"> {</span><br><span class="line"> map[x][y] = <span class="number">2</span>; <span class="comment">// 将可行路径上的节点赋值为2</span></span><br><span class="line"> a = pre[x][y][<span class="number">0</span>];</span><br><span class="line"> b = pre[x][y][<span class="number">1</span>];</span><br><span class="line"> x = a;</span><br><span class="line"> y = b;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// ' '表示未经过的节点, '#'表示障碍物, '@'表示可行节点</span></span><br><span class="line"> string s[<span class="number">3</span>] = {<span class="string">" "</span>, <span class="string">" #"</span>, <span class="string">" @"</span>};</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < N; i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j < N; j++)</span><br><span class="line"> cout << s[map[i][j]];</span><br><span class="line"> cout << endl;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">(<span class="type">int</span> argc, <span class="type">char</span> *argv[])</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="built_in">fill</span>(close[<span class="number">0</span>], close[<span class="number">0</span>] + N * N, <span class="literal">false</span>); <span class="comment">// 将visit数组赋初值false</span></span><br><span class="line"> <span class="built_in">fill</span>(valueF[<span class="number">0</span>], valueF[<span class="number">0</span>] + N * N, <span class="number">0</span>); <span class="comment">// 初始化F全为0</span></span><br><span class="line"> <span class="built_in">fill</span>(pre[<span class="number">0</span>][<span class="number">0</span>], pre[<span class="number">0</span>][<span class="number">0</span>] + N * N * <span class="number">2</span>, <span class="number">-1</span>); <span class="comment">// 路径同样赋初值-1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// // 起点 // 终点</span></span><br><span class="line"> <span class="type">int</span> x0 = <span class="number">2</span>, y0 = <span class="number">4</span>, x1 = <span class="number">8</span>, y1 = <span class="number">6</span>;</span><br><span class="line"> <span class="type">int</span> t;</span><br><span class="line"></span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"test number: "</span>);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d"</span>,&t);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (t--){</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"input start: "</span>);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x0, &y0);</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"input destination: "</span>);</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &x1, &y1);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="built_in">isValidNode</span>(x0, y0, x0, y0))</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"Invalid input.\n"</span>);</span><br><span class="line"> <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="built_in">Astar</span>(x0, y0, x1, y1); <span class="comment">// A*算法</span></span><br><span class="line"> <span class="built_in">PrintPath</span>(x1, y1); <span class="comment">// 打印路径</span></span><br><span class="line"></span><br><span class="line">}}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><p>那这是如何结合两种算法的?</p><ul><li>对于起点s到i的路径,Dijkstra保证最优性</li><li>对于i到t的路径,采用贪心预测,选择i的下一个节点。</li><li>当i碰壁时,i被丢弃;回退到上一层重新选择节点j,而j任由Dijkstra保证最优性</li></ul><p>对于以上具体实现想法,我们设计一个函数抽象的表示: f(i)=g(i)+h(i) (g反映Dijkstra,h反映贪心)</p><ul><li>显然,g=0,A<em> 退化成贪心;h=0,A</em>退化成Dijkstra</li></ul><p>详细解释一遍:A*更具最小的f(i)选择下一个节点i,g(i)是走过的路径,已知;h(i)是预测未来走的路径,f(i)取决于h(i)的计算</p><p>A*的结果一定是最优吗?</p><p>当i到t终点时,h(t)=0,此时f(t)=g(t),而g(t)是由Dijstra保证的最短路径,因此A*能够得出最优解</p><p>总的来说,<strong>A*以Dijkstra获得最优结果,用贪心扩展方向,大大减少搜索的节点</strong></p><h3 id="h函数设计"><a href="#h函数设计" class="headerlink" title="h函数设计"></a>h函数设计</h3><p>对于二维平面图,三种办法近似计算h函数,设i的坐标(x1,x2),t(x2,y2)</p><ul><li>曼哈顿距离。节点只能上下左右移动,h(i)=abs(x1-x2)+abs(y1-y2)</li><li>对角线距离,节点能够8个方向上移动(新增东北,西北,东南,西南方向),h(i)=max{abs(x1-x2),abs(y1-y2)}</li><li>欧式距离,节点可以任意方向移动,h(i)=sqrt((x1-x2)<strong> 2+(y1-y2)</strong> 2)</li></ul><p>注意三条规则</p><ul><li>g和h采用相同距离计算方法</li><li>根据具体图的背景信息,选择相应的距离算法</li><li><strong>h优于实际存在的所有路径</strong>,这一点很关键,接下来我们会对此具体讨论<br>1.h(i) > i-t中存在的最短路径长度。设实际最短路径为path,由于计算h(i)扩展下一个节点,path被抛弃了(因为找到h(i)),选择非最短路径</li></ul><ol><li>h(i) < i-t中所有路径。这意味着在i-t中不存在长度为h(i)的路径,在搜寻h(i)的路径时,节点一定会碰壁。但这不要紧,因为Dijkstra算法让这些碰壁的点弹出,回退到合适的点,从而扩展出实际路径。</li></ol><p>参考:<a href="https://blog.csdn.net/denghecsdn/article/details/78778769">https://blog.csdn.net/denghecsdn/article/details/78778769</a></p><h3 id="A-算法例题"><a href="#A-算法例题" class="headerlink" title="A*算法例题"></a>A*算法例题</h3><p>from poj 2249</p><p>题面描述:<br>给定N个点,M条有向边。给出起点s和终点t,求其中第K个最短路径。</p><p>注意:若出发点与结束点为同一点,则一定要从出发点跑出去,再跑回来,才算最短路</p><p>第K短路则运用了A ∗ A<em>A∗的排除多余状态与优先队列B F S BFSBFS,第几次取出e d eded,即求到第几短路的性质来解决问题,A ∗ A</em>A∗主要起优化作用。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> </span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><iostream></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cstdio></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><cstring></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><algorithm></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><queue></span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string"><vector></span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> maxn = <span class="number">1010</span>;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> inf = <span class="number">0x3f3f3f3f</span>;</span><br><span class="line"><span class="type">int</span> n, m, s, t, k;</span><br><span class="line"><span class="type">bool</span> vis[maxn];</span><br><span class="line"><span class="type">int</span> dist[maxn];</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Node</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> v, c;v是节点编号,c是s到v估计值,c是g[i],dist为h[i]</span><br><span class="line"> <span class="built_in">Node</span> (<span class="type">int</span> _v = <span class="number">0</span>, <span class="type">int</span> _c = <span class="number">0</span>): <span class="built_in">v</span>(_v), <span class="built_in">c</span>(_c) {}</span><br><span class="line"> <span class="type">bool</span> <span class="keyword">operator</span> < (<span class="type">const</span> Node &rhs) <span class="type">const</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> c + dist[v] > rhs.c + dist[rhs.v]; 估价函数 fx = gx + hx 路径短先出队</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">Edge</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> to, cost;</span><br><span class="line"> <span class="built_in">Edge</span> (<span class="type">int</span> _to = <span class="number">0</span>, <span class="type">int</span> _cost = <span class="number">0</span>): <span class="built_in">to</span>(_to), <span class="built_in">cost</span>(_cost) {}</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">vector <Edge> E[maxn], revE[maxn];</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">addedge</span><span class="params">(<span class="type">int</span> u, <span class="type">int</span> v, <span class="type">int</span> w)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> E[v].<span class="built_in">push_back</span>(<span class="built_in">Edge</span>(u, w)); 反向加边</span><br><span class="line"> revE[u].<span class="built_in">push_back</span>(<span class="built_in">Edge</span>(v, w)); 正向加边</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">dijkstra</span><span class="params">(<span class="type">int</span> s, <span class="type">int</span> n)</span> 最短路径,从终点t遍历所有节点,找出所有节点到t的最短路径,即dist[v]为v到t的最短路径h[i]</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= n; ++i)</span><br><span class="line"> {</span><br><span class="line"> vis[i] = <span class="literal">false</span>;</span><br><span class="line"> dist[i] = inf;</span><br><span class="line"> }</span><br><span class="line"> dist[s] = <span class="number">0</span>;</span><br><span class="line"> priority_queue <Node> Q;</span><br><span class="line"> Q.<span class="built_in">push</span>(<span class="built_in">Node</span>(s, dist[s]));</span><br><span class="line"> <span class="keyword">while</span> (!Q.<span class="built_in">empty</span>())</span><br><span class="line"> {</span><br><span class="line"> Node tmp = Q.<span class="built_in">top</span>();</span><br><span class="line"> Q.<span class="built_in">pop</span>();</span><br><span class="line"> <span class="type">int</span> u = tmp.v;</span><br><span class="line"> <span class="keyword">if</span> (vis[u])</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> vis[u] = <span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < E[u].<span class="built_in">size</span>(); ++i)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">int</span> v = E[u][i].to, cost = E[u][i].cost;</span><br><span class="line"> <span class="keyword">if</span> (!vis[v] && dist[v] > dist[u] + cost)</span><br><span class="line"> {</span><br><span class="line"> dist[v] = dist[u] + cost;</span><br><span class="line"> Q.<span class="built_in">push</span>(<span class="built_in">Node</span>(v, dist[v]));</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><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">astar</span><span class="params">(<span class="type">int</span> s)</span></span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> 这里dist已经计算,优先队列排序会按照f[i]=g[i]+h[i](dist[i])排序,<span class="number">55</span>行</span><br><span class="line"> a*算法从s起点开始,通过反向边同样计算最短路径</span><br><span class="line"> priority_queue <Node> Q;</span><br><span class="line"> Q.<span class="built_in">push</span>(<span class="built_in">Node</span>(s, <span class="number">0</span>));</span><br><span class="line"> k--;</span><br><span class="line"> <span class="keyword">while</span> (!Q.<span class="built_in">empty</span>())</span><br><span class="line"> {</span><br><span class="line"> Node tmp = Q.<span class="built_in">top</span>();</span><br><span class="line"> Q.<span class="built_in">pop</span>();</span><br><span class="line"> <span class="type">int</span> u = tmp.v;节点编号</span><br><span class="line"> <span class="keyword">if</span> (u == t) 找到终点</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (k)</span><br><span class="line"> --k;</span><br><span class="line"> <span class="keyword">else</span> 第k次到达目标节点t</span><br><span class="line"> <span class="keyword">return</span> tmp.c;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < revE[u].<span class="built_in">size</span>(); ++i)</span><br><span class="line"> {</span><br><span class="line"> <span class="type">int</span> v = revE[u][i].to, cost = revE[u][i].cost;</span><br><span class="line"> Q.<span class="built_in">push</span>(<span class="built_in">Node</span>(v, tmp.c + cost));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">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="type">int</span> u, v, w;</span><br><span class="line"> <span class="keyword">while</span> (<span class="built_in">scanf</span>(<span class="string">"%d%d"</span>, &n, &m) != EOF)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i <= n; ++i)</span><br><span class="line"> {</span><br><span class="line"> E[i].<span class="built_in">clear</span>();</span><br><span class="line"> revE[i].<span class="built_in">clear</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i < m; ++i)</span><br><span class="line"> {</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d%d"</span>, &u, &v, &w);</span><br><span class="line"> <span class="built_in">addedge</span>(u, v, w);</span><br><span class="line"> }</span><br><span class="line"> <span class="built_in">scanf</span>(<span class="string">"%d%d%d"</span>, &s, &t, &k);</span><br><span class="line"> <span class="built_in">dijkstra</span>(t, n); t点到所有点的最短路</span><br><span class="line"> <span class="keyword">if</span> (dist[s] == inf)</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"-1\n"</span>);</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">if</span> (s == t) 起点等于终点特判</span><br><span class="line"> ++k;</span><br><span class="line"> <span class="built_in">printf</span>(<span class="string">"%d\n"</span>, <span class="built_in">astar</span>(s));</span><br><span class="line"> }</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><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h2 id="题选:图的最短路径算法,A-算法"><a href="#题选:图的最短路径算法,A-算法" class="headerlink" title="题选:图的最短路径算法,A*算法"></a>题选:图的最短路径算法,A*算法</h2><p>求解最短路径的算法有很多,而</summary>
</entry>
<entry>
<title>acm</title>
<link href="https://hongdouzza.github.io/posts/cb875d84.html"/>
<id>https://hongdouzza.github.io/posts/cb875d84.html</id>
<published>2024-05-21T14:12:11.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
</entry>
<entry>
<title>期末大作业-连连看</title>
<link href="https://hongdouzza.github.io/posts/b26e54f8.html"/>
<id>https://hongdouzza.github.io/posts/b26e54f8.html</id>
<published>2024-05-11T16:37:51.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
</entry>
<entry>
<title>栈和队列</title>
<link href="https://hongdouzza.github.io/posts/8d66b5f2.html"/>
<id>https://hongdouzza.github.io/posts/8d66b5f2.html</id>
<published>2024-04-18T19:50:22.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
</entry>
<entry>
<title>算法分析</title>
<link href="https://hongdouzza.github.io/posts/d68e5e2f.html"/>
<id>https://hongdouzza.github.io/posts/d68e5e2f.html</id>
<published>2024-04-16T15:47:33.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="算法分析"><a href="#算法分析" class="headerlink" title="算法分析"></a>算法分析</h1><ul><li>简介</li><li>观察</li><li>数学模型</li><li>增长顺序分类</li><li>算法理论</li><li>内存</li></ul><h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>为什么要做算法分析:</p><ul><li>预测性能</li><li>比对算法</li><li>提供保证</li><li>避免性能错误<br>最主要是避免性能bug</li></ul><p>成功算法的例子:FFT<br>快速傅里叶变换(这好像高斯以前手稿提过,认为含金量,故未发表,大物课上有提到)</p><p>暴力算法:n**2<br>FFT:n log n</p><p>当我们做算法分析时,应该考虑程序是否能适应大规模数据。性能是否足够(速度、内存)</p><h2 id="观察"><a href="#观察" class="headerlink" title="观察"></a>观察</h2><p>3-sum:给定n个整型数据,有多少组(每组三个元素)元素和为0</p><p>javac测算运行时间的函数:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span>[] a = In.readInts(args[<span class="number">0</span>]);</span><br><span class="line"> <span class="type">Stopwatch</span> <span class="variable">stopwatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Stopwatch</span>();</span><br><span class="line"> StdOut.println(ThreeSum.count(a));</span><br><span class="line"> <span class="type">double</span> <span class="variable">time</span> <span class="operator">=</span> stopwatch.elapsedTime();</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><p>我们发现对于不同的数据,程序运行时间大致为a<em>N*</em>b ,b=lg ratio</p><p>其中b的影响因子有:</p><ul><li>算法</li><li>输入数据</li></ul><p>而a的影响因子有:</p><ul><li>硬件:CPU,内存,缓存</li><li>软件:编译器,解释器,垃圾回收器</li><li>系统:操作系统、网路、其他app</li><li>以及算法和输入数据</li></ul><p>这很精确得到测量,但对比其他科学更便宜更方便</p><h2 id="数学模型"><a href="#数学模型" class="headerlink" title="数学模型"></a>数学模型</h2><p>运行时间:所有操作的成本*频率之和</p><ul><li>需要分析程序去决定操作集</li><li>成本取决于机器、编译器</li><li>频率取决于算法和输入数据</li></ul><p>不同的operation的运行的h时间是不同的(加减乘除、浮点数整数都不一样)</p><p>字符串的连接通常耗费很多时间,切勿滥用</p><p>“ It is convenient to have a measure of the amount of work involved<br> in a computing process, even though it be a very crude one. We may<br> count up the number of times that various elementary operations are<br> applied in the whole process and then given them various weights.<br> We might, for instance, count the number of additions, subtractions,<br> multiplications, divisions, recording of numbers, and extractions<br> of figures from tables. In the case of computing with matrices most<br> of the work consists of multiplications and writing down numbers,<br> and we shall therefore only attempt to count the number of<br> multiplications and recordings. “</p>]]></content>
<summary type="html"><h1 id="算法分析"><a href="#算法分析" class="headerlink" title="算法分析"></a>算法分析</h1><ul>
<li>简介</li>
<li>观察</li>
<li>数学模型</li>
<li>增长顺序分类</li>
<li>算法</summary>
</entry>
<entry>
<title>javaLearning_继承和多态</title>
<link href="https://hongdouzza.github.io/posts/2006c4c8.html"/>
<id>https://hongdouzza.github.io/posts/2006c4c8.html</id>
<published>2024-04-15T11:19:27.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="继承和多态"><a href="#继承和多态" class="headerlink" title="继承和多态"></a>继承和多态</h1><ul><li>继承</li><li>多态</li><li>特殊类:抽象类、final类、内部类</li><li>接口</li><li>object类</li><li>垃圾内存回收机制</li></ul><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><h3 id="类的继承"><a href="#类的继承" class="headerlink" title="类的继承"></a>类的继承</h3><p>继承:复用已存在类的属性和方法。</p><p>语法:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span>(){</span><br><span class="line"> String name</span><br><span class="line"> <span class="type">int</span> age;</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">eat</span><span class="params">()</span>{</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">void</span> <span class="title function_">speak</span><span class="params">()</span>{</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">clas Cat <span class="keyword">extends</span> <span class="title class_">Animal</span>(){</span><br><span class="line"> String color;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>java支持单继承,不允许多继承(子类只能有一个父类,类似树),可有多层继承。</p><p>子类继承父类的成员变量和成员方法(private成员也继承,private方法不继承),不继承父类的构造方法。</p><p>函数重写覆盖:子类方法和父类相同,调用子类对象时,子类方法覆盖父类。重写的方法权限不能缩小。</p><p>final方法不能被重写。</p>]]></content>
<summary type="html"><h1 id="继承和多态"><a href="#继承和多态" class="headerlink" title="继承和多态"></a>继承和多态</h1><ul>
<li>继承</li>
<li>多态</li>
<li>特殊类:抽象类、final类、内部类</li>
<li></summary>
</entry>
<entry>
<title>并查集作业和一些想法</title>
<link href="https://hongdouzza.github.io/posts/31b72f78.html"/>
<id>https://hongdouzza.github.io/posts/31b72f78.html</id>
<published>2024-04-09T22:02:57.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="三个简述题"><a href="#三个简述题" class="headerlink" title="三个简述题"></a>三个简述题</h1><p>ENGLISH</p><p>Q1:Question 1<br><strong>Social network connectivity</strong>. Given a social network containing n members and a log file containing m timestamps at which times pairs of members formed friendships, design an algorithm to determine the earliest time at which all members are connected (i.e., every member is a friend of a friend of a friend … of a friend). Assume that the log file is sorted by timestamp and that friendship is an equivalence relation. The running time of your algorithm should be logm logn or better and use extra space proportional to n.</p><p>A1:We can solve this problem using the weighted quick union algorithm. When initialized, the n members are each in an independent group, and count=n is recorded as the current remaining continuous flux. The weighted union operation is performed each time a log is read, and the remaining continued flux is reduced by 1. When the remaining flux drops to 1, the earliest connection time is the current log time。</p><p>Q2:<strong>Union-find with specific canonical element</strong>. Add a method find() to the<br>find(i) returns the largest element in the connected component containing i. The operations, union(), connected(), and find() should all take logarithmic time or better.For example, if one of the connected components is {1,2,6,9}{1,2,6,9}, then the find method should return 99 for each of the four elements in the connected components.</p><p>A2:We use the weighted quick-union algorithm to solve this problem. When initialized, each data is independent as a group, we define two arrays: arr records the maximum number of each group, arr[i]=i.; sz[i]=1 is the current size of each set of trees. Update arr[Root node of a larger tree] =max(arr[root(p)],arr[root(q)]) when performing the union operation, that is, when connecting a small tree to a large tree. When performing the find (i) operation, return the value of arr[root(i)]</p><p>Q3:<strong>Successor with delete</strong>.Given a set of n integers ={0,1,…,N-1}.S={0,1,…,n−1} and a sequence of requests of the following form:</p><ul><li>Remove x from S</li><li>Find the successor of x: the smallest y in S such that y≥x.</li></ul><p>design a data type so that all operations (except construction) take logarithmic time or better in the worst case.</p><p>A3:We use quick-union with compression. Design two arrays: parent[], to record the data set, initialized with each element pointing to itself; next[] Record the next successsor for i. When remove x from s is executed, the collection to which x belongs is first pointed to next[next[i]] by uinon(x, next[i]). We optimize the algorithm with path compression when merging the two collections. When we find the current successor to x, return the value next[root[x]]. So we’re going to have a logarithmic complexity</p><h1 id="通过并查集模拟渗流问题(ddl-4-14)"><a href="#通过并查集模拟渗流问题(ddl-4-14)" class="headerlink" title="通过并查集模拟渗流问题(ddl: 4.14)"></a>通过并查集模拟渗流问题(ddl: 4.14)</h1><p>这是Coursera留下的并查集大作业</p><p>渗流模型简述:在给定的n*n网格中,每一个格点有三个状态(关、开、充满水),当水流自上而下能够流通网格时(水的流通方向为上下左右),该模型处于渗流状态。</p><p>问题简述:数学家发现当每个格点打开的概率为p,存在一个阈值p<em> ,当n最足够大时,所有p>p</em> 的模型几乎都处于渗流,而所有p<p<em> 的模型几乎处于阻塞状态。然而数学家无法运用数理方法准确估计p</em> 的值。现在我们在计算机上设计程序模拟该p*。 <img src="/source/_posts/image.png" alt="Alt text"></p><p>设置头部虚拟节点(topsite)和尾部虚拟节点(tailsite),当topsite和tailsite连通时,改模型为渗流状态。头部虚拟节点相当于水源,尾部虚拟节点相当于水桶。当水桶里有水了,说明模型处于渗流状态。</p><p>使用weighted quick-union算法解决,当然直接用迭代判断渗流也可,但算法时间复杂度高于前者</p><p>提交的percplation.zip 包含 percolation.java、percolationStats.java</p><p>percolation.java:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> edu.princeton.cs.algs4.WeightedQuickUnionUF;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Percolation</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">boolean</span>[][] grid;</span><br><span class="line"> <span class="keyword">private</span> WeightedQuickUnionUF uf;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> topsite;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> bottomsite;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> n;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建n*n的网格(grid),初始化时所有格点为闭塞状态(false)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Percolation</span><span class="params">(<span class="type">int</span> n)</span>{</span><br><span class="line"> <span class="keyword">if</span>(n<<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"the n is outside"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="built_in">this</span>.n=n;</span><br><span class="line"> uf =<span class="keyword">new</span> <span class="title class_">WeightedQuickUnionUF</span>(n*n+<span class="number">2</span>);</span><br><span class="line"> topsite=n*n;</span><br><span class="line"> bottomsite=n*n+<span class="number">1</span>;</span><br><span class="line"> grid=<span class="keyword">new</span> <span class="title class_">boolean</span>[n][n];</span><br><span class="line"> <span class="built_in">this</span>.n=n;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">open</span><span class="params">(<span class="type">int</span> row, <span class="type">int</span> col)</span>{</span><br><span class="line"> <span class="keyword">if</span>(row<<span class="number">0</span>||row>n-<span class="number">1</span>||col<<span class="number">0</span>||col>n-<span class="number">1</span>)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"the row/col is outside"</span>);</span><br><span class="line"> <span class="type">int</span> [][] direction={{-<span class="number">1</span>, <span class="number">0</span>}, {<span class="number">1</span>, <span class="number">0</span>}, {<span class="number">0</span>, -<span class="number">1</span>}, {<span class="number">0</span>, <span class="number">1</span>}};</span><br><span class="line"> <span class="keyword">if</span>(!grid[row][col]){</span><br><span class="line"> grid[row][col]=<span class="literal">true</span>;</span><br><span class="line"> <span class="keyword">if</span>(row==<span class="number">0</span>){</span><br><span class="line"> uf.union(encode(row,col),topsite);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(row==n-<span class="number">1</span>)</span><br><span class="line"> {uf.union(row*n+col,bottomsite);</span><br><span class="line"> grid[row][col]=<span class="literal">true</span>;}</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span>[] d:direction){</span><br><span class="line"> <span class="type">int</span> addrow=row+d[<span class="number">0</span>];</span><br><span class="line"> <span class="type">int</span> addcol=col+d[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">if</span>(addrow>=<span class="number">0</span>&&addcol<=n-<span class="number">1</span>&&addcol>=<span class="number">0</span>&&addcol<=n-<span class="number">1</span>&&isOpen(addrow,addcol)){</span><br><span class="line"> uf.union(encode(row,col),encode(addrow,addcol));</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><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">public</span> <span class="type">boolean</span> <span class="title function_">isOpen</span><span class="params">(<span class="type">int</span> row, <span class="type">int</span> col)</span>{</span><br><span class="line"> <span class="keyword">if</span>(row<<span class="number">0</span>||row><span class="built_in">this</span>.n-<span class="number">1</span>||col<<span class="number">0</span>||col><span class="built_in">this</span>.n-<span class="number">1</span>){</span><br><span class="line"> <span class="comment">//throw new IllegalArgumentException("the row/col is outside");</span></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 class="keyword">return</span> grid[row][col];</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="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFull</span><span class="params">(<span class="type">int</span> row, <span class="type">int</span> col)</span>{</span><br><span class="line"> <span class="keyword">if</span>(row<<span class="number">1</span>||row>n||col<<span class="number">1</span>||col>n)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"the row/col is outside"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> uf.find(encode(row,col))== uf.find(topsite)&&isOpen(row,col);</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="keyword">public</span> <span class="type">int</span> <span class="title function_">numberOfOpenSites</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">int</span> count=<span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<n;i++){</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> j=<span class="number">0</span>;j<n;j++){</span><br><span class="line"> <span class="keyword">if</span>(isOpen(i, j)==<span class="literal">true</span>)</span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> count;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过查看bottoemsite是否连通topsite判断模型是否渗流(connected()方法已被弃用了)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">percolates</span><span class="params">()</span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> uf.find(topsite)==uf.find(bottomsite);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">encode</span><span class="params">(<span class="type">int</span> row ,<span class="type">int</span> col)</span>{</span><br><span class="line"> <span class="keyword">return</span> row*n+col;</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><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// test client (optional)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>{</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><br></pre></td></tr></table></figure></p><p>percolationStats.java:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> edu.princeton.cs.algs4.StdRandom;</span><br><span class="line"><span class="keyword">import</span> edu.princeton.cs.algs4.StdStats;</span><br><span class="line"><span class="keyword">import</span> edu.princeton.cs.algs4.StdOut;</span><br><span class="line"><span class="keyword">import</span> edu.princeton.cs.algs4.StdIn;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PercolationStats</span> {</span><br><span class="line"> <span class="comment">//x数组存储每次实验的渗流概率</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">double</span>[] x;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> n;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> T;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 测试T次n*n的模型</span></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">PercolationStats</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> trials)</span>{</span><br><span class="line"> <span class="built_in">this</span>.n=n;</span><br><span class="line"> T=trials;</span><br><span class="line"> <span class="keyword">if</span>(<span class="built_in">this</span>.n<=<span class="number">0</span>||T<=<span class="number">0</span>)</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">"the n/trials is outside"</span>);</span><br><span class="line"> x=<span class="keyword">new</span> <span class="title class_">double</span>[trials];</span><br><span class="line"> <span class="type">int</span> row;</span><br><span class="line"> <span class="type">int</span> col;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<trials;i++){</span><br><span class="line"> Percolation a=<span class="keyword">new</span> <span class="title class_">Percolation</span>(n);</span><br><span class="line"> <span class="keyword">while</span>(!a.percolates()){</span><br><span class="line"> <span class="keyword">do</span>{</span><br><span class="line"> row=StdRandom.uniformInt(n);</span><br><span class="line"> col=StdRandom.uniformInt(n);}<span class="keyword">while</span> (a.isOpen(row,col));</span><br><span class="line"> a.open(row,col);</span><br><span class="line"></span><br><span class="line"> };</span><br><span class="line"> x[i]=a.numberOfOpenSites()/(<span class="type">double</span>)(n*n);</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="comment">// sample mean of percolation threshold</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">mean</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">double</span> m;</span><br><span class="line"> m=StdStats.mean(x);</span><br><span class="line"> <span class="keyword">return</span> m;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// sample standard deviation of percolation threshold</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">stddev</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">double</span> s;</span><br><span class="line"> s=StdStats.stddev(x);</span><br><span class="line"> <span class="keyword">return</span> s;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// low endpoint of 95% confidence interval</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">confidenceLo</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">double</span> c;</span><br><span class="line"> <span class="type">double</span> s=StdStats.stddev(x);</span><br><span class="line"> c=StdStats.mean(x)-<span class="number">1.96</span>*s/Math.sqrt(T);</span><br><span class="line"> <span class="keyword">return</span> 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="comment">// high endpoint of 95% confidence interval</span></span><br><span class="line"> <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">confidenceHi</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">double</span> c;</span><br><span class="line"> <span class="type">double</span> s=StdStats.stddev(x);</span><br><span class="line"> c=StdStats.mean(x)+<span class="number">1.96</span>*s/Math.sqrt(T);</span><br><span class="line"> <span class="keyword">return</span> 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="comment">// test client (see below)</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>{</span><br><span class="line"> <span class="type">int</span> n;</span><br><span class="line"> <span class="type">int</span> T;</span><br><span class="line"> n=StdIn.readInt();</span><br><span class="line"> T=StdIn.readInt();</span><br><span class="line"> PercolationStats m=<span class="keyword">new</span> <span class="title class_">PercolationStats</span>(n,T);</span><br><span class="line"> StdOut.println(<span class="string">"mean = "</span>+m.mean());</span><br><span class="line"> StdOut.println(<span class="string">"stddev = "</span>+m.stddev());</span><br><span class="line"> StdOut.println(<span class="string">"95% confidence interval = ["</span>+m.confidenceLo()+<span class="string">","</span>+m.confidenceHi()+<span class="string">"]"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html"><h1 id="三个简述题"><a href="#三个简述题" class="headerlink" title="三个简述题"></a>三个简述题</h1><p>ENGLISH</p>
<p>Q1:Question 1<br><strong>Social network con</summary>
</entry>
<entry>
<title>algorithms1</title>
<link href="https://hongdouzza.github.io/posts/b3c0369f.html"/>
<id>https://hongdouzza.github.io/posts/b3c0369f.html</id>
<published>2024-04-08T13:52:00.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="【学习】并查集"><a href="#【学习】并查集" class="headerlink" title="【学习】并查集"></a>【学习】并查集</h1><p>采用princton的Coursera课程:<a href="https://www.coursera.org/learn/algorithms-part1">algorithms1&2</a></p><p>union-find:</p><ul><li>dynamic connecticity</li><li>quick find</li><li>quck union</li><li>improvemnts</li><li>applications</li></ul><h2 id="动态连通性"><a href="#动态连通性" class="headerlink" title="动态连通性"></a>动态连通性</h2><p>union commamd指令: 连接两个对象<br>find/connected query指令: 检查是否有两个对象连通的路径</p><p>应用场景:在编程时,将整数作为数组的索引时<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//以下使用algs4。jar包</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span></span><br><span class="line">{</span><br><span class="line"> <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> StdIn.readInt();</span><br><span class="line"> <span class="type">UF</span> <span class="variable">uf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UF</span>(N);</span><br><span class="line"> <span class="keyword">while</span> (!StdIn.isEmpty())</span><br><span class="line"> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">p</span> <span class="operator">=</span> StdIn.readInt();</span><br><span class="line"> <span class="type">int</span> <span class="variable">q</span> <span class="operator">=</span> StdIn.readInt();</span><br><span class="line"> <span class="keyword">if</span> (!uf.connected(p, q))</span><br><span class="line"> {</span><br><span class="line"> uf.union(p, q);</span><br><span class="line"> StdOut.println(p + <span class="string">" "</span> + q);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h2 id="快速查找-Quick-find"><a href="#快速查找-Quick-find" class="headerlink" title="快速查找(Quick-find)"></a>快速查找(Quick-find)</h2><h3 id="java实现方式"><a href="#java实现方式" class="headerlink" title="java实现方式"></a>java实现方式</h3><p>数据结构:</p><ul><li>长度为N的整数数组 id[]</li><li>当且仅当p和q的id值相同,表示p和q连通<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">QuickFind</span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span>[] id;</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">QuickFind</span><span class="params">(<span class="type">int</span> N)</span>{</span><br><span class="line"> id=<span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<N;i++){</span><br><span class="line"> id[i]=i;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">connected</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> q)</span>{</span><br><span class="line"> <span class="keyword">return</span> id[p]==id[q];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">Union</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> q)</span>{</span><br><span class="line"> <span class="type">int</span> pid=id[p];</span><br><span class="line"> <span class="type">int</span> qid=id[q];</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<id.length;i++){</span><br><span class="line"> <span class="keyword">if</span>(id[i]==pid){</span><br><span class="line"> id[i]=qid;</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><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析:"><a href="#算法分析:" class="headerlink" title="算法分析:"></a>算法分析:</h3>快速查找too slow?</li><li>cost model: 初始化、union都需要遍历整个数组,复杂度正比于N;查找很快,复杂度为常数</li><li>union太expensive:N**2的复杂度</li></ul><h2 id="快速并集(Quick-union-lazy-approach-)"><a href="#快速并集(Quick-union-lazy-approach-)" class="headerlink" title="快速并集(Quick-union [lazy approach])"></a>快速并集(Quick-union [lazy approach])</h2><p>数据结构:树型</p><ul><li>长度为N的整数数组 id[]</li><li>id[i]时id的父节点</li><li>i的root节点为id[id[id[…id[i]…]]]</li></ul><p>对象连通? 判断p和q的根节点是否相同</p><p>集合:当连接两个对象p,q,设置p的根节点为q根节点的id</p><p>java实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">QucikUnion</span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span>[] id;</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">QucikUnion</span><span class="params">(<span class="type">int</span> N)</span>{</span><br><span class="line"> id=<span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<N;i++){</span><br><span class="line"> id[i]=i;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">root</span><span class="params">(<span class="type">int</span> i)</span>{</span><br><span class="line"> <span class="keyword">while</span>(id[i]!=i){</span><br><span class="line"> i=id[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">connected</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> q)</span>{</span><br><span class="line"> <span class="keyword">return</span> root(p)==root(q);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">union</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> q)</span>{</span><br><span class="line"> id[root(p)]=id[root(q)];</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>然而quick-union算法也无法适应大规模数据<br>cost model:</p><div class="table-container"><table><thead><tr><th style="text-align:left">algorithm</th><th style="text-align:left">initialize</th><th style="text-align:left">union</th><th style="text-align:left">find</th></tr></thead><tbody><tr><td style="text-align:left">quick-find</td><td style="text-align:left">N</td><td style="text-align:left">N</td><td style="text-align:left">1</td></tr><tr><td style="text-align:left">quick-union</td><td style="text-align:left">N</td><td style="text-align:left">N(包含找根节点)</td><td style="text-align:left">N</td></tr></tbody></table></div><p>我们对比快速查找和快速并集,不难发现各自的缺陷</p><p>快速查找:</p><ul><li>并集费时(N)</li><li>树是平展的,但维持平铺的状态需要费时(怎么理解?)</li></ul><p>快速并集:</p><ul><li>树高</li><li>查找费时(N)</li></ul><p>所以,我们接下来对并查做一些改进。</p><h2 id="加权快速并集(Weighted-quick-union)和压缩路径快速并集-Quick-union-with-path-compression"><a href="#加权快速并集(Weighted-quick-union)和压缩路径快速并集-Quick-union-with-path-compression" class="headerlink" title="加权快速并集(Weighted quick-union)和压缩路径快速并集(Quick union with path compression)"></a>加权快速并集(Weighted quick-union)和压缩路径快速并集(Quick union with path compression)</h2><p>我们跟踪记录生成树的大小(包含对象的个数),将其作为权。并集的时候,权轻的树根节点改为权重的根节点。换句话说,就是小树接到大树下,而非前树接到后树下。这样降低所有节点到根节点的平均距离,并且避免树过深的的算法叫<strong>加权快速并查</strong></p><h2 id="加权快速并查"><a href="#加权快速并查" class="headerlink" title="加权快速并查"></a>加权快速并查</h2><p>数据结构:与快速查找相同,<strong>增加sz[i]存储以i为根节点的树的大小(树下包含对象的数量)</strong></p><p>并集:小树接到大树下,实时更新sz[i]<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">QucikUnion</span>{</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span>[] id;</span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">QucikUnion</span><span class="params">(<span class="type">int</span> N)</span>{</span><br><span class="line"> id=<span class="keyword">new</span> <span class="title class_">int</span>[N];</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">0</span>;i<N;i++){</span><br><span class="line"> id[i]=i;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">root</span><span class="params">(<span class="type">int</span> i)</span>{</span><br><span class="line"> <span class="keyword">while</span>(id[i]!=i){</span><br><span class="line"> i=id[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">connected</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> q)</span>{</span><br><span class="line"> <span class="keyword">return</span> root(p)==root(q);</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">union</span><span class="params">(<span class="type">int</span> p,<span class="type">int</span> q)</span>{</span><br><span class="line"> <span class="keyword">if</span>(sz[p]>sz[q])</span><br><span class="line"> {id[root(q)]=root(p);</span><br><span class="line"> sz[p]+=sz[q];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span></span><br><span class="line"> {id[root(p)]=root(q);</span><br><span class="line"> sz[q]+=sz[p];</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><br></pre></td></tr></table></figure><br>算法分析:</p><p>1.<strong>运行时间的分析</strong>:</p><ul><li>查找:时间和p、q的深度成正比</li><li>并集:时间为常量</li></ul><p>2.<strong>任意节点X最大为lgN</strong></p><p>以下为简答的数学证明(注意:在计算机领域,lgN底数为2):</p><ol><li><p><strong>定义</strong>:令(N)表示节点总数,(T_i)表示进行了(i)次并集操作后的某个树的大小(节点数),(h_i)表示这个树的高度(即树中任意节点的最大深度)。</p></li><li><p><strong>观察</strong>:在加权快速并集中,每次并集操作都是将一个较小的树连接到一个较大的树上。因此,每次树的大小至少翻倍(对原先的小树而言)。</p></li><li><p><strong>数学归纳</strong>:</p><ul><li><strong>基础情况</strong>:当树大小(T = 1)时,高度(h = 0),满足(h \leq \lg T)。</li><li><strong>归纳步骤</strong>:假设对于树的大小为(T<em>k)时,其高度(h_k \leq \lg T_k)成立。当执行一次并集操作,将一个大小至少为(T_k)的树连接到另一个大小至少为(T_k)的树上时,结果树的大小至少为(2T_k),因此新树的高度(h</em>{k+1} = h_k + 1)(因为最多增加一层)。</li></ul></li><li><p><strong>证明</strong>:根据归纳步骤,新的高度(h<em>{k+1} \leq \lg T_k + 1 = \lg (2T_k)(完全二叉树时) = \lg T</em>{k+1})。这证明了在任意时刻,树的高度(深度)(h)总是小于等于(\lg N),其中(N)是节点总数。</p></li></ol><h3 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h3><p>因此,我们可以得出结论,在加权快速并集算法中,任何节点(x)的深度最大为(\lg N)。这个性质保证了查找操作的高效性,因为查找路径长度的上限是对数级别的。</p><div class="table-container"><table><thead><tr><th style="text-align:left">algorithm</th><th style="text-align:left">initialize</th><th style="text-align:left">union</th><th style="text-align:left">find</th></tr></thead><tbody><tr><td style="text-align:left">quick-find</td><td style="text-align:left">N</td><td style="text-align:left">N</td><td style="text-align:left">1</td></tr><tr><td style="text-align:left">quick-union</td><td style="text-align:left">N</td><td style="text-align:left">N(包含找根节点)</td><td style="text-align:left">N</td></tr><tr><td style="text-align:left">weighted</td><td style="text-align:left">N</td><td style="text-align:left">lgN(包含找根节点)</td><td style="text-align:left">lgN</td></tr></tbody></table></div><p>我们还可以进一步优化</p><h2 id="路径压缩快速并集"><a href="#路径压缩快速并集" class="headerlink" title="路径压缩快速并集"></a>路径压缩快速并集</h2><p>在快速并集时,在找到根节点时,将之前的检查点重新直接指向根节点,这样压缩了之后的检查路径的算法叫<strong>路径压缩快速并集(Quick union with path compression)</strong></p><p>java实现方式<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">...</span><br><span class="line"><span class="keyword">private</span> <span class="type">int</span> <span class="title function_">root</span><span class="params">(<span class="type">int</span> i)</span>{</span><br><span class="line"> <span class="keyword">while</span>(id[i]!=i){</span><br><span class="line"> id[i]=id[id[i]];</span><br><span class="line"> i=id[i];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>但还可以进一步优化算法。</p><h2 id="集大成的玩意儿"><a href="#集大成的玩意儿" class="headerlink" title="集大成的玩意儿"></a>集大成的玩意儿</h2><p>怎么优化呢?</p><p>很简单,加权和路径压缩搅一块儿就好啦。所以诞生了这么个玩意儿。<br><strong>Weighted</strong> quick-union <strong>with path compression</strong>!!</p><p>接下来简单介绍对其平摊分析</p><h3 id="平摊分析(Amortized-analysis)"><a href="#平摊分析(Amortized-analysis)" class="headerlink" title="平摊分析(Amortized analysis)"></a><strong>平摊分析(Amortized analysis)</strong></h3><p>Q:什么是平摊分析?<br>A:是用于算法分析的方法。在使用平摊分析前须知道数据结构<strong>各种操作所可能发生的时间</strong>,并计算出<strong>最坏情况下</strong>的操作情况并加以平均。能够确认<strong>最坏情况性能的每次操作耗费的平均时间</strong>,但不能确认平均情况性能。(from wikipedia)</p><p>1.<strong>Hopcroft-Ulman, Tarjan</strong> 提出:从一个空的数据结构开始,对N个对象执行M次并查集操作(包括查找和合并)最多需要 c<em>(N + M \lg</em> N) 次数组访问。其中,lg<em> N 是迭代对数函数,表示需要多少次迭代对数运算才能使N减少到1以下。这是一个增长非常缓慢的函数,对于所有实际的N值,lg^</em> N 都是一个非常小的常数。如下:</p><ul><li>当 N = 1 时,lg^* N = 0</li><li>当 N = 2 时,lg^* N = 1</li><li>当 N 增加到16时,lg^* N = 3</li><li>直到 N = 65536 时,lg^* N 才变成4</li></ul><p>2.分析可以进一步改进为 N + M \alpha(M, N),其中 alpha(M, N) 是阿克曼函数的逆函数,也是一个非常慢增长的函数。这表明在实际应用中,加权快速合并算法(Weighted Quick-Union)配合路径压缩(Path Compression,简称WQUPC)的性能非常接近线性时间。</p><p>3.线性时间算法问题:对于M次并查集操作在N个对象上,是否存在线性时间算法?理论上,基于加权快速合并与路径压缩的分析(WQUPC),并不是严格的线性时间。</p><ul><li>然而,实际上,这个算法的性能非常接近线性时间,考虑到数据输入的成本,其代价在常数因子范围内。</li></ul><h3 id="Amazing-Fact"><a href="#Amazing-Fact" class="headerlink" title="Amazing Fact"></a>Amazing Fact</h3><p>!!</p><ul><li><strong>Fredman-Saks</strong> 提出的惊人事实:<strong>不存在严格的线性时间算法</strong>。即便是WQUPC算法,也不能在理论上达到完全的线性时间复杂度。</li></ul><h3 id="WQUPC"><a href="#WQUPC" class="headerlink" title="WQUPC"></a>WQUPC</h3><p>WQUPC通过维护子树大小并在执行查找操作时压缩路径来优化合并操作,使得后续的查找操作更高效。通过这种方式,算法的平均时间复杂度<strong>非常接近线性</strong>,尽管在最坏情况下并非完全线性,但在实际应用中表现出色。</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><div class="table-container"><table><thead><tr><th style="text-align:left">algorithm</th><th style="text-align:left">worst-case time</th></tr></thead><tbody><tr><td style="text-align:left">quick-find</td><td style="text-align:left">M N</td></tr><tr><td style="text-align:left">weighted QU</td><td style="text-align:left">M N</td></tr><tr><td style="text-align:left">weighted QU</td><td style="text-align:left">N + M log N</td></tr><tr><td style="text-align:left">QU+compression</td><td style="text-align:left">N + M log N</td></tr><tr><td style="text-align:left">WQUPC</td><td style="text-align:left">N+ M lg*N</td></tr></tbody></table></div><p>事实证明,超级计算机靠暴力无法解决的,超级算法可以</p><h2 id="应用场景"><a href="#应用场景" class="headerlink" title="应用场景"></a>应用场景</h2><p>大致应用场景直接抄PPT上的内容了,这块主要讲的是渗流问题</p><ul><li>渗流</li><li>游戏(围棋,Hex)</li><li>动态连通性</li><li>最小公共祖先</li><li>有限状态自动机的等价性</li><li>物理中的Hoshen-Kopelman算法</li><li>Hinley-Milner多态类型推断</li><li>Kruskal’s最小生成树算法</li><li>Fortran中编译等价语句</li><li>形态学属性的开启和关闭</li><li>图像处理中Matlab的bwlabel()函数</li></ul><p>渗流问题Coursera留了一个大作业,下篇具体写</p><h2 id="全篇总结"><a href="#全篇总结" class="headerlink" title="全篇总结"></a>全篇总结</h2><p>并查集主要解决的是动态连通性问题,为了优化算法引入了树模型作为对象连通结构。那么普通的quick-union在面临大规模数据可能遇到两个问题:树过高,路径过长;于是我们分别用加权和路径压缩算法加以解决,最后尝试将加权和路径压缩搅在一起成了本集顶级战力WQUPC。</p><p>接下来要滚去写大作业力</p>]]></content>
<summary type="html"><h1 id="【学习】并查集"><a href="#【学习】并查集" class="headerlink" title="【学习】并查集"></a>【学习】并查集</h1><p>采用princton的Coursera课程:<a href="https://www.courser</summary>
</entry>
<entry>
<title>java学习记录2</title>
<link href="https://hongdouzza.github.io/posts/fd87d377.html"/>
<id>https://hongdouzza.github.io/posts/fd87d377.html</id>
<published>2024-04-08T10:45:38.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="【JAVA学习笔记2】-数组和java常用类"><a href="#【JAVA学习笔记2】-数组和java常用类" class="headerlink" title="【JAVA学习笔记2】 数组和java常用类"></a>【JAVA学习笔记2】 数组和java常用类</h1><h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p>数组的概念:</p><ul><li>一维数组</li><li>二维数组</li><li>Arrays类</li><li>包装类</li><li>字符串类<br>集合类</li></ul><p>数组长度不可变</p><h2 id="对象数组"><a href="#对象数组" class="headerlink" title="对象数组"></a>对象数组</h2><p>数组初始化:int[]x={1,2,3,4}; (静态初始化)<br>声明时不能指定数组长度<br>动态初始化:int []x=new int[12];</p><p>垃圾内存(释放)</p><p>for each语句<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> []a=<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">3</span>];</span><br><span class="line"><span class="keyword">for</span>(<span class="type">int</span> i:arr){</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p>好像没什么好写的,数组这块很简单….</p><h2 id="包"><a href="#包" class="headerlink" title="包"></a>包</h2><h2 id="字符串类"><a href="#字符串类" class="headerlink" title="字符串类"></a>字符串类</h2><p>string:常量 stringbuffer stringbuilder:变量</p><p>定义字符串:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//int无法转char</span></span><br><span class="line"><span class="type">int</span> b[]={}</span><br></pre></td></tr></table></figure></p><p>int length方法</p><p>String toLowerCase()<br>String toUpperCase()</p><p>String类</p><p>字符串比大小(equals())<br>==: 比的是地址</p><p>可变字符串:<br>append()</p><p> test 2024.12.17</p>]]></content>
<summary type="html"><h1 id="【JAVA学习笔记2】-数组和java常用类"><a href="#【JAVA学习笔记2】-数组和java常用类" class="headerlink" title="【JAVA学习笔记2】 数组和java常用类"></a>【JAVA学习笔记2】 数组和java常</summary>
</entry>
<entry>
<title>Mysql学习记录1</title>
<link href="https://hongdouzza.github.io/posts/52057b56.html"/>
<id>https://hongdouzza.github.io/posts/52057b56.html</id>
<published>2024-04-01T14:59:12.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<p>这不是mysql学习记录<br>这是mysql<strong>重装记录</strong>。。。。。。<br>半年前装的mysql,密码忘了。开管理员权限怎么也无法跳过密码(纯怨种)。看了日志,说mysql配置出了问题,还可能数据损坏了….这怎么问题越挖越多。咱就是说人越菜,挖的坑越多。</p><p>准备重装了,原来卸载也比较繁琐:删库、删注册表、删软件</p><p>明天重装了</p>]]></content>
<summary type="html"><p>这不是mysql学习记录<br>这是mysql<strong>重装记录</strong>。。。。。。<br>半年前装的mysql,密码忘了。开管理员权限怎么也无法跳过密码(纯怨种)。看了日志,说mysql配置出了问题,还可能数据损坏了….这怎么问题越挖越多。咱就是说人越菜,</summary>
</entry>
<entry>
<title>阶段随笔</title>
<link href="https://hongdouzza.github.io/posts/8cc9c8aa.html"/>
<id>https://hongdouzza.github.io/posts/8cc9c8aa.html</id>
<published>2024-03-31T18:27:56.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="第四周"><a href="#第四周" class="headerlink" title="第四周"></a>第四周</h1><p>之前的事基本告一段落</p><ul><li>藤廊演出还算成功,合奏选曲不合适。每次演出后都很高兴,都有新的朋友</li><li>互联网+成功提桶跑路,不想多说什么,懒得喷(组织架构过于松散)</li><li>养成写博客习惯,挺好</li><li>下个阶段:录制WAGF和吉他中国比赛的视频;把之前写的一些riff、demo发展成完整的曲目</li><li>下周:物理学人类文明PPT、C语言大作业</li><li>幽梦影、词与物读完</li><li>开始学sql数据库知识</li><li>坚持跑步健身!!</li></ul><p>4.2号:<br>我再也不魔改博客了(:cry:),音乐馆给炸了,哈哈哈。算了明天改回aplayer吸底,属于装修走火入魔了。我今天都不敢部署到仓库里(:cold_sweat:)</p>]]></content>
<summary type="html"><h1 id="第四周"><a href="#第四周" class="headerlink" title="第四周"></a>第四周</h1><p>之前的事基本告一段落</p>
<ul>
<li>藤廊演出还算成功,合奏选曲不合适。每次演出后都很高兴,都有新的朋友</li>
<li</summary>
</entry>
<entry>
<title>GO学习记录1</title>
<link href="https://hongdouzza.github.io/posts/dff1b3b7.html"/>
<id>https://hongdouzza.github.io/posts/dff1b3b7.html</id>
<published>2024-03-29T23:43:49.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="【Go学习记录】入门"><a href="#【Go学习记录】入门" class="headerlink" title="【Go学习记录】入门"></a>【Go学习记录】入门</h1><p>起步基础先跟着<a href="https://tour.go-zh.org/">Go语言之旅</a><br>基础学习结构:</p><ul><li>包、变量、函数</li><li>流程控制语句</li><li>struct、slice和映射</li><li>方法和接口</li><li>并发</li></ul><h2 id="包、变量、函数"><a href="#包、变量、函数" class="headerlink" title="包、变量、函数"></a>包、变量、函数</h2><ul><li>程序从 main 包开始运行。包名与导入路径的最后一个元素一致。例如,<strong>“math/rand”</strong> 包中的源码均以 <strong>package rand</strong></li><li>尽量分组导入</li><li>一个名字以大写字母开头,那么它就是已导出的。Pizza 就是个已导出名,Pi 也同样,它导出自 math 包。pizza 和 pi 并未以大写字母开头,所以它们是未导出的。在导入一个包时,你只能引用其中已导出的名字。任何 <strong>“未导出”的名字在该包外均无法访问</strong>。</li><li>当连续两个或多个函数的已命名形参类型相同时,除最后一个类型以外,其它都可以省略。如下<code>x int</code>,<code>y int</code>缩写<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">add</span><span class="params">(x, y <span class="type">int</span>)</span></span> <span class="type">int</span> {</span><br><span class="line"><span class="keyword">return</span> x + y</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>Go 的返回值可被命名,它们会被视作定义在函数顶部的变量。如下返回了sum<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">split</span><span class="params">(sum <span class="type">int</span>)</span></span> (x, y <span class="type">int</span>) {</span><br><span class="line">x = sum * <span class="number">4</span> / <span class="number">9</span></span><br><span class="line">y = sum - x</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="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line">fmt.Println(split(<span class="number">17</span>))</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><code>var</code> 语句用于声明一个变量(列表),跟函数的参数列表一样,类型在最后</li><li><code>:=</code>可在类型明确的地方代替 <code>var</code></li><li><strong>Go 在不同类型的项之间赋值时需要显式转换</strong> </li><li>常量的声明与变量类似,使用 <code>const</code>关键字,不能用 <code>:=</code>语法声明</li></ul><h2 id="流程控制语句"><a href="#流程控制语句" class="headerlink" title="流程控制语句"></a>流程控制语句</h2><ul><li>for循环:Go 的 for 语句后面的三个构成部分外没有小括号<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span>{</span><br><span class="line"> sum:=<span class="number">0</span></span><br><span class="line"> <span class="keyword">for</span> i=<span class="number">0</span>;i<<span class="number">10</span>;i++{</span><br><span class="line"> sum+=i</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> fmt.Println(sum)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li>同样if语句表达式也不需要小括号,同 for 一样, if 语句可以在条件表达式前执行一个简单的语句(<strong>当然作用域只在if大括号内</strong>)。<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">pow</span><span class="params">(x, n, lim <span class="type">float64</span>)</span></span> <span class="type">float64</span> {</span><br><span class="line"><span class="keyword">if</span> v := math.Pow(x, n); v < lim {</span><br><span class="line"><span class="keyword">return</span> v</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> lim</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>defer语句:defer 语句会将函数推迟到<strong>外层函数返回之后执行</strong>(推迟调用的函数<strong>其参数会立即求值</strong>,但直到外层函数返回前该函数都不会被调用),<strong>推迟的函数调用会被压入一个栈(先进后出)中</strong><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line">fmt.Println(<span class="string">"counting"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line"><span class="keyword">defer</span> fmt.Println(i)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">fmt.Println(<span class="string">"done"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure>运行结果为<br>counting<br>done<br>9<br>8<br>7<br>6<br>5<br>4<br>3<br>2<br>1<br>0</li></ul><hr><h2 id="struct、slice和映射"><a href="#struct、slice和映射" class="headerlink" title="struct、slice和映射"></a>struct、slice和映射</h2><ul><li>指针:Go的指针保存了值的内存地址。与 C 不同,Go 没有指针运算,其余类似</li><li>结构体指针:有一个指向结构体的指针 <code>p</code>,可通过 <code>(*p).X</code>来访问其字段 <code>X</code>。也可以<strong>隐式间接引用</strong>,直接写 <code>p.X</code></li><li>结构体文法:直接列出字段的值来新分配一个结构体。使用 <code>Name</code>: 语法可以仅列出部分字段。(字段名的顺序无关。)特殊的前缀 & 返回一个指向结构体的指针。<figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line">X, Y <span class="type">int</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> (</span><br><span class="line">v1 = Vertex{<span class="number">1</span>, <span class="number">2</span>} <span class="comment">// 创建一个 Vertex 类型的结构体</span></span><br><span class="line">v2 = Vertex{X: <span class="number">1</span>} <span class="comment">// Y:0 被隐式地赋予</span></span><br><span class="line">v3 = Vertex{} <span class="comment">// X:0 Y:0</span></span><br><span class="line">p = &Vertex{<span class="number">1</span>, <span class="number">2</span>} <span class="comment">// 创建一个 *Vertex 类型的结构体(指针)</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">main</span><span class="params">()</span></span> {</span><br><span class="line">fmt.Println(v1, p, v2, v3)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li>数组:<code>var a [10]int</code><h3 id="切片(slice)"><a href="#切片(slice)" class="headerlink" title="切片(slice)"></a>切片(slice)</h3></li><li>切片:GO语言中切片比数组使用更频繁。与Python切片类似,<code>a[low : high]</code>为左闭右开。更改切片的元素会修改其底层数组中对应的元素,与它共享底层数组的切片都会观测到这些修改。</li><li>切片文法:这是一个数组的文法<code>[3]bool{true, true, false}</code><br>现构建一个引用了它的切片:<code>[]bool{true, true, false}</code></li><li>切片长度和容量:切片长度是指自身包含的元素个数,用<code>len()</code>获取;切片容量指的是<strong>切片的第一个元素开始数</strong>,到<strong>其底层数组元素末尾的个数</strong>,用<code>cap()</code>获取</li><li>nil切片:切片的零值是 nil。nil 切片的长度和容量为 0 且没有底层数组。</li><li>用 make 创建切片:切片可以用内建函数 make 来创建,也是创建动态数组的方式。make 函数会分配一个<strong>元素为零值</strong>的数组并返回一个引用了它的切片:<code>a := make([]int, 5) // len(a)=5</code>要指定它的容量,需向 <code>make</code>传入第三个参数:<code>b := make([]int, 0, 5) /* len(b)=0, cap(b)=5*/ b = b[:cap(b)] /*len(b)=5, cap(b)=5 */ b = b[1:] /* len(b)=4, cap(b)=4 */</code></li><li>切片可嵌套</li><li><p>append():为切片追加新的元素<code>func append(s []T, vs ...T) []T</code> append的第一个参数 <code>s</code>是一个元素类型为 T 的切片,其余类型为 T 的值将会追加到该切片的末尾。<strong>当 s 的底层数组太小,不足以容纳所有给定的值时,它就会分配一个更大的数组。返回的切片会指向这个新分配的数组。</strong>如下:</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">var</span> s []<span class="type">int</span></span><br><span class="line">printSlice(s)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 添加一个空切片</span></span><br><span class="line">s = <span class="built_in">append</span>(s, <span class="number">0</span>)</span><br><span class="line">printSlice(s)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个切片会按需增长</span></span><br><span class="line">s = <span class="built_in">append</span>(s, <span class="number">1</span>)</span><br><span class="line">printSlice(s)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以一次性添加多个元素</span></span><br><span class="line">s = <span class="built_in">append</span>(s, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>)</span><br><span class="line">printSlice(s)</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">printSlice</span><span class="params">(s []<span class="type">int</span>)</span></span> {</span><br><span class="line">fmt.Printf(<span class="string">"len=%d cap=%d %v\n"</span>, <span class="built_in">len</span>(s), <span class="built_in">cap</span>(s), s)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>Range:直接上code(切片这块基本和Python使用相同)</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> pow = []<span class="type">int</span>{<span class="number">1</span>, <span class="number">2</span>, <span class="number">4</span>, <span class="number">8</span>, <span class="number">16</span>, <span class="number">32</span>, <span class="number">64</span>, <span class="number">128</span>}</span><br><span class="line"><span class="comment">//第一个值(i)为当前元素的下标,第二个值(v)为该下标所对应元素的一份副本。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">for</span> i, v := <span class="keyword">range</span> pow {</span><br><span class="line">fmt.Printf(<span class="string">"2**%d = %d\n"</span>, i, v)</span><br><span class="line">}</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">main</span><span class="params">()</span></span> {</span><br><span class="line">pow := <span class="built_in">make</span>([]<span class="type">int</span>, <span class="number">10</span>)</span><br><span class="line"><span class="keyword">for</span> i := <span class="keyword">range</span> pow {</span><br><span class="line">pow[i] = <span class="number">1</span> << <span class="type">uint</span>(i) <span class="comment">// == 2**i</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">for</span> _, value := <span class="keyword">range</span> pow {</span><br><span class="line">fmt.Printf(<span class="string">"%d\n"</span>, value)</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ul><h3 id="映射-map"><a href="#映射-map" class="headerlink" title="映射(map)"></a>映射(map)</h3><p>映射将键映射到值。映射的零值为 nil 。<strong>nil 映射既没有键,也不能添加键</strong>。make 函数会返回给定类型的映射,并将其初始化备用。</p><p>映射文法与结构体类似,但要有键名:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line">Lat, Long <span class="type">float64</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> m = <span class="keyword">map</span>[<span class="type">string</span>]Vertex{</span><br><span class="line"><span class="string">"Bell Labs"</span>: Vertex{</span><br><span class="line"><span class="number">40.68433</span>, <span class="number">-74.39967</span>,</span><br><span class="line">},</span><br><span class="line"><span class="string">"Google"</span>: Vertex{</span><br><span class="line"><span class="number">37.42202</span>, <span class="number">-122.08408</span>,</span><br><span class="line">},</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>修改映射:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line">m := <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)</span><br><span class="line"></span><br><span class="line">m[<span class="string">"Answer"</span>] = <span class="number">42</span></span><br><span class="line">fmt.Println(<span class="string">"The value:"</span>, m[<span class="string">"Answer"</span>])</span><br><span class="line"></span><br><span class="line">m[<span class="string">"Answer"</span>] = <span class="number">48</span></span><br><span class="line">fmt.Println(<span class="string">"The value:"</span>, m[<span class="string">"Answer"</span>])</span><br><span class="line"></span><br><span class="line"><span class="built_in">delete</span>(m, <span class="string">"Answer"</span>)</span><br><span class="line">fmt.Println(<span class="string">"The value:"</span>, m[<span class="string">"Answer"</span>])</span><br><span class="line"></span><br><span class="line">v, ok := m[<span class="string">"Answer"</span>]</span><br><span class="line">fmt.Println(<span class="string">"The value:"</span>, v, <span class="string">"Present?"</span>, ok)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><h3 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h3><p>函数的包:Go 函数可以是一个闭包。闭包是一个函数值,它引用了其函数体之外的变量。该函数可以访问并赋予其引用的变量的值,换句话说,该函数被这些变量“绑定”在一起。</p><h2 id="方法和接口"><a href="#方法和接口" class="headerlink" title="方法和接口"></a>方法和接口</h2><p>方法是比较简单的,实际上跟类是一个应用(有方法接受对象的函数);接口有点难。</p><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><p>Go没有类:但可以为结构体类型定义<strong>方法</strong>。方法就是一类带特殊的 <strong>接收者</strong>参数的函数。方法接收者在它自己的参数列表内,位于 func 关键字和方法名之间。<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span>{</span><br><span class="line">X, Y <span class="type">float64</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="params">(v Vertex)</span></span> Abs() <span class="type">float64</span> {</span><br><span class="line"><span class="keyword">return</span> math.Sqrt(v.X*v.X + v.Y*v.Y)</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">main</span><span class="params">()</span></span> {</span><br><span class="line">v := Vertex{<span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line">fmt.Println(v.Abs())</span><br><span class="line">}</span><br><span class="line"><span class="comment">//也可为非结构体类型声明方法(只能在同一包内定义的类型的接收者声明方法,而不能为其它包内定义的类型(包括 int 之类的内建类型)的接收者声明方法。)</span></span><br><span class="line"><span class="keyword">type</span> MyFloat <span class="type">float64</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(f MyFloat)</span></span> Abs() <span class="type">float64</span> {</span><br><span class="line"><span class="keyword">if</span> f < <span class="number">0</span> {</span><br><span class="line"><span class="keyword">return</span> <span class="type">float64</span>(-f)</span><br><span class="line">}</span><br><span class="line"><span class="keyword">return</span> <span class="type">float64</span>(f)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>为指针接收者声明方法:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Vertex <span class="keyword">struct</span> {</span><br><span class="line">X, Y <span class="type">float64</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="params">(v Vertex)</span></span> Abs() <span class="type">float64</span> {</span><br><span class="line"><span class="keyword">return</span> math.Sqrt(v.X*v.X + v.Y*v.Y)</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="params">(v *Vertex)</span></span> Scale(f <span class="type">float64</span>) {</span><br><span class="line">v.X = v.X * f</span><br><span class="line">v.Y = v.Y * f</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">main</span><span class="params">()</span></span> {</span><br><span class="line">v := Vertex{<span class="number">3</span>, <span class="number">4</span>}</span><br><span class="line">v.Scale(<span class="number">10</span>)</span><br><span class="line">fmt.Println(v.Abs())</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>由于方法经常需要修改它的接收者,<strong>指针接收者</strong>比<strong>值接收者</strong>更常用。若使用<strong>值接收者</strong>,那么 Scale 方法会对原始 Vertex 值的<strong>副本</strong>进行操作。Scale 方法必须用指针接受者来更改<strong>main 函数中声明的 Vertex 的值</strong>。</p><p>针对指针的函数与方法:</p><ul><li><strong>带指针参数的函数</strong>必须接受一个指针<br><code>var v Vertex ScaleFunc(v, 5) // 编译错误!ScaleFunc(&v, 5) // OK</code></li><li>以<strong>指针为接收者的方法</strong>被调用时,接收者<strong>既能为值又能为指针</strong>:<br><code>var v Vertexv.Scale(5) // OK p := &v p.Scale(10) // OK</code><br>所以一般情况下,Go 会将语句 <code>v.Scale(5)</code>解释为 <code>(&v).Scale(5)</code></li></ul><p>所以选择指针为方法接收者的原因有这些:</p><ul><li>方法能够<strong>直接</strong>修改其接收者指向的值</li><li>避免在每次调用方法时<strong>复制</strong>该值。若值的类型为大型结构体时,这样做会更加高效</li></ul><h2 id="接口"><a href="#接口" class="headerlink" title="接口"></a>接口</h2><p>接口类型是由<strong>一组方法签名定义的集合</strong>。<br>接口与隐式实现:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">type</span> I <span class="keyword">interface</span> {</span><br><span class="line">M()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> T <span class="keyword">struct</span> {</span><br><span class="line">S <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 此方法表示类型 T 实现了接口 I,但我们无需显式声明此事。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(t T)</span></span> M() {</span><br><span class="line">fmt.Println(t.S)</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">main</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">var</span> i I = T{<span class="string">"hello"</span>}</span><br><span class="line">i.M()</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><p>接口值:可以像其它值一样传递,可用作函数的参数或返回值。接口值可以看做包含值和具体类型的元组:<code>(value, type)</code><strong>接口值调用方法时会执行其底层类型的同名方法。</strong>(如上例中i调用M()方法执行的是对T的方法)<br>底层为nil接口值:即便接口内的具体值为 nil,方法仍然会被 nil 接收者调用。如下:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> I <span class="keyword">interface</span> {</span><br><span class="line">M()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> T <span class="keyword">struct</span> {</span><br><span class="line">S <span class="type">string</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="params">(t *T)</span></span> M() {</span><br><span class="line"><span class="keyword">if</span> t == <span class="literal">nil</span> {</span><br><span class="line">fmt.Println(<span class="string">"<nil>"</span>)</span><br><span class="line"><span class="keyword">return</span></span><br><span class="line">}</span><br><span class="line">fmt.Println(t.S)</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">main</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">var</span> i I</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> t *T</span><br><span class="line">i = t</span><br><span class="line">describe(i)</span><br><span class="line">i.M()</span><br><span class="line"></span><br><span class="line">i = &T{<span class="string">"hello"</span>}</span><br><span class="line">describe(i)</span><br><span class="line">i.M()</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>然而nil 接口值既不保存值也不保存具体类型。为 nil 接口调用方法会产生运行时错误。如下:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> I <span class="keyword">interface</span> {</span><br><span class="line">M()</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">main</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">var</span> i I</span><br><span class="line">describe(i)</span><br><span class="line">i.M()</span><br><span class="line">}</span><br><span class="line"><span class="comment">//i接口的元组内并未包含能够指明该调用哪个 具体 方法的类型</span></span><br></pre></td></tr></table></figure></p><p>空接口:指定了零个方法的接口值<code>interface{}</code>空接口可保存任何类型的值。(因为每个类型都至少实现了零个方法。)空接口被用来处理未知类型的值。常见使用方法:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">var</span> i <span class="keyword">interface</span>{}</span><br><span class="line">describe(i)</span><br><span class="line"></span><br><span class="line">i = <span class="number">42</span></span><br><span class="line">describe(i)</span><br><span class="line"></span><br><span class="line">i = <span class="string">"hello"</span></span><br><span class="line">describe(i)</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">describe</span><span class="params">(i <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line">fmt.Printf(<span class="string">"(%v, %T)\n"</span>, i, i)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>类型断言:提供了访问接口值底层具体值的方式;判断一个接口值是否保存了一个特定的类型,类型断言可返回两个值:其<strong>底层值</strong>以及一个<strong>报告断言是否成功的布尔值</strong>。<code>t, ok := i.(T)</code>(若 i 保存了一个 T,那么 t 将会是其底层值,而 ok 为 true;否则,ok为false而t为T类型的零值)。<strong>类型断言语法和读取一个映射时有相同之处</strong></p><p>类型选择:是一种按顺序从几个类型断言中选择分支的结构。类型选择与一般的 switch 语句相似,不过类型选择中的 case 为类型(而非值), 它们针对给定接口值所存储的值的类型进行比较。其声明与类型断言 <code>i.(T)</code>的语法相同,<code>T</code>被替换成了关键字 <code>type</code>。<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">do</span><span class="params">(i <span class="keyword">interface</span>{})</span></span> {</span><br><span class="line"><span class="keyword">switch</span> v := i.(<span class="keyword">type</span>) {</span><br><span class="line"><span class="keyword">case</span> <span class="type">int</span>:</span><br><span class="line">fmt.Printf(<span class="string">"Twice %v is %v\n"</span>, v, v*<span class="number">2</span>)</span><br><span class="line"><span class="keyword">case</span> <span class="type">string</span>:</span><br><span class="line">fmt.Printf(<span class="string">"%q is %v bytes long\n"</span>, v, <span class="built_in">len</span>(v))</span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line">fmt.Printf(<span class="string">"I don't know about type %T!\n"</span>, v)</span><br><span class="line">}</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">main</span><span class="params">()</span></span> {</span><br><span class="line">do(<span class="number">21</span>)</span><br><span class="line">do(<span class="string">"hello"</span>)</span><br><span class="line">do(<span class="literal">true</span>)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>三个常用接口(包):</p><ul><li>Stringer</li><li>reader</li><li>image</li></ul><p>Stringer:fmt 包中定义的 Stringer 是最普遍的接口之一。它可以用字符串描述自己的类型。fmt 包(还有很多包)都通过此接口来打印值。</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">type</span> Stringer <span class="keyword">interface</span> {</span><br><span class="line"> String() <span class="type">string</span></span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>Reader:<code>io</code>包指定了 <code>io.Reader</code>接口,它表示从数据流的末尾进行读取。<code>io.Reader</code>接口有一个 Read 方法:<code>func (T) Read(b []byte) (n int, err error)</code>;Read 用数据填充给定的字节切片并返回填充的字节数和错误值。在遇到数据流的结尾时,它会返回一个 io.EOF 错误。<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</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">"io"</span></span><br><span class="line"><span class="string">"strings"</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">main</span><span class="params">()</span></span> {</span><br><span class="line">r := strings.NewReader(<span class="string">"Hello, Reader!"</span>)</span><br><span class="line"></span><br><span class="line">b := <span class="built_in">make</span>([]<span class="type">byte</span>, <span class="number">8</span>)</span><br><span class="line"><span class="keyword">for</span> {</span><br><span class="line">n, err := r.Read(b)</span><br><span class="line">fmt.Printf(<span class="string">"n = %v err = %v b = %v\n"</span>, n, err, b)</span><br><span class="line">fmt.Printf(<span class="string">"b[:n] = %q\n"</span>, b[:n])</span><br><span class="line"><span class="keyword">if</span> err == io.EOF {</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><br><span class="line"></span><br></pre></td></tr></table></figure></p><p>图像:image 包定义了 Image 接口:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> image</span><br><span class="line"></span><br><span class="line"><span class="keyword">type</span> Image <span class="keyword">interface</span> {</span><br><span class="line"> ColorModel() color.Model</span><br><span class="line"> Bounds() Rectangle</span><br><span class="line"> At(x, y <span class="type">int</span>) color.Color</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure></p><h2 id="并发"><a href="#并发" class="headerlink" title="并发"></a>并发</h2><h3 id="goroutine"><a href="#goroutine" class="headerlink" title="goroutine"></a>goroutine</h3><p>Go程(goroutine):是由 Go 运行时管理的轻量级线程。<code>go f(x, y, z)</code>会启动一个新的 Go 程并执行。<code>f(x, y, z)f</code>, <code>x</code> , <code>y</code>和 <code>z</code>的求值发生在当前的 Go 程中,而 f 的执行发生在新的 Go 程中。</p><p>Go 程在相同的地址空间中运行,因此在访问共享的内存时必须进行同步,<code>sync</code>包提供了这种能力.</p><h3 id="信道"><a href="#信道" class="headerlink" title="信道"></a>信道</h3><p>信道:带有类型的管道,通过它用信道操作符<code><-</code> 来发送或者接收值。<br><code>ch <- v // 将 v 发送至信道 ch v := <-ch // 从 ch 接收值并赋予 v</code>(“箭头”就是数据流的方向)</p><p>信道在使用前必须创建:<code>ch := make(chan int)</code>默认情况下,发送和接收操作在另一端准备好之前都会阻塞。这使得 Go 程可以在没有<strong>显式的锁或竞态变量的情况下进行同步</strong>。没懂…..以下为<strong>gpt的形象解释</strong></p><blockquote><p>想象一下你正在组织一场接力赛。在这个接力赛中,每个参赛者(代表一个Goroutine)需要等待前一个队友完成他的部分并传递接力棒(代表信道中的数据)给他,然后他才能开始跑。如果前一个队友还没有跑到(即没有数据发送到信道),下一个队友(接收操作)就必须等待。反之,如果一个队友到达并准备传递接力棒,但下一个队友还没有准备好接手,他也需要等待。这个过程确保了所有的参赛者按照正确的顺序开始和完成他们的部分,没有人会跑得太早或太晚,从而无需额外的指示(即无需显式的锁)就自然而然地实现了同步。</p><p>通过使用信道的这种阻塞机制,Go能够在并发程序中简化数据的同步和通信,避免了直接使用锁或处理竞态变量的复杂性。这使得编写并发程序变得更加直观和安全。</p><p>具体例子:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="string">"fmt"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">sum</span><span class="params">(s []<span class="type">int</span>, c <span class="keyword">chan</span> <span class="type">int</span>)</span></span> {</span><br><span class="line">sum := <span class="number">0</span></span><br><span class="line"><span class="keyword">for</span> _, v := <span class="keyword">range</span> s {</span><br><span class="line">sum += v</span><br><span class="line">}</span><br><span class="line">c <- sum <span class="comment">// 将和送入 c</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">main</span><span class="params">()</span></span> {</span><br><span class="line">s := []<span class="type">int</span>{<span class="number">7</span>, <span class="number">2</span>, <span class="number">8</span>, <span class="number">-9</span>, <span class="number">4</span>, <span class="number">0</span>}</span><br><span class="line"></span><br><span class="line">c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"><span class="keyword">go</span> sum(s[:<span class="built_in">len</span>(s)/<span class="number">2</span>], c)</span><br><span class="line"><span class="keyword">go</span> sum(s[<span class="built_in">len</span>(s)/<span class="number">2</span>:], c)</span><br><span class="line">x, y := <-c, <-c <span class="comment">// 从 c 中接收</span></span><br><span class="line"></span><br><span class="line">fmt.Println(x, y, x+y)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>带缓冲的信道:将缓冲长度作为第二个参数提供给 make 来初始化一个带缓冲的信道,<code>ch := make(chan int, 100)</code>;<strong>仅当信道的缓冲区填满后,向其发送数据时才会阻塞。当缓冲区为空时,接受方会阻塞</strong></p></blockquote><h3 id="range和close"><a href="#range和close" class="headerlink" title="range和close"></a>range和close</h3><p>发送者可通过<code>close</code>关闭一个信道来表示没有需要发送的值了。接收者可以通过为接收表达式分配第二个参数来测试信道是否被关闭:若没有值可以接收且信道已被关闭,那么在执行完<code>v, ok := <-ch</code>之后<code>ok</code>会被设置为 false。</p><p>循环<code>for i := range c</code>会不断从信道接收值,直到它被关闭。</p><p><strong>WARNING:</strong> 只有发送者才能关闭信道,而接收者不能。向一个已经关闭的信道发送数据会引发程序恐慌(panic)。</p><p><strong>WARNING:</strong> 信道与文件不同,通常情况下无需关闭它们。只有在必须告诉接收者不再有需要发送的值时才有必要关闭,例如终止一个 range 循环。以下为示例:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</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><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fibonacci</span><span class="params">(n <span class="type">int</span>, c <span class="keyword">chan</span> <span class="type">int</span>)</span></span> {</span><br><span class="line">x, y := <span class="number">0</span>, <span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < n; i++ {</span><br><span class="line">c <- x</span><br><span class="line">x, y = y, x+y</span><br><span class="line">}</span><br><span class="line"><span class="built_in">close</span>(c)</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">main</span><span class="params">()</span></span> {</span><br><span class="line">c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>, <span class="number">10</span>)</span><br><span class="line"><span class="keyword">go</span> fibonacci(<span class="built_in">cap</span>(c), c)</span><br><span class="line"><span class="keyword">for</span> i := <span class="keyword">range</span> c {</span><br><span class="line">fmt.Println(i)</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><h3 id="select语句"><a href="#select语句" class="headerlink" title="select语句"></a>select语句</h3><p><code>select</code>语句使一个 Go 程可以等待多个通信操作。</p><p><code>select</code>会阻塞到某个分支可以继续执行为止,这时就会执行该分支。当多个分支都准备好时会随机选择一个执行。<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">fibonacci</span><span class="params">(c, quit <span class="keyword">chan</span> <span class="type">int</span>)</span></span> {</span><br><span class="line">x, y := <span class="number">0</span>, <span class="number">1</span></span><br><span class="line"><span class="keyword">for</span> {</span><br><span class="line"><span class="keyword">select</span> {</span><br><span class="line"><span class="keyword">case</span> c <- x:</span><br><span class="line">x, y = y, x+y</span><br><span class="line"><span class="keyword">case</span> <-quit:</span><br><span class="line">fmt.Println(<span class="string">"quit"</span>)</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><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="title">main</span><span class="params">()</span></span> {</span><br><span class="line">c := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line">quit := <span class="built_in">make</span>(<span class="keyword">chan</span> <span class="type">int</span>)</span><br><span class="line"><span class="keyword">go</span> <span class="function"><span class="keyword">func</span><span class="params">()</span></span> {</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">10</span>; i++ {</span><br><span class="line">fmt.Println(<-c)</span><br><span class="line">}</span><br><span class="line">quit <- <span class="number">0</span></span><br><span class="line">}()</span><br><span class="line">fibonacci(c, quit)</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>没懂…</p><p>默认选择:当 select 中的其它分支都没有准备好时,default 分支就会执行。为了在尝试发送或者接收时不发生阻塞,可使用 default 分支:<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">select</span> {</span><br><span class="line"><span class="keyword">case</span> i := <-c:</span><br><span class="line"> <span class="comment">// 使用 i</span></span><br><span class="line"><span class="keyword">default</span>:</span><br><span class="line"> <span class="comment">// 从 c 中接收会阻塞时执行</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><h3 id="sync-Mutex"><a href="#sync-Mutex" class="headerlink" title="sync.Mutex"></a>sync.Mutex</h3><p>显而易见信道非常适合在各个 Go 程间进行通信。</p><p>但是如果我们并不需要通信呢?如果只是想保证每次只有一个 Go 程能够访问一个共享的变量,从而避免冲突?</p><p>这里涉及的概念叫做 <strong>互斥(mutual*exclusion)</strong> ,我们通常使用 <strong>互斥锁(Mutex)</strong> 这一数据结构来提供这种机制。</p><p>Go 标准库中提供了 <code>sync.Mutex</code>互斥锁类型及其两个方法:</p><ul><li><code>Lock</code></li><li><code>Unlock</code><br>我们可以通过在代码前调用<code>Lock</code>方法,在代码后调用<code>Unlock</code>方法来保证一段代码的互斥执行。参见 <code>Inc</code>方法。</li></ul><p>我们也可以用 defer 语句来保证互斥锁一定会被解锁。(参考<code>Value</code>方法)<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> main</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">"sync"</span></span><br><span class="line"><span class="string">"time"</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment">// SafeCounter 的并发使用是安全的。</span></span><br><span class="line"><span class="keyword">type</span> SafeCounter <span class="keyword">struct</span> {</span><br><span class="line">v <span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span></span><br><span class="line">mux sync.Mutex</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Inc 增加给定 key 的计数器的值。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *SafeCounter)</span></span> Inc(key <span class="type">string</span>) {</span><br><span class="line">c.mux.Lock()</span><br><span class="line"><span class="comment">// Lock 之后同一时刻只有一个 goroutine 能访问 c.v</span></span><br><span class="line">c.v[key]++</span><br><span class="line">c.mux.Unlock()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// Value 返回给定 key 的计数器的当前值。</span></span><br><span class="line"><span class="function"><span class="keyword">func</span> <span class="params">(c *SafeCounter)</span></span> Value(key <span class="type">string</span>) <span class="type">int</span> {</span><br><span class="line">c.mux.Lock()</span><br><span class="line"><span class="comment">// Lock 之后同一时刻只有一个 goroutine 能访问 c.v</span></span><br><span class="line"><span class="keyword">defer</span> c.mux.Unlock()</span><br><span class="line"><span class="keyword">return</span> c.v[key]</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">main</span><span class="params">()</span></span> {</span><br><span class="line">c := SafeCounter{v: <span class="built_in">make</span>(<span class="keyword">map</span>[<span class="type">string</span>]<span class="type">int</span>)}</span><br><span class="line"><span class="keyword">for</span> i := <span class="number">0</span>; i < <span class="number">1000</span>; i++ {</span><br><span class="line"><span class="keyword">go</span> c.Inc(<span class="string">"somekey"</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">time.Sleep(time.Second)</span><br><span class="line">fmt.Println(c.Value(<span class="string">"somekey"</span>))</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><hr><p>芜湖…..整理完了,(应该是copy完了);并发这块还不是很懂,其他还比较简单。<br>主要参考:<br> <a href="https://tour.go-zh.org/">GO语言之旅</a></p>]]></content>
<summary type="html"><h1 id="【Go学习记录】入门"><a href="#【Go学习记录】入门" class="headerlink" title="【Go学习记录】入门"></a>【Go学习记录】入门</h1><p>起步基础先跟着<a href="https://tour.go-zh.org</summary>
</entry>
<entry>
<title>TEST(博客又被我炸了,哈哈)</title>
<link href="https://hongdouzza.github.io/posts/cd52ad99.html"/>
<id>https://hongdouzza.github.io/posts/cd52ad99.html</id>
<published>2024-03-26T21:39:25.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>POST的md文档,没有自动生成html文档?或者说html文档内容为空</p><h2 id="今天豆瓣把数据还没写完"><a href="#今天豆瓣把数据还没写完" class="headerlink" title="//今天豆瓣把数据还没写完"></a>//今天豆瓣把数据还没写完</h2>]]></content>
<summary type="html"><h2 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h2><p>POST的md文档,没有自动生成html文档?或者说html文档内容为空</p>
<h2 id="今天豆瓣把数据还没写完"><a href=</summary>
</entry>
<entry>
<title>学生管理系统(C)</title>
<link href="https://hongdouzza.github.io/posts/8810ab0d.html"/>
<id>https://hongdouzza.github.io/posts/8810ab0d.html</id>
<published>2024-03-26T11:52:28.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="【C大作业】学生成绩管理系统(DDL:3-31)"><a href="#【C大作业】学生成绩管理系统(DDL:3-31)" class="headerlink" title="【C大作业】学生成绩管理系统(DDL:3.31)"></a>【C大作业】学生成绩管理系统(DDL:3.31)</h1><p> 背景:坏了,copy的代码还没读完,明天要交了(:cold_sweat:)</p><h2 id="问题描述"><a href="#问题描述" class="headerlink" title="问题描述"></a>问题描述</h2><p>背景设置:</p><ul><li>教学班学生人数和课程门数受限制:<strong>学生人数<=50</strong>,<strong>课程门数<=10</strong></li><li>采用<strong>链表</strong>构成所有学生记录</li><li>使用结构体表示每个学生信息(学号、姓名、考试成绩)</li></ul><p>系统具备的功能:</p><ul><li>增:增加后的记录<=50</li><li>删:删除多条记录</li><li>查:按<strong>学号和姓名</strong>查询</li><li>改</li><li>计算每位学生成绩总分和平均分</li><li>计算每门课总分和平均分</li><li>对学生记录排序:按学号升序、按姓名字典排序、按成绩总分/平均分排序</li><li>对学生记录统计:按成绩总分/平均分统计各个分数段的人数、占比,统计各个分数段人数、占比</li><li>数据存储:记录存盘操作,从磁盘读取已有学生记录</li></ul><h2 id="业务流程分析"><a href="#业务流程分析" class="headerlink" title="业务流程分析"></a>业务流程分析</h2><h2 id="文件结构:"><a href="#文件结构:" class="headerlink" title="文件结构:"></a>文件结构:</h2>]]></content>
<summary type="html"><h1 id="【C大作业】学生成绩管理系统(DDL:3-31)"><a href="#【C大作业】学生成绩管理系统(DDL:3-31)" class="headerlink" title="【C大作业】学生成绩管理系统(DDL:3.31)"></a>【C大作业】学生成绩管理系统</summary>
</entry>
<entry>
<title>java_Learning1</title>
<link href="https://hongdouzza.github.io/posts/dc874005.html"/>
<id>https://hongdouzza.github.io/posts/dc874005.html</id>
<published>2024-03-25T10:27:21.000Z</published>
<updated>2024-12-18T14:58:18.508Z</updated>
<content type="html"><![CDATA[<h1 id="【JAVA学习笔记1】-面向对象基础"><a href="#【JAVA学习笔记1】-面向对象基础" class="headerlink" title="【JAVA学习笔记1】 面向对象基础"></a>【JAVA学习笔记1】 面向对象基础</h1><p>JAVA知识点多,写点笔记,整理3.25 周一上午JAVA课内容,</p><h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><h3 id="private字段"><a href="#private字段" class="headerlink" title="private字段"></a>private字段</h3><p>当字段被修饰为<code>private</code>时,外部代码无法访问该字段。我们可以用调用类里的方法间接访问该字段,确保了封装性和安全性</p><h3 id="private方法"><a href="#private方法" class="headerlink" title="private方法"></a>private方法</h3><p>和<code>private</code>字段一样,<code>private</code>方法也不允许类外的代码调用。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Person</span> <span class="variable">ming</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line"> ming.setBirth(<span class="number">2008</span>);</span><br><span class="line"> System.out.println(ming.getAge());</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> birth;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setBirth</span><span class="params">(<span class="type">int</span> birth)</span> {</span><br><span class="line"> <span class="built_in">this</span>.birth = birth;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> calcAge(<span class="number">2019</span>); <span class="comment">// 调用private方法</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// private方法:</span></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">calcAge</span><span class="params">(<span class="type">int</span> currentYear)</span> {</span><br><span class="line"> <span class="keyword">return</span> currentYear - <span class="built_in">this</span>.birth;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></p><blockquote><p>观察上述代码,<code>calcAge()</code>是一个<code>private</code>方法,外部代码无法调用,但是,内部方法<code>getAge()</code>可以调用它。此外,我们还注意到,这个<code>Person</code>类只定义了<code>birth</code>字段,没有定义<code>age</code>字段,获取<code>age</code>时,通过方法<code>getAge()</code>返回的是一个实时计算的值,并非存储在某个字段的值。这说明方法可以封装一个类的对外接口,调用方不需要知道也不关心<code>Person</code>实例在内部到底有没有<code>age</code>字段。</p></blockquote><h3 id="可变参数(E)"><a href="#可变参数(E)" class="headerlink" title="可变参数(E)"></a>可变参数(E)</h3><p>可变参数用类型…定义,可变参数相当于数组类型:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span>{</span><br><span class="line"> <span class="keyword">private</span> String[] names;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setNames</span><span class="params">(String... names)</span> {</span><br><span class="line"> <span class="built_in">this</span>.names = names;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>上面的<code>setNames()</code>就定义了一个可变参数。调用时,可以这么写:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Animal</span> <span class="variable">g</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Animal</span>();</span><br><span class="line">g.setNames(<span class="string">"cat"</span>, <span class="string">"dog"</span>, <span class="string">"cow"</span>); <span class="comment">// 传入3个String</span></span><br><span class="line">g.setNames(<span class="string">"cat"</span>, <span class="string">"dog"</span>); <span class="comment">// 传入2个String</span></span><br><span class="line">g.setNames(<span class="string">"cat"</span>); <span class="comment">// 传入1个String</span></span><br><span class="line">g.setNames(); <span class="comment">// 传入0个String</span></span><br></pre></td></tr></table></figure><br>完全可以把可变参数改写为<code>String[]</code>类型:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Animal</span> {</span><br><span class="line"> <span class="keyword">private</span> String[] names;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setNames</span><span class="params">(String[] names)</span> {</span><br><span class="line"> <span class="built_in">this</span>.names = names;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>但是,调用方需要自己先构造<code>String[]</code>,比较麻烦。例如:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Animal</span> <span class="variable">g</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Animal</span>();</span><br><span class="line">g.setNames(<span class="keyword">new</span> <span class="title class_">String</span>[] {<span class="string">"cat"</span>, <span class="string">"dog"</span>, <span class="string">"cow"</span>}); <span class="comment">// 传入1个String[]</span></span><br></pre></td></tr></table></figure><p>另一个问题是,调用方可以传入<code>null</code>:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Animal</span> <span class="variable">g</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Animal</span>();</span><br><span class="line">g.setNames(<span class="literal">null</span>);</span><br></pre></td></tr></table></figure><br>而可变参数可以保证无法传入null,因为传入0个参数时,接收到的实际值是一个空数组而不是null。</p><p>可变参数感觉跟重载异曲同工嘛,都由参数形式决定方法的结果。</p><h3 id="参数绑定(E)"><a href="#参数绑定(E)" class="headerlink" title="参数绑定(E)"></a>参数绑定(E)</h3><blockquote><p>调用方把参数传递给实例方法时,调用时传递的值会按参数位置一一绑定。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//基本类型参数绑定</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Person</span> <span class="variable">p</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> <span class="number">15</span>; <span class="comment">// n的值为15</span></span><br><span class="line"> p.setAge(n); <span class="comment">// 传入n的值</span></span><br><span class="line"> System.out.println(p.getAge()); <span class="comment">// 15</span></span><br><span class="line"> n = <span class="number">20</span>; <span class="comment">// n的值改为20</span></span><br><span class="line"> System.out.println(p.getAge()); <span class="comment">// 15还是20?</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getAge</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setAge</span><span class="params">(<span class="type">int</span> age)</span> {</span><br><span class="line"> <span class="built_in">this</span>.age = age;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>此时<code>n</code>和<code>p.age</code>两者互不影响,<code>setAge()</code>方法复制了<code>n</code>的值</p></blockquote><p>重点:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//引用类型参数绑定</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Main</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Person</span> <span class="variable">p</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Person</span>();</span><br><span class="line"> String[] fullname = <span class="keyword">new</span> <span class="title class_">String</span>[] { <span class="string">"Homer"</span>, <span class="string">"Simpson"</span> };</span><br><span class="line"> p.setName(fullname); <span class="comment">// 传入fullname数组</span></span><br><span class="line"> System.out.println(p.getName()); <span class="comment">// "Homer Simpson"</span></span><br><span class="line"> fullname[<span class="number">0</span>] = <span class="string">"Bart"</span>; <span class="comment">// fullname数组的第一个元素修改为"Bart"</span></span><br><span class="line"> System.out.println(p.getName()); <span class="comment">// "Homer Simpson"还是"Bart Simpson"?</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="keyword">private</span> String[] name;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> String <span class="title function_">getName</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">this</span>.name[<span class="number">0</span>] + <span class="string">" "</span> + <span class="built_in">this</span>.name[<span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setName</span><span class="params">(String[] name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br><code>fullname</code>和<code>name</code>同步更改,原因是<code>new String[]{}</code><strong>创建了一个对象</strong>,而<code>fullname</code>和<code>p.name</code><strong>指向了同一个对象</strong>。关键是创建了一个对象</p><h2 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h2><p> 构造方法初始化实例,<strong>构造方法名就是类名</strong>,<strong>无返回值</strong>。<br> 调用该方法,必须用<code>new</code>操作符</p><p> 如果既对字段进行初始化,又在构造方法中对字段进行初始化,会发生什么?</p><p> 在Java中,创建对象实例的时候,按照如下顺序进行初始化:</p><ul><li><p>先初始化字段:int age = 10;表示字段初始化为10,double salary;表示字段默认初始化为0,String name;表示引用类型字段默认初始化为null;</p></li><li><p>执行构造方法的代码进行初始化。</p></li></ul><p>因此,构造方法的代码是后运行,将覆盖一开始字段初始化。</p><p>多构造方法,用法类似方法重载。<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Person</span> {</span><br><span class="line"> <span class="keyword">private</span> String name;</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> age;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="built_in">this</span>.age = age;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">(String name)</span> {</span><br><span class="line"> <span class="built_in">this</span>.name = name;</span><br><span class="line"> <span class="built_in">this</span>.age = <span class="number">12</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Person</span><span class="params">()</span> {</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><hr><h2 id="方法重载-OverLoad-:"><a href="#方法重载-OverLoad-:" class="headerlink" title="方法重载(OverLoad):"></a>方法重载(<code>OverLoad</code>):</h2><p><strong>同一个类</strong>里定义几个<strong>方法名相同</strong>,功能相似但<strong>参数不同</strong>(参数个数、参数类型不同)的<strong>方法</strong></p><p>换而言之,以参数内容决定方法内容,省去多个函数名</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Hello</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">()</span> {</span><br><span class="line"> System.out.println(<span class="string">"Hello, world!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">(String name)</span> {</span><br><span class="line"> System.out.println(<span class="string">"Hello, "</span> + name + <span class="string">"!"</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">hello</span><span class="params">(String name, <span class="type">int</span> age)</span> {</span><br><span class="line"> <span class="keyword">if</span> (age < <span class="number">18</span>) {</span><br><span class="line"> System.out.println(<span class="string">"Hi, "</span> + name + <span class="string">"!"</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> System.out.println(<span class="string">"Hello, "</span> + name + <span class="string">"!"</span>);</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><br><span class="line"></span><br><span class="line"><span class="comment">//当前对象是p1;</span></span><br><span class="line">p1.distance(p2);</span><br><span class="line"><span class="comment">//static?</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">double</span> <span class="title function_">distance</span><span class="params">(Point c,Point d)</span>{}<span class="comment">//类是一种数据类型,static静态函数可省略调用对象</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><hr><h2 id="其他知识点"><a href="#其他知识点" class="headerlink" title="其他知识点"></a>其他知识点</h2><p><code>this</code>的用法、对象运算符、匿名对象(类)、包和修饰符</p><h3 id="this-用法"><a href="#this-用法" class="headerlink" title="this 用法"></a>this 用法</h3><ul><li>调用类内的成员变量<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> num;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setNum</span><span class="params">(<span class="type">int</span> num)</span> {</span><br><span class="line"> <span class="built_in">this</span>.num = num;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>调用本类中的其他方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> num;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(MyClass mc)</span> {</span><br><span class="line"> System.out.println(mc.num);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p>调用本类中的其他构造方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyClass</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> num;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MyClass</span><span class="params">()</span> {</span><br><span class="line"> <span class="built_in">this</span>(<span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MyClass</span><span class="params">(<span class="type">int</span> num)</span> {</span><br><span class="line"> <span class="built_in">this</span>.num = num;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li></ul><h3 id="对象运算符-instanceof"><a href="#对象运算符-instanceof" class="headerlink" title="对象运算符(instanceof)"></a>对象运算符(instanceof)</h3><p>该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。</p><p><code>instanceof</code>运算符使用格式如下:</p><p>( Object reference variable ) instanceof (class/interface type)<br>如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个对象,那么结果为真。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">String</span> <span class="variable">name</span> <span class="operator">=</span> <span class="string">"James"</span>;</span><br><span class="line"><span class="type">boolean</span> <span class="variable">result</span> <span class="operator">=</span> name <span class="keyword">instanceof</span> String; <span class="comment">// 由于 name 是 String 类型,所以返回真</span></span><br></pre></td></tr></table></figure><p>如果被比较的对象兼容于右侧类型,该运算符仍然返回 true。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Vehicle</span> {}</span><br><span class="line"> </span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Car</span> <span class="keyword">extends</span> <span class="title class_">Vehicle</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>{</span><br><span class="line"> <span class="type">Vehicle</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Car</span>();</span><br><span class="line"> <span class="type">boolean</span> <span class="variable">result</span> <span class="operator">=</span> a <span class="keyword">instanceof</span> Car;</span><br><span class="line"> System.out.println( result);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>以上实例编译运行结果为:<code>true</code></p><h3 id="匿名对象"><a href="#匿名对象" class="headerlink" title="匿名对象"></a>匿名对象</h3><p>普通的类对象在使用时会<strong>定义一个类类型的变量</strong>,用来保存new出来的类所在的地址。而匿名类取<strong>消掉了这个变量</strong>,这个地址由编译器来处理,并且在new出来之后,它占用的内存会有<strong>JVM自动回收掉</strong>。后续无法再使用了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Student</span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">classBegin</span><span class="params">()</span>{</span><br><span class="line"> System.out.println(<span class="string">"豆zza来喽ヘ(~ω~ヘ)"</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Student</span>().classBegin();</span><br></pre></td></tr></table></figure><p>匿名对象最常用的方式是<strong>作为函数的参数</strong>,上述的打印语句 “豆zza来喽ヘ(~ω~ヘ)” 是一个匿名对象,由于字符串是以对象的形式存储的,所以这里实际上就是一个<strong>没有使用对象引用的匿名对象</strong>。</p><h3 id="匿名类"><a href="#匿名类" class="headerlink" title="匿名类"></a>匿名类</h3><p>如果一个内部类在整个操作中<strong>只使用一次</strong>,就可以定义为匿名内部类。匿名内部类也就是没有名字的内部类,这是java为了方便我们编写程序而设计的一个机制.<strong>只创建一个它的对象</strong>,以后再不会用到这个类,使用匿名内部类就比较合适。</p><p>匿名内部类伴随着接口一起使用:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">interface</span> <span class="title class_">USB</span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">open</span><span class="params">()</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Demo</span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>{</span><br><span class="line"> <span class="type">USB</span> <span class="variable">usb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">USB</span>(){</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">open</span><span class="params">()</span>{}</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span>{}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> usb.open();</span><br><span class="line"> usb.close();</span><br><span class="line"></span><br><span class="line"> <span class="comment">//使用匿名内部类的匿名对象的方式</span></span><br><span class="line"> <span class="type">USB</span> <span class="variable">usb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">USB</span>(){</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">open</span><span class="params">()</span>{}</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">close</span><span class="params">()</span>{}</span><br><span class="line"> }.open();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><br>在Demo这个类的main方法中创建了一个局部的内部类,这个内部类没有名字,也就是创建了一个匿名内部类。</p><p>匿名对象(类)中文本均来自 *<a href="https://cloud.tencent.com/developer/article/1444362">Java 学习笔记(8)——匿名对象与内部类</a></p><h3 id="包-package"><a href="#包-package" class="headerlink" title="包(package)"></a>包(package)</h3><ul><li>将功能相近的类放在同一个包里</li><li>某些访问是以包为单位</li><li>由于不同包里可能有相同的类名,一定程度上可以避免命名冲突</li><li>package语句必须是文件中第一条语句</li><li><strong>WARNING</strong> :包没有父子关系。java.util和java.util.zip是不同的包,两者没有任何继承关系。</li></ul><h3 id="private、public、protected、无修饰"><a href="#private、public、protected、无修饰" class="headerlink" title="private、public、protected、无修饰"></a>private、public、protected、无修饰</h3><div class="table-container"><table><thead><tr><th style="text-align:left">访问级别</th><th style="text-align:left">访问控制修饰符</th><th style="text-align:left">同类</th><th style="text-align:left">同包</th><th style="text-align:left">子类</th><th style="text-align:left">不同包</th></tr></thead><tbody><tr><td style="text-align:left">公开</td><td style="text-align:left">public</td><td style="text-align:left">✔</td><td style="text-align:left">✔</td><td style="text-align:left">✔</td><td style="text-align:left">✔</td></tr><tr><td style="text-align:left">受保护</td><td style="text-align:left">protected</td><td style="text-align:left">✔</td><td style="text-align:left">✔</td><td style="text-align:left">✔</td><td style="text-align:left">×</td></tr><tr><td style="text-align:left">默认</td><td style="text-align:left">无修饰符</td><td style="text-align:left">✔</td><td style="text-align:left">✔</td><td style="text-align:left">×</td><td style="text-align:left">×</td></tr><tr><td style="text-align:left">私有</td><td style="text-align:left">private</td><td style="text-align:left">✔</td><td style="text-align:left">×</td><td style="text-align:left">×</td><td style="text-align:left">×</td></tr></tbody></table></div><hr><p>主要参考:</p><ul><li>java上课内容</li><li><a href="https://www.liaoxuefeng.com/wiki/1252599548343744">廖雪峰java课堂</a></li><li><a href="https://cloud.tencent.com/developer/article/1444362">Java 学习笔记(8)——匿名对象与内部类</a></li></ul>]]></content>
<summary type="html"><h1 id="【JAVA学习笔记1】-面向对象基础"><a href="#【JAVA学习笔记1】-面向对象基础" class="headerlink" title="【JAVA学习笔记1】 面向对象基础"></a>【JAVA学习笔记1】 面向对象基础</h1><p>JAVA知识</summary>
</entry>
</feed>