-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathatom.xml
More file actions
559 lines (267 loc) · 499 KB
/
atom.xml
File metadata and controls
559 lines (267 loc) · 499 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Hqystar's Blog</title>
<link href="https://huanqgingyu.top/atom.xml" rel="self"/>
<link href="https://huanqgingyu.top/"/>
<updated>2022-08-28T15:28:45.740Z</updated>
<id>https://huanqgingyu.top/</id>
<author>
<name>Qyun</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>手写数字识别(Android版)Android端</title>
<link href="https://huanqgingyu.top/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89Android%E7%AB%AF/"/>
<id>https://huanqgingyu.top/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89Android%E7%AB%AF/</id>
<published>2022-03-14T16:00:00.000Z</published>
<updated>2022-08-28T15:28:45.740Z</updated>
<content type="html"><![CDATA[<h2 id="手写数字识别(Android版)Android端"><a href="#手写数字识别(Android版)Android端" class="headerlink" title="手写数字识别(Android版)Android端"></a>手写数字识别(Android版)Android端</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>Android端实现的需求:</p><ul><li>调用手机相机拍摄手写好的数字图片</li><li>从手机相册中选取手写好的数字图片</li><li>将图片上传到服务器</li><li>接收服务器返回的结果并显示</li></ul><p>这里Android和服务器通信用的是okhttp3协议</p><h3 id="问题"><a href="#问题" class="headerlink" title="问题"></a>问题</h3><h4 id="okhttp3:"><a href="#okhttp3:" class="headerlink" title="okhttp3:"></a>okhttp3:</h4><p>导入:可以<a href="https://blog.csdn.net/m0_37622302/article/details/107249262">参考文章</a></p><p>若遇到异常:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">No Network Security Config specified, using platform default</span><br></pre></td></tr></table></figure><p>解决方法:<a href="https://blog.csdn.net/weixin_35691921/article/details/106520749">参考文章</a></p><h4 id="手机存储:"><a href="#手机存储:" class="headerlink" title="手机存储:"></a>手机存储:</h4><p>若遇到异常:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0</span><br></pre></td></tr></table></figure><p>在 res/xml/ 下新建一个 file_paths.xml 文件,然后往里写入:</p><figure class="highlight xml"><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="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">paths</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">external-path</span> <span class="attr">name</span>=<span class="string">"my_images"</span> <span class="attr">path</span>=<span class="string">"."</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">paths</span>></span></span><br></pre></td></tr></table></figure><p>然后在 AndroidManifest.xml 中添加:</p><figure class="highlight xml"><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="tag"><<span class="name">provider</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:name</span>=<span class="string">"androidx.core.content.FileProvider"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:authorities</span>=<span class="string">"com.example.identification.fileprovider"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:exported</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:grantUriPermissions</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta-data</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:name</span>=<span class="string">"android.support.FILE_PROVIDER_PATHS"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:resource</span>=<span class="string">"@xml/file_paths"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">provider</span>></span></span><br></pre></td></tr></table></figure><h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><p>就一个java文件,也是主活动,相册和相机都是常用的多媒体功能。</p><p>这里要注意的就是常见的子线程不能更新UI,UI要到主线程更新。</p><h4 id="活动:"><a href="#活动:" class="headerlink" title="活动:"></a>活动:</h4><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><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><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> androidx.appcompat.app.AppCompatActivity;</span><br><span class="line"><span class="keyword">import</span> androidx.core.app.ActivityCompat;</span><br><span class="line"><span class="keyword">import</span> androidx.core.content.ContextCompat;</span><br><span class="line"><span class="keyword">import</span> androidx.core.content.FileProvider;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> android.Manifest;</span><br><span class="line"><span class="keyword">import</span> android.annotation.SuppressLint;</span><br><span class="line"><span class="keyword">import</span> android.annotation.TargetApi;</span><br><span class="line"><span class="keyword">import</span> android.content.ContentUris;</span><br><span class="line"><span class="keyword">import</span> android.content.Context;</span><br><span class="line"><span class="keyword">import</span> android.content.Intent;</span><br><span class="line"><span class="keyword">import</span> android.content.pm.PackageManager;</span><br><span class="line"><span class="keyword">import</span> android.database.Cursor;</span><br><span class="line"><span class="keyword">import</span> android.graphics.Bitmap;</span><br><span class="line"><span class="keyword">import</span> android.graphics.BitmapFactory;</span><br><span class="line"><span class="keyword">import</span> android.net.Uri;</span><br><span class="line"><span class="keyword">import</span> android.os.Build;</span><br><span class="line"><span class="keyword">import</span> android.os.Bundle;</span><br><span class="line"><span class="keyword">import</span> android.os.Environment;</span><br><span class="line"><span class="keyword">import</span> android.os.Handler;</span><br><span class="line"><span class="keyword">import</span> android.os.Message;</span><br><span class="line"><span class="keyword">import</span> android.provider.DocumentsContract;</span><br><span class="line"><span class="keyword">import</span> android.provider.MediaStore;</span><br><span class="line"><span class="keyword">import</span> android.util.Log;</span><br><span class="line"><span class="keyword">import</span> android.view.View;</span><br><span class="line"><span class="keyword">import</span> android.widget.Button;</span><br><span class="line"><span class="keyword">import</span> android.widget.ImageView;</span><br><span class="line"><span class="keyword">import</span> android.widget.TextView;</span><br><span class="line"><span class="keyword">import</span> android.widget.Toast;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.FileOutputStream;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> okhttp3.Call;</span><br><span class="line"><span class="keyword">import</span> okhttp3.Callback;</span><br><span class="line"><span class="keyword">import</span> okhttp3.MediaType;</span><br><span class="line"><span class="keyword">import</span> okhttp3.MultipartBody;</span><br><span class="line"><span class="keyword">import</span> okhttp3.OkHttpClient;</span><br><span class="line"><span class="keyword">import</span> okhttp3.Request;</span><br><span class="line"><span class="keyword">import</span> okhttp3.RequestBody;</span><br><span class="line"><span class="keyword">import</span> okhttp3.Response;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MainActivity</span> <span class="keyword">extends</span> <span class="title">AppCompatActivity</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TAKE_PHOTO = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CHOOSE_PHOTO = <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 没连接上服务器 */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> NON_INFORMATION = -<span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 返回结果 */</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ZERO_INFORMATION = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> ONE_INFORMATION = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TWO_INFORMATION = <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> THREE_INFORMATION = <span class="number">3</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FOUR_INFORMATION = <span class="number">4</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> FIVE_INFORMATION = <span class="number">5</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SIX_INFORMATION = <span class="number">6</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SEVEN_INFORMATION = <span class="number">7</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> EIGHT_INFORMATION = <span class="number">8</span>;</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> NINE_INFORMATION = <span class="number">9</span>;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 定义控件 */</span></span><br><span class="line"> <span class="keyword">private</span> Button takePhoto;</span><br><span class="line"> <span class="keyword">private</span> Button chooseFromAlbum;</span><br><span class="line"> <span class="keyword">private</span> Button sendImageToServer;</span><br><span class="line"> <span class="keyword">private</span> TextView myResult;</span><br><span class="line"> <span class="keyword">private</span> ImageView picture;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> Uri imageUri;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> String myImagePath = <span class="string">"null"</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> MediaType MEDIA_TYPE_JPG = MediaType.parse(<span class="string">"image/jpg"</span>);</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">final</span> OkHttpClient client = <span class="keyword">new</span> OkHttpClient();</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onCreate</span><span class="params">(Bundle savedInstanceState)</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onCreate(savedInstanceState);</span><br><span class="line"> setContentView(R.layout.activity_main);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建控件对象</span></span><br><span class="line"> takePhoto = (Button) findViewById(R.id.take_photo);</span><br><span class="line"> chooseFromAlbum = (Button) findViewById(R.id.choose_from_album);</span><br><span class="line"> sendImageToServer = (Button) findViewById(R.id.send_image);</span><br><span class="line"> myResult = (TextView) findViewById(R.id.get_result);</span><br><span class="line"> picture = (ImageView) findViewById(R.id.picture);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 发送图片到服务器</span></span><br><span class="line"> sendImageToServer.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!myImagePath.equals(<span class="string">"null"</span>)) {</span><br><span class="line"> File f = <span class="keyword">new</span> File(myImagePath);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> runUpload(f);</span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"没有输入图片的路径"</span>, Toast.LENGTH_SHORT).show();</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"> takePhoto.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建File对象,用于存储拍照后的图片</span></span><br><span class="line"> File outputImage = <span class="keyword">new</span> File(getExternalCacheDir(), <span class="string">"output_image.jpg"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">if</span> (outputImage.exists()) {</span><br><span class="line"> outputImage.delete();</span><br><span class="line"> }</span><br><span class="line"> outputImage.createNewFile();</span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 判断系统版本并选择相应的方法,小于24表明版本低于7.0</span></span><br><span class="line"> <span class="keyword">if</span> (Build.VERSION.SDK_INT < <span class="number">24</span>) {</span><br><span class="line"> imageUri = Uri.fromFile(outputImage);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> imageUri = FileProvider.getUriForFile(MainActivity.<span class="keyword">this</span>, <span class="string">"com.example.coalgangueidentification.fileprovider"</span>, outputImage);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 启动相机程序</span></span><br><span class="line"> Intent intent = <span class="keyword">new</span> Intent(<span class="string">"android.media.action.IMAGE_CAPTURE"</span>);</span><br><span class="line"> intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 开启当前活动</span></span><br><span class="line"> startActivityForResult(intent, TAKE_PHOTO);</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"> chooseFromAlbum.setOnClickListener(<span class="keyword">new</span> View.OnClickListener() {</span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onClick</span><span class="params">(View v)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (ContextCompat.checkSelfPermission(MainActivity.<span class="keyword">this</span>, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {</span><br><span class="line"> ActivityCompat.requestPermissions(MainActivity.<span class="keyword">this</span>, <span class="keyword">new</span> String[]{ Manifest.permission. WRITE_EXTERNAL_STORAGE }, <span class="number">1</span>);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> openAlbum();</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">/* 定义UI线程 */</span></span><br><span class="line"> <span class="keyword">private</span> Handler handler = <span class="keyword">new</span> Handler() {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">handleMessage</span><span class="params">(Message msg)</span> </span>{</span><br><span class="line"> <span class="keyword">switch</span> (msg.what) {</span><br><span class="line"> <span class="keyword">case</span> NON_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"暂时没有识别结果!"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ZERO_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:0"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> ONE_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:1"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> TWO_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:2"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> THREE_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:3"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> FOUR_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:4"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> FIVE_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:5"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> SIX_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:6"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> SEVEN_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:7"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> EIGHT_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:8"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> NINE_INFORMATION:</span><br><span class="line"> myResult.setText(<span class="string">"识别结果为:9"</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 打开手机相册 */</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">openAlbum</span><span class="params">()</span> </span>{</span><br><span class="line"> Intent intent = <span class="keyword">new</span> Intent(<span class="string">"android.intent.action.GET_CONTENT"</span>);</span><br><span class="line"> intent.setType(<span class="string">"image/*"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 打开相册</span></span><br><span class="line"> startActivityForResult(intent, CHOOSE_PHOTO);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onRequestPermissionsResult</span><span class="params">(<span class="keyword">int</span> requestCode, String[] permissions, <span class="keyword">int</span>[] grantResults)</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onRequestPermissionsResult(requestCode, permissions, grantResults);</span><br><span class="line"> <span class="keyword">switch</span> (requestCode) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> <span class="keyword">if</span> (grantResults.length > <span class="number">0</span> && grantResults[<span class="number">0</span>] == PackageManager.PERMISSION_GRANTED) {</span><br><span class="line"> openAlbum();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> Toast.makeText(<span class="keyword">this</span>, <span class="string">"你拒绝了打开相册请求!"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">onActivityResult</span><span class="params">(<span class="keyword">int</span> requestCode, <span class="keyword">int</span> resultCode, Intent data)</span> </span>{</span><br><span class="line"> <span class="keyword">super</span>.onActivityResult(requestCode, resultCode, data);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (requestCode) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 拍照获取图片</span></span><br><span class="line"> <span class="keyword">case</span> TAKE_PHOTO:</span><br><span class="line"> <span class="keyword">if</span> (resultCode == RESULT_OK) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将拍摄的照片显示出来</span></span><br><span class="line"> Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));</span><br><span class="line"> picture.setImageBitmap(bitmap);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将拍摄到的图片保存到相册当中</span></span><br><span class="line"> String saveToMyAlbum = saveImageToGallery(<span class="keyword">this</span>, bitmap);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 若照片保存成功,则把照片路径赋值到要传输照片的路径,否则提示</span></span><br><span class="line"> <span class="keyword">if</span> (!saveToMyAlbum.equals(<span class="string">"false"</span>)){</span><br><span class="line"> myImagePath = saveToMyAlbum;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span>{</span><br><span class="line"> Toast.makeText(<span class="keyword">this</span>, <span class="string">"图片没有保存成功"</span>, Toast.LENGTH_LONG).show();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 从相册选择照片获取图片</span></span><br><span class="line"> <span class="keyword">case</span> CHOOSE_PHOTO:</span><br><span class="line"> <span class="keyword">if</span> (resultCode == RESULT_OK) {</span><br><span class="line"> <span class="comment">// 判断手机系统版本号</span></span><br><span class="line"> <span class="keyword">if</span> (Build.VERSION.SDK_INT >= <span class="number">19</span>) {</span><br><span class="line"> <span class="comment">// 4.4及以上系统使用这个方法处理图片</span></span><br><span class="line"> handleImageOnKitKat(data);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 4.4以下系统使用这个方法处理图片</span></span><br><span class="line"> handleImageBeforeKitKat(data);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@TargetApi(19)</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleImageOnKitKat</span><span class="params">(Intent data)</span> </span>{</span><br><span class="line"> String imagePath = <span class="keyword">null</span>;</span><br><span class="line"> Uri uri = data.getData();</span><br><span class="line"> Log.d(<span class="string">"TAG"</span>, <span class="string">"handleImageOnKitKat: uri is "</span> + uri);</span><br><span class="line"> <span class="keyword">if</span> (DocumentsContract.isDocumentUri(<span class="keyword">this</span>, uri)) {</span><br><span class="line"> <span class="comment">// 如果是document类型的Uri,则通过document id处理</span></span><br><span class="line"> String docId = DocumentsContract.getDocumentId(uri);</span><br><span class="line"> <span class="keyword">if</span>(<span class="string">"com.android.providers.media.documents"</span>.equals(uri.getAuthority())) {</span><br><span class="line"> String id = docId.split(<span class="string">":"</span>)[<span class="number">1</span>]; <span class="comment">// 解析出数字格式的id</span></span><br><span class="line"> String selection = MediaStore.Images.Media._ID + <span class="string">"="</span> + id;</span><br><span class="line"> imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"com.android.providers.downloads.documents"</span>.equals(uri.getAuthority())) {</span><br><span class="line"> Uri contentUri = ContentUris.withAppendedId(Uri.parse(<span class="string">"content://downloads/public_downloads"</span>), Long.valueOf(docId));</span><br><span class="line"> imagePath = getImagePath(contentUri, <span class="keyword">null</span>);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"content"</span>.equalsIgnoreCase(uri.getScheme())) {</span><br><span class="line"> <span class="comment">// 如果是content类型的Uri,则使用普通方式处理</span></span><br><span class="line"> imagePath = getImagePath(uri, <span class="keyword">null</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="string">"file"</span>.equalsIgnoreCase(uri.getScheme())) {</span><br><span class="line"> <span class="comment">// 如果是file类型的Uri,直接获取图片路径即可</span></span><br><span class="line"> imagePath = uri.getPath();</span><br><span class="line"> }</span><br><span class="line"> displayImage(imagePath); <span class="comment">// 根据图片路径显示图片</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">handleImageBeforeKitKat</span><span class="params">(Intent data)</span> </span>{</span><br><span class="line"> Uri uri = data.getData();</span><br><span class="line"> String imagePath = getImagePath(uri, <span class="keyword">null</span>);</span><br><span class="line"> displayImage(imagePath);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@SuppressLint("Range")</span></span><br><span class="line"> <span class="function"><span class="keyword">private</span> String <span class="title">getImagePath</span><span class="params">(Uri uri, String selection)</span> </span>{</span><br><span class="line"> String path = <span class="keyword">null</span>;</span><br><span class="line"> <span class="comment">// 通过Uri和selection来获取真实的图片路径</span></span><br><span class="line"> Cursor cursor = getContentResolver().query(uri, <span class="keyword">null</span>, selection, <span class="keyword">null</span>, <span class="keyword">null</span>);</span><br><span class="line"> <span class="keyword">if</span> (cursor != <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">if</span> (cursor.moveToFirst()) {</span><br><span class="line"> path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));</span><br><span class="line"> }</span><br><span class="line"> cursor.close();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> path;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">displayImage</span><span class="params">(String imagePath)</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (imagePath != <span class="keyword">null</span>) {</span><br><span class="line"> Bitmap bitmap = BitmapFactory.decodeFile(imagePath);</span><br><span class="line"> picture.setImageBitmap(bitmap);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 赋值给要传送到服务器的图片路径</span></span><br><span class="line"> myImagePath = imagePath;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> Toast.makeText(<span class="keyword">this</span>, <span class="string">"图片获取失败!"</span>, Toast.LENGTH_SHORT).show();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 保存文件到指定路径 */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> String <span class="title">saveImageToGallery</span><span class="params">(Context context, Bitmap bmp)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将图片保存</span></span><br><span class="line"> String storePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + <span class="string">"dearxy"</span>;</span><br><span class="line"> File appDir = <span class="keyword">new</span> File(storePath);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!appDir.exists()) {</span><br><span class="line"> appDir.mkdir();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> String fileName = System.currentTimeMillis() + <span class="string">".jpg"</span>;</span><br><span class="line"></span><br><span class="line"> File file = <span class="keyword">new</span> File(appDir, fileName);</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"></span><br><span class="line"> FileOutputStream fos = <span class="keyword">new</span> FileOutputStream(file);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通过io流的方式来压缩保存图片</span></span><br><span class="line"> <span class="keyword">boolean</span> isSuccess = bmp.compress(Bitmap.CompressFormat.JPEG, <span class="number">60</span>, fos);</span><br><span class="line"> fos.flush();</span><br><span class="line"> fos.close();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 保存图片后发送广播通知更新数据库</span></span><br><span class="line"> Uri uri = Uri.fromFile(file);</span><br><span class="line"> context.sendBroadcast(<span class="keyword">new</span> Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri));</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果保存成功则返回保存路径,否则返回错误码</span></span><br><span class="line"> <span class="keyword">if</span> (isSuccess) {</span><br><span class="line"> <span class="keyword">return</span> (storePath + <span class="string">"/"</span> + fileName);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"false"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">catch</span> (IOException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="string">"false"</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/* 运行上传线程 */</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">runUpload</span><span class="params">(File f)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"> <span class="keyword">final</span> File file = f;</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 子线程需要做的工作</span></span><br><span class="line"> RequestBody requestBody = <span class="keyword">new</span> MultipartBody.Builder()</span><br><span class="line"> .setType(MultipartBody.FORM)</span><br><span class="line"> .addFormDataPart(<span class="string">"file"</span>,<span class="string">"image.jpg"</span>,</span><br><span class="line"> RequestBody.create(MEDIA_TYPE_JPG, file))</span><br><span class="line"> .build();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置为自己的ip地址</span></span><br><span class="line"> Request request = <span class="keyword">new</span> Request.Builder()</span><br><span class="line"> .url(getString(R.string.server_ip))</span><br><span class="line"> .post(requestBody)</span><br><span class="line"> .build();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取请求回应对象</span></span><br><span class="line"> Call call = client.newCall(request);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 对回应进行的处理</span></span><br><span class="line"> call.enqueue(<span class="keyword">new</span> Callback() {</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="comment">// 当无法连接上服务器时,发出提示信号</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onFailure</span><span class="params">(Call call, IOException e)</span> </span>{</span><br><span class="line"> runOnUiThread(() -> {</span><br><span class="line"> sendMessage(NON_INFORMATION);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="meta">@Override</span></span><br><span class="line"> <span class="comment">// 当连接上服务器时,对请求的回复进行响应</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">onResponse</span><span class="params">(Call call, <span class="keyword">final</span> Response response)</span> <span class="keyword">throws</span> IOException </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获取回调的字符串信息</span></span><br><span class="line"> <span class="keyword">final</span> String res = response.body().string();</span><br><span class="line"></span><br><span class="line"> runOnUiThread(() -> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span>(res.charAt(<span class="number">0</span>)) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'0'</span>:</span><br><span class="line"> sendMessage(ZERO_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'1'</span>:</span><br><span class="line"> sendMessage(ONE_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'2'</span>:</span><br><span class="line"> sendMessage(TWO_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'3'</span>:</span><br><span class="line"> sendMessage(THREE_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'4'</span>:</span><br><span class="line"> sendMessage(FOUR_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'5'</span>:</span><br><span class="line"> sendMessage(FIVE_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'6'</span>:</span><br><span class="line"> sendMessage(SIX_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'7'</span>:</span><br><span class="line"> sendMessage(SEVEN_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'8'</span>:</span><br><span class="line"> sendMessage(EIGHT_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'9'</span>:</span><br><span class="line"> sendMessage(NINE_INFORMATION);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }).start();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 发送给UI线程显示信息</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">sendMessage</span><span class="params">(<span class="keyword">int</span> information)</span> </span>{</span><br><span class="line"></span><br><span class="line"> runOnUiThread(() -> {</span><br><span class="line"></span><br><span class="line"> Message msg = <span class="keyword">new</span> Message();</span><br><span class="line"> msg.what = information;</span><br><span class="line"> handler.sendMessage(msg);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (information == NON_INFORMATION) {</span><br><span class="line"> <span class="comment">// 提示连接服务器失败信号</span></span><br><span class="line"> Toast.makeText(MainActivity.<span class="keyword">this</span>, <span class="string">"服务器错误"</span>, Toast.LENGTH_SHORT).show();</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><h4 id="界面布局:"><a href="#界面布局:" class="headerlink" title="界面布局:"></a>界面布局:</h4><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">RelativeLayout</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xmlns:tools</span>=<span class="string">"http://schemas.android.com/tools"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:id</span>=<span class="string">"@+id/activity_main"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:paddingBottom</span>=<span class="string">"16dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:paddingLeft</span>=<span class="string">"16dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:paddingRight</span>=<span class="string">"16dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:paddingTop</span>=<span class="string">"16dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">tools:context</span>=<span class="string">"com.example.identification.MainActivity"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"vertical"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"0dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_weight</span>=<span class="string">"7"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"vertical"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"vertical"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">ImageView</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:id</span>=<span class="string">"@+id/picture"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"550dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_gravity</span>=<span class="string">"center_horizontal"</span> /></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"0dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_weight</span>=<span class="string">"3"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"vertical"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"horizontal"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">TextView</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:id</span>=<span class="string">"@+id/get_result"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:gravity</span>=<span class="string">"center"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textColor</span>=<span class="string">"@color/red"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textSize</span>=<span class="string">"30sp"</span></span></span><br><span class="line"><span class="tag"></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginTop</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginBottom</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginLeft</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginRight</span>=<span class="string">"5dp"</span>/></span></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"horizontal"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">Button</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:id</span>=<span class="string">"@+id/send_image"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textColor</span>=<span class="string">"@color/white"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:backgroundTint</span>=<span class="string">"@color/blue"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textSize</span>=<span class="string">"30sp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:text</span>=<span class="string">"开始识别"</span></span></span><br><span class="line"><span class="tag"></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginTop</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginBottom</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginLeft</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginRight</span>=<span class="string">"5dp"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">LinearLayout</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"match_parent"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:orientation</span>=<span class="string">"horizontal"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">Button</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:id</span>=<span class="string">"@+id/take_photo"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"0dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_weight</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textColor</span>=<span class="string">"@color/white"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:backgroundTint</span>=<span class="string">"@color/blue"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textSize</span>=<span class="string">"30sp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:text</span>=<span class="string">"拍照"</span></span></span><br><span class="line"><span class="tag"></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginTop</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginBottom</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginLeft</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginRight</span>=<span class="string">"5dp"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">Button</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:id</span>=<span class="string">"@+id/choose_from_album"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_width</span>=<span class="string">"0dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_height</span>=<span class="string">"wrap_content"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_weight</span>=<span class="string">"1"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textColor</span>=<span class="string">"@color/white"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:backgroundTint</span>=<span class="string">"@color/blue"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:textSize</span>=<span class="string">"30sp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:text</span>=<span class="string">"相册"</span></span></span><br><span class="line"><span class="tag"></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginTop</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginBottom</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginLeft</span>=<span class="string">"5dp"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:layout_marginRight</span>=<span class="string">"5dp"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">LinearLayout</span>></span></span><br><span class="line"><span class="tag"></<span class="name">RelativeLayout</span>></span></span><br></pre></td></tr></table></figure><p><strong>布局效果:</strong></p><p><img src= "/img/loading.gif" data-lazy-src="/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89Android%E7%AB%AF/img1.jpeg" alt></p><h4 id="AndroidManifest-xml文件"><a href="#AndroidManifest-xml文件" class="headerlink" title="AndroidManifest.xml文件"></a>AndroidManifest.xml文件</h4><figure class="highlight xml"><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></pre></td><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">manifest</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">xmlns:tools</span>=<span class="string">"http://schemas.android.com/tools"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">package</span>=<span class="string">"com.example.identification"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!--申请读写SD卡权限--></span></span><br><span class="line"> <span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.READ_EXTERNAL_STORAGE"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.WRITE_EXTERNAL_STORAGE"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.MOUNT_UNMOUNT_FILESYSTEMS"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">tools:ignore</span>=<span class="string">"ProtectedPermissions"</span> /></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!--申请网络权限--></span></span><br><span class="line"> <span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.INTERNET"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.ACCESS_WIFI_STATE"</span>/></span></span><br><span class="line"> <span class="tag"><<span class="name">uses-permission</span> <span class="attr">android:name</span>=<span class="string">"android.permission.ACCESS_NETWORK_STATE"</span>/></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">application</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:allowBackup</span>=<span class="string">"true"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:icon</span>=<span class="string">"@drawable/icon"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:label</span>=<span class="string">"@string/app_name"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:roundIcon</span>=<span class="string">"@mipmap/ic_launcher_round"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:networkSecurityConfig</span>=<span class="string">"@xml/network_security_config"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:supportsRtl</span>=<span class="string">"true"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:theme</span>=<span class="string">"@style/Theme.Identification"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:requestLegacyExternalStorage</span>=<span class="string">"true"</span>></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">activity</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:name</span>=<span class="string">".MainActivity"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:exported</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">intent-filter</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">android:name</span>=<span class="string">"android.intent.action.MAIN"</span> /></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">category</span> <span class="attr">android:name</span>=<span class="string">"android.intent.category.LAUNCHER"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">intent-filter</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">activity</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">provider</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:name</span>=<span class="string">"androidx.core.content.FileProvider"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:authorities</span>=<span class="string">"com.example.identification.fileprovider"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:exported</span>=<span class="string">"false"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:grantUriPermissions</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">meta-data</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:name</span>=<span class="string">"android.support.FILE_PROVIDER_PATHS"</span></span></span><br><span class="line"><span class="tag"> <span class="attr">android:resource</span>=<span class="string">"@xml/file_paths"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">provider</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"></<span class="name">application</span>></span></span><br><span class="line"></span><br><span class="line"><span class="tag"></<span class="name">manifest</span>></span></span><br></pre></td></tr></table></figure><h4 id="res-values-内的文件:"><a href="#res-values-内的文件:" class="headerlink" title="res/values/内的文件:"></a>res/values/内的文件:</h4><p><strong>colors.xml</strong></p><figure class="highlight xml"><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"><span class="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">resources</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"purple_200"</span>></span>#FFBB86FC<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"purple_500"</span>></span>#FF6200EE<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"purple_700"</span>></span>#FF3700B3<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"teal_200"</span>></span>#FF03DAC5<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"teal_700"</span>></span>#FF018786<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"black"</span>></span>#FF000000<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"white"</span>></span>#FFFFFFFF<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"blue"</span>></span>#6699ff<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">color</span> <span class="attr">name</span>=<span class="string">"red"</span>></span>#EE0000<span class="tag"></<span class="name">color</span>></span></span><br><span class="line"><span class="tag"></<span class="name">resources</span>></span></span><br></pre></td></tr></table></figure><p><strong>strings.xml</strong></p><figure class="highlight xml"><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="tag"><<span class="name">resources</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span> <span class="attr">name</span>=<span class="string">"app_name"</span>></span>数字识别<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">string</span> <span class="attr">name</span>=<span class="string">"server_ip"</span>></span>http://[服务器的IP]:[服务器的端口号]/upload<span class="tag"></<span class="name">string</span>></span></span><br><span class="line"><span class="tag"></<span class="name">resources</span>></span></span><br></pre></td></tr></table></figure><h4 id="res-xml-内的文件:"><a href="#res-xml-内的文件:" class="headerlink" title="res/xml/内的文件:"></a>res/xml/内的文件:</h4><p>这个文件夹和里面的文件都是自己新建的。</p><p><strong>file_paths.xml</strong></p><figure class="highlight xml"><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="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">paths</span> <span class="attr">xmlns:android</span>=<span class="string">"http://schemas.android.com/apk/res/android"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">external-path</span> <span class="attr">name</span>=<span class="string">"my_images"</span> <span class="attr">path</span>=<span class="string">"."</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">paths</span>></span></span><br></pre></td></tr></table></figure><p><strong>network_security_config.xml</strong></p><figure class="highlight xml"><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="meta"><?xml version="1.0" encoding="utf-8"?></span></span><br><span class="line"><span class="tag"><<span class="name">network-security-config</span>></span> //默认配置:允许明文通信</span><br><span class="line"> <span class="tag"><<span class="name">base-config</span> <span class="attr">cleartextTrafficPermitted</span>=<span class="string">"true"</span> /></span></span><br><span class="line"><span class="tag"></<span class="name">network-security-config</span>></span></span><br></pre></td></tr></table></figure><h3 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h3><p><img src= "/img/loading.gif" data-lazy-src="/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89Android%E7%AB%AF/img2.jpeg" alt></p><p><img src= "/img/loading.gif" data-lazy-src="/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89Android%E7%AB%AF/img3.jpeg" alt></p><p><img src= "/img/loading.gif" data-lazy-src="/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89Android%E7%AB%AF/img4.jpeg" alt></p>]]></content>
<summary type="html"><h2 id="手写数字识别(Android版)Android端"><a href="#手写数字识别(Android版)Android端" class="headerlink" title="手写数字识别(Android版)Android端"></a>手写数字识别(Android</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="手写数字识别" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="Android" scheme="https://huanqgingyu.top/tags/Android/"/>
</entry>
<entry>
<title>手写数字识别(Android版)服务端</title>
<link href="https://huanqgingyu.top/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89%E6%9C%8D%E5%8A%A1%E7%AB%AF/"/>
<id>https://huanqgingyu.top/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89%E6%9C%8D%E5%8A%A1%E7%AB%AF/</id>
<published>2022-03-14T16:00:00.000Z</published>
<updated>2022-08-28T15:30:22.565Z</updated>
<content type="html"><![CDATA[<h2 id="手写数字识别(Android版)服务端"><a href="#手写数字识别(Android版)服务端" class="headerlink" title="手写数字识别(Android版)服务端"></a>手写数字识别(Android版)服务端</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>服务端实现的需求:</p><ul><li>接收到手机发来的图片并存储到本地</li><li>识别本地存储的图片</li><li>将识别结果传回手机</li></ul><p>使用flask服务器和http协议</p><h3 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h3><p>训练和预测部分和之前的类似,可以参考<a href="https://huangqingyun.top/2021/03/31/手写数字识别(识别纸上的手写数字)/">之前写的文章</a></p><h4 id="网络"><a href="#网络" class="headerlink" title="网络"></a>网络</h4><p>识别网络可以自定义成识别效果更好的,文件名为:<strong>network.py</strong></p><figure class="highlight python"><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">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自定义手写数字识别网络</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">net</span>(<span class="params">nn.Module</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self</span>):</span></span><br><span class="line"> super(net, self).__init__()</span><br><span class="line"></span><br><span class="line"> self.Conn_layers = nn.Sequential(</span><br><span class="line"> nn.Linear(<span class="number">784</span>, <span class="number">100</span>),</span><br><span class="line"> nn.Sigmoid(),</span><br><span class="line"> nn.Linear(<span class="number">100</span>, <span class="number">10</span>),</span><br><span class="line"> nn.Sigmoid()</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">forward</span>(<span class="params">self, input</span>):</span></span><br><span class="line"> output = self.Conn_layers(input)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> output</span><br></pre></td></tr></table></figure><h4 id="训练"><a href="#训练" class="headerlink" title="训练"></a>训练</h4><p>和之前写的类似,超参数、损失函数和优化器可根据自己实际情况调整,文件名为:<strong>train.py</strong></p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.optim <span class="keyword">as</span> optim</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> datasets, transforms</span><br><span class="line"><span class="keyword">from</span> torch.autograd <span class="keyword">import</span> Variable</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"></span><br><span class="line"><span class="keyword">from</span> network <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载训练集</span></span><br><span class="line">train_dataset = datasets.MNIST(root=<span class="string">'./data/'</span>,</span><br><span class="line"> train=<span class="literal">True</span>,</span><br><span class="line"> transform=transforms.ToTensor(),</span><br><span class="line"> download=<span class="literal">False</span>)</span><br><span class="line"><span class="comment"># 下载测试集</span></span><br><span class="line">test_dataset = datasets.MNIST(root=<span class="string">'./data/'</span>,</span><br><span class="line"> train=<span class="literal">False</span>,</span><br><span class="line"> transform=transforms.ToTensor(),</span><br><span class="line"> download=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置批次数</span></span><br><span class="line">batch_size = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 装载训练集</span></span><br><span class="line">train_loader = torch.utils.data.DataLoader(dataset = train_dataset,</span><br><span class="line"> batch_size = batch_size,</span><br><span class="line"> shuffle=<span class="literal">True</span>)</span><br><span class="line"><span class="comment"># 装载测试集</span></span><br><span class="line">test_loader = torch.utils.data.DataLoader(dataset = test_dataset,</span><br><span class="line"> batch_size = batch_size,</span><br><span class="line"> shuffle = <span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义学习率</span></span><br><span class="line">LR = <span class="number">0.1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义一个网络对象</span></span><br><span class="line">net = net()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 损失函数使用交叉熵</span></span><br><span class="line">loss_function = nn.CrossEntropyLoss()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 优化函数使用 SGD</span></span><br><span class="line">optimizer = optim.SGD(</span><br><span class="line"> net.parameters(),</span><br><span class="line"> lr = LR,</span><br><span class="line"> momentum = <span class="number">0.9</span>,</span><br><span class="line"> weight_decay = <span class="number">0.0005</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义迭代次数</span></span><br><span class="line">epoch = <span class="number">20</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行迭代训练</span></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> range(epoch):</span><br><span class="line"> <span class="keyword">for</span> i, data <span class="keyword">in</span> enumerate(train_loader):</span><br><span class="line"> inputs, labels = data</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 转换下输入形状</span></span><br><span class="line"> inputs = inputs.reshape(batch_size, <span class="number">784</span>)</span><br><span class="line"></span><br><span class="line"> inputs, labels = Variable(inputs), Variable(labels)</span><br><span class="line"> outputs = net(inputs)</span><br><span class="line"> loss = loss_function(outputs, labels)</span><br><span class="line"> optimizer.zero_grad()</span><br><span class="line"> loss.backward()</span><br><span class="line"> optimizer.step()</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 初始化正确结果数为0</span></span><br><span class="line"> test_result = <span class="number">0</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 用测试数据进行测试</span></span><br><span class="line"> <span class="keyword">for</span> data_test <span class="keyword">in</span> test_loader:</span><br><span class="line"> images, labels = data_test</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 转换下输入形状</span></span><br><span class="line"> images = images.reshape(batch_size, <span class="number">784</span>)</span><br><span class="line"></span><br><span class="line"> images, labels = Variable(images), Variable(labels)</span><br><span class="line"> output_test = net(images)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 对一个批次的数据的准确性进行判断</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(len(labels)):</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 如果输出结果的最大值的索引与标签内正确数据相等,准确个数累加</span></span><br><span class="line"> <span class="keyword">if</span> torch.argmax(output_test[i]) == labels[i]:</span><br><span class="line"> test_result += <span class="number">1</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 打印每次迭代后正确的结果数</span></span><br><span class="line"> print(<span class="string">"Epoch {} : {} / {}"</span>.format(epoch, test_result, len(test_dataset)))</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 保存权重模型</span></span><br><span class="line">torch.save(net.state_dict(), <span class="string">'weight/test.pth'</span>)</span><br></pre></td></tr></table></figure><h4 id="图像预处理"><a href="#图像预处理" class="headerlink" title="图像预处理"></a>图像预处理</h4><p>文件名为:<strong>pretreatment.py</strong></p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">image_preprocessing</span>():</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取图片</span></span><br><span class="line">img = cv2.imread(<span class="string">"getImage/image.jpg"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># =====================图像处理======================== #</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 转换成灰度图像</span></span><br><span class="line">gray_img = cv2.cvtColor(img , cv2.COLOR_BGR2GRAY)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行高斯滤波</span></span><br><span class="line">gauss_img = cv2.GaussianBlur(gray_img, (<span class="number">5</span>,<span class="number">5</span>), <span class="number">0</span>, <span class="number">0</span>, cv2.BORDER_DEFAULT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 边缘检测</span></span><br><span class="line">img_edge1 = cv2.Canny(gauss_img, <span class="number">100</span>, <span class="number">200</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># =====================图像分割======================== #</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取原始图像的宽和高</span></span><br><span class="line">high = img.shape[<span class="number">0</span>]</span><br><span class="line">width = img.shape[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 分别初始化高和宽的和</span></span><br><span class="line">add_width = np.zeros(high, dtype = int)</span><br><span class="line">add_high = np.zeros(width, dtype = int)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算每一行的灰度图的值的和</span></span><br><span class="line"><span class="keyword">for</span> h <span class="keyword">in</span> range(high):</span><br><span class="line"><span class="keyword">for</span> w <span class="keyword">in</span> range(width):</span><br><span class="line">add_width[h] = add_width[h] + img_edge1[h][w]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算每一列的值的和</span></span><br><span class="line"><span class="keyword">for</span> w <span class="keyword">in</span> range(width):</span><br><span class="line"><span class="keyword">for</span> h <span class="keyword">in</span> range(high):</span><br><span class="line">add_high[w] = add_high[w] + img_edge1[h][w]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化上下边界为宽度总值最大的值的索引</span></span><br><span class="line">acount_high_up = np.argmax(add_width)</span><br><span class="line">acount_high_down = np.argmax(add_width)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将上边界坐标值上移,直到没有遇到白色点停止,此为数字的上边界</span></span><br><span class="line"><span class="keyword">while</span> add_width[acount_high_up] != <span class="number">0</span>:</span><br><span class="line">acount_high_up = acount_high_up + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将下边界坐标值下移,直到没有遇到白色点停止,此为数字的下边界</span></span><br><span class="line"><span class="keyword">while</span> add_width[acount_high_down] != <span class="number">0</span>:</span><br><span class="line">acount_high_down = acount_high_down - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化左右边界为宽度总值最大的值的索引</span></span><br><span class="line">acount_width_left = np.argmax(add_high)</span><br><span class="line">acount_width_right = np.argmax(add_high)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将左边界坐标值左移,直到没有遇到白色点停止,此为数字的左边界</span></span><br><span class="line"><span class="keyword">while</span> add_high[acount_width_left] != <span class="number">0</span>:</span><br><span class="line">acount_width_left = acount_width_left - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将右边界坐标值右移,直到没有遇到白色点停止,此为数字的右边界</span></span><br><span class="line"><span class="keyword">while</span> add_high[acount_width_right] != <span class="number">0</span>:</span><br><span class="line">acount_width_right = acount_width_right + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 求出宽和高的间距</span></span><br><span class="line">width_spacing = acount_width_right - acount_width_left</span><br><span class="line">high_spacing = acount_high_up - acount_high_down</span><br><span class="line"></span><br><span class="line"><span class="comment"># 求出宽和高的间距差</span></span><br><span class="line">poor = width_spacing - high_spacing</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将数字进行正方形分割,目的是方便之后进行图像压缩</span></span><br><span class="line"><span class="keyword">if</span> poor > <span class="number">0</span>:</span><br><span class="line">tailor_image = img[acount_high_down - poor \</span><br><span class="line">// <span class="number">2</span> - <span class="number">5</span>:acount_high_up + poor - poor \</span><br><span class="line">// <span class="number">2</span> + <span class="number">5</span>, acount_width_left - <span class="number">5</span>:acount_width_right + <span class="number">5</span>]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">tailor_image = img[acount_high_down - <span class="number">5</span>:acount_high_up + <span class="number">5</span>, \</span><br><span class="line">acount_width_left + poor // \</span><br><span class="line"><span class="number">2</span> - <span class="number">5</span>:acount_width_right - poor + poor // <span class="number">2</span> + <span class="number">5</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># ======================小图处理======================= #</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将裁剪后的图片进行灰度化</span></span><br><span class="line">gray_img = cv2.cvtColor(tailor_image , cv2.COLOR_BGR2GRAY)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 高斯去噪</span></span><br><span class="line">gauss_img = cv2.GaussianBlur(gray_img, (<span class="number">5</span>,<span class="number">5</span>), <span class="number">0</span>, <span class="number">0</span>, cv2.BORDER_DEFAULT)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将图像形状调整到28*28大小</span></span><br><span class="line">zoom_image = cv2.resize(gauss_img, (<span class="number">28</span>, <span class="number">28</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取图像的高和宽</span></span><br><span class="line">high = zoom_image.shape[<span class="number">0</span>]</span><br><span class="line">wide = zoom_image.shape[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将图像每个点的灰度值进行阈值比较</span></span><br><span class="line"><span class="keyword">for</span> h <span class="keyword">in</span> range(high):</span><br><span class="line"><span class="keyword">for</span> w <span class="keyword">in</span> range(wide):</span><br><span class="line"></span><br><span class="line"><span class="comment"># 若灰度值大于100,则判断为背景并赋值0,否则将深灰度值变白处理</span></span><br><span class="line"><span class="keyword">if</span> zoom_image[h][w] > <span class="number">100</span>:</span><br><span class="line">zoom_image[h][w] = <span class="number">0</span></span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">zoom_image[h][w] = <span class="number">255</span> - zoom_image[h][w]</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> zoom_image</span><br></pre></td></tr></table></figure><h4 id="预测"><a href="#预测" class="headerlink" title="预测"></a>预测</h4><p>这里和之前写的不同的是把预测部分封装成一个函数,这样方便传递结果,文件名为:<strong>predict.py</strong></p><figure class="highlight python"><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">import</span> torch</span><br><span class="line"><span class="keyword">from</span> network <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># pretreatment.py为上面图片预处理的文件名,导入图片预处理文件</span></span><br><span class="line"><span class="keyword">import</span> pretreatment <span class="keyword">as</span> PRE</span><br><span class="line"></span><br><span class="line">net = net()</span><br><span class="line">net.load_state_dict(torch.load(<span class="string">'weight/test.pth'</span>))</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">predict_number</span>():</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 得到返回的待预测图片值,就是pretreatment.py中的zoom_image</span></span><br><span class="line">img = PRE.image_preprocessing()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将待预测图片转换形状</span></span><br><span class="line">inputs = img.reshape(<span class="number">-1</span>, <span class="number">784</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输入数据转换成tensor张量类型,并转换成浮点类型</span></span><br><span class="line">inputs = torch.from_numpy(inputs)</span><br><span class="line">inputs = inputs.float()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 丢入网络进行预测,得到预测数据</span></span><br><span class="line">predict = net(inputs)</span><br><span class="line"></span><br><span class="line"><span class="comment"># # 打印对应的最后的预测结果</span></span><br><span class="line"><span class="comment"># print("The number in this picture is {}".format(torch.argmax(predict).detach().numpy()))</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 返回预测结果</span></span><br><span class="line"><span class="keyword">return</span> torch.argmax(predict).detach().numpy()</span><br></pre></td></tr></table></figure><h4 id="服务器"><a href="#服务器" class="headerlink" title="服务器"></a>服务器</h4><p>文件名为:<strong>my_server.py</strong></p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> Flask</span><br><span class="line"><span class="keyword">from</span> flask <span class="keyword">import</span> request</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"><span class="keyword">from</span> werkzeug.utils <span class="keyword">import</span> secure_filename</span><br><span class="line"><span class="keyword">from</span> predict <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line">app = Flask(__name__)</span><br><span class="line"></span><br><span class="line"><span class="meta">@app.route('/')</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">test</span>():</span></span><br><span class="line"><span class="keyword">return</span> <span class="string">'服务器正常运行'</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 此方法接收图片</span></span><br><span class="line"><span class="meta">@app.route('/upload', methods=['POST'])</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">upload</span>():</span></span><br><span class="line"></span><br><span class="line">f = request.files[<span class="string">'file'</span>]</span><br><span class="line">print(<span class="string">'连接成功'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 当前文件所在路径</span></span><br><span class="line">basepath = os.path.dirname(__file__)</span><br><span class="line">upload_path = os.path.join(basepath, <span class="string">'getImage'</span>, secure_filename(f.filename))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存文件</span></span><br><span class="line">f.save(upload_path)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 放入预测函数得到结果</span></span><br><span class="line">my_result = predict_number()</span><br><span class="line">print(my_result)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将结果返回给手机</span></span><br><span class="line"><span class="keyword">return</span> str(my_result)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">app.run(host=<span class="string">'0.0.0.0'</span>, port=<span class="number">5555</span>)</span><br></pre></td></tr></table></figure><h3 id="效果"><a href="#效果" class="headerlink" title="效果"></a>效果</h3><p><img src= "/img/loading.gif" data-lazy-src="/2022/03/15/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88Android%E7%89%88%EF%BC%89%E6%9C%8D%E5%8A%A1%E7%AB%AF/img1.png" alt></p>]]></content>
<summary type="html"><h2 id="手写数字识别(Android版)服务端"><a href="#手写数字识别(Android版)服务端" class="headerlink" title="手写数字识别(Android版)服务端"></a>手写数字识别(Android版)服务端</h2><h3 i</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="手写数字识别" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
</entry>
<entry>
<title>二分查找</title>
<link href="https://huanqgingyu.top/2022/01/20/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/"/>
<id>https://huanqgingyu.top/2022/01/20/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/</id>
<published>2022-01-19T16:00:00.000Z</published>
<updated>2022-08-28T15:20:00.918Z</updated>
<content type="html"><![CDATA[<h3 id="11-旋转数组的最小数字"><a href="#11-旋转数组的最小数字" class="headerlink" title="11.旋转数组的最小数字"></a>11.旋转数组的最小数字</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/9f3231a991af4f55b95579b44b7a01ba">牛客网</a><br><strong>思路</strong><br><strong>方法:二分法</strong><br>旋转数组将原本有序的数组分成了两部分有序的数组,因为在原始有序数组中,最小的元素一定是在首位,旋转后无序的点就是最小的数字。我们可以将旋转前的前半段命名为A,旋转后的前半段命名为B,旋转数组即将AB变成了BA,我们想知道最小的元素到底在哪里。<br>因为A部分和B部分都是各自有序的,所以我们还是想用分治来试试,每次比较中间值,确认目标值(最小元素)所在的区间。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(log2n),二分法最坏情况对n取2的对数</li><li>空间复杂度:O(1),常数级变量,无额外辅助空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">minNumberInRotateArray</span><span class="params">(<span class="keyword">int</span> [] array)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> right = array.length - <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> (left < right) {</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> mid = (left + right) / <span class="number">2</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 最小的数字在mid右边</span></span><br><span class="line"> <span class="keyword">if</span> (array[mid] > array[right]) {</span><br><span class="line"> left = mid + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 无法判断最小的数字在哪一边,一个一个试</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (array[mid] == array[right]) {</span><br><span class="line"> right--;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 最小数字要么是mid要么在mid左边</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> right = mid;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> array[left];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="53-数字在排序数组中出现的次数"><a href="#53-数字在排序数组中出现的次数" class="headerlink" title="53.数字在排序数组中出现的次数"></a>53.数字在排序数组中出现的次数</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/70610bf967994b22bb1c26f9ae901fa2">牛客网</a><br><strong>思路</strong><br><strong>方法:二分法</strong><br>因为data是一个非降序数组,它是有序的,这种时候我们可能会想到用二分查找。但是一个数组可能有多个k,而且我们要查找的并非常规二分法中k出现的位置,而是k出现的左界和k出现的右界。要是能刚好找到恰好小于k的数字位置和恰好大于k的数字的位置就好了。<br>再有因为数组中全是整数,因此我们可以考虑,用二分查找找到k+0.5应该出现的位置和k−0.5应该出现的位置,二者相减就是k出现的次数。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(log2n),其中n为数组长度,两次二分查找,二分查找复杂度为O(log2n)</li><li>空间复杂度:O(1),常数级变量,无额外辅助空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="comment">// 二分查找</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">binarySearch</span><span class="params">(<span class="keyword">int</span>[] array, <span class="keyword">double</span> k)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> right = array.length - <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 二分左右界</span></span><br><span class="line"> <span class="keyword">while</span> (left <= right) {</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> mid = (left + right) / <span class="number">2</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (array[mid] < k) {</span><br><span class="line"> left = mid + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> right = mid - <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> left;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">GetNumberOfK</span><span class="params">(<span class="keyword">int</span> [] array , <span class="keyword">int</span> k)</span> </span>{</span><br><span class="line"> <span class="comment">// 分别查找k+0.5和k-0.5应该出现的位置,中间的部分就全是k</span></span><br><span class="line"> <span class="keyword">return</span> binarySearch(array, k + <span class="number">0.5</span>) - binarySearch(array, k - <span class="number">0.5</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h3 id="11-旋转数组的最小数字"><a href="#11-旋转数组的最小数字" class="headerlink" title="11.旋转数组的最小数字"></a>11.旋转数组的最小数字</h3><p><strong>题目</strong><br><a href</summary>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
<category term="剑指offer刷题" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer%E5%88%B7%E9%A2%98/"/>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>动态规划</title>
<link href="https://huanqgingyu.top/2022/01/20/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/"/>
<id>https://huanqgingyu.top/2022/01/20/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/</id>
<published>2022-01-19T16:00:00.000Z</published>
<updated>2022-08-28T15:23:07.962Z</updated>
<content type="html"><![CDATA[<h3 id="10-1斐波那契数列"><a href="#10-1斐波那契数列" class="headerlink" title="10.1斐波那契数列"></a>10.1斐波那契数列</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/c6c7742f5ba7442aada113136ddea0c3">牛客网</a><br><strong>思路</strong><br><strong>方法:迭代相加</strong><br>斐波那契数列初始化第1项与第2项都是1,则根据公式第0项为0,可以按照斐波那契公式累加到第n项。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),其中n为输入的数,n次迭代</li><li>空间复杂度:O(1),常数级变量,没有其他额外辅助空间</li></ul><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="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">Fibonacci</span><span class="params">(<span class="keyword">int</span> n)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 从0开始,第0项是0,第一项是1</span></span><br><span class="line"> <span class="keyword">if</span> (n <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> b = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 因n=2时也为1,初始化的时候把a=0,b=1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>;i <= n;i++) {</span><br><span class="line"> <span class="comment">// 第三项开始是前两项的和,然后保留最新的两项,更新数据相加</span></span><br><span class="line"> res = (a + b);</span><br><span class="line"> a = b;</span><br><span class="line"> b = res;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="10-2矩形覆盖"><a href="#10-2矩形覆盖" class="headerlink" title="10.2矩形覆盖"></a>10.2矩形覆盖</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/72a5a919508a4251859fb2cfb987a0e6">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划</strong></p><ul><li>n = 1的时候<ul><li>只能竖着覆盖,1种</li></ul></li><li>n = 2的时候<ul><li>可以竖着和横着覆盖,2种</li></ul></li><li>n = 3的时候<ul><li>第三级竖着覆盖,用了一级,剩下 n = 2,有2种覆盖方法</li><li>第三级横着覆盖,用了两级,剩下 n = 1,有1种覆盖方法</li><li>总共有3种</li></ul></li><li>n = 4的时候<ul><li>第四级竖着覆盖,用了一级,剩下 n = 3,有3种覆盖方法</li><li>第四级横着覆盖,用了两级,剩下 n = 2,有2种覆盖方法</li><li>总共有5种</li></ul></li><li>n = n的时候<ul><li>第n级竖着覆盖,用了一级,剩下 n = n - 1,所以关注第 n - 1 种有几种覆盖方法</li><li>第n级横着覆盖,用了两级,剩下 n = n - 2,所以关注第 n - 2 种有几种覆盖方法</li><li>总和为两种情况的总和</li></ul></li></ul><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),一次遍历</li><li>空间复杂度:O(1),常数级变量,没有其他额外辅助空间</li></ul><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="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">rectCover</span><span class="params">(<span class="keyword">int</span> target)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (target <= <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">int</span> b = <span class="number">2</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">3</span>;i <= target;i++) {</span><br><span class="line"> res = a + b;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 变量更新</span></span><br><span class="line"> a = b;</span><br><span class="line"> b = res;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="10-3跳台阶"><a href="#10-3跳台阶" class="headerlink" title="10.3跳台阶"></a>10.3跳台阶</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/8c82a5b80378478f9484d87d1c5f12a4">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划</strong><br>当 n = 1 时,只有一种跳法。<br>当 n = 2 时,有两种跳法。<br>跳 n 阶台阶,可以先跳 1 阶台阶,再跳 n-1 阶台阶;或者先跳 2 阶台阶,再跳 n-2 阶台阶。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),一次遍历</li><li>空间复杂度:O(1),常数级变量,没有其他额外辅助空间</li></ul><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="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">rectCover</span><span class="params">(<span class="keyword">int</span> target)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (target <= <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">return</span> target;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> a = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">int</span> b = <span class="number">2</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">3</span>;i <= target;i++) {</span><br><span class="line"> res = a + b;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 变量更新</span></span><br><span class="line"> a = b;</span><br><span class="line"> b = res;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="10-4变态跳台阶"><a href="#10-4变态跳台阶" class="headerlink" title="10.4变态跳台阶"></a>10.4变态跳台阶</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/22243d016f6b47f2a6928b4313c85387">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划</strong><br>跳上 n-1 级台阶,可以从 n-2 级跳 1 级上去,也可以从 n-3 级跳 2 级上去…,那么<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">f(n-1) = f(n-2) + f(n-3) + ... + f(0)</span><br></pre></td></tr></table></figure><br>同样,跳上 n 级台阶,可以从 n-1 级跳 1 级上去,也可以从 n-2 级跳 2 级上去… ,那么<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">f(n) = f(n-1) + f(n-2) + ... + f(0)</span><br></pre></td></tr></table></figure><br>综上可得<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">f(n) - f(n-1) = f(n-1)</span><br></pre></td></tr></table></figure><br>即<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">f(n) = 2*f(n-1)</span><br></pre></td></tr></table></figure></p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),一次遍历</li><li>空间复杂度:O(1),常数级变量,没有其他额外辅助空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">jumpFloorII</span><span class="params">(<span class="keyword">int</span> target)</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (target <= <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> target;</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">int</span> pre = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化最终结果</span></span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">2</span>;i <= target;i++) {</span><br><span class="line"> res = <span class="number">2</span> * pre;</span><br><span class="line"> pre = res;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="42-连续子数组的最大和"><a href="#42-连续子数组的最大和" class="headerlink" title="42.连续子数组的最大和"></a>42.连续子数组的最大和</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/459bd355da1549fa8a49e350bf3df484">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划</strong><br>设动态规划列表 dp,dp[i] 代表以元素 array[i] 为结尾的连续子数组最大和。<br>状态转移方程: dp[i] = Math.max(dp[i - 1] + array[i], array[i]);<br>dp[i]与dp[ i - 1]是否大于0相关:</p><ul><li>dp[i] = dp[i - 1] + array[i],dp[i - 1] > 0</li><li>dp[i] = array[i] ,dp[i - 1] <= 0</li></ul><p><strong>假设:</strong>在看到这种思路时提出了一个问题,那就是假设:dp[i - 1] = dp[i - 2] + 正整数,那么当dp[i - 1] < 0 的时候,为什么要抛弃array[i]之前的所有数呢?毕竟array[i] + 正整数也是比array[i]要更大的。<br><strong>证明:</strong>当dp[i - 1] = dp[i - 2] + 正整数,那么必定有dp[i - 2] > 0,那么dp[i - 1]一定也 > 0,所以以上假设中dp[i - 1] < 0不成立,所以以上问题不存在。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),一次遍历</li><li>空间复杂度:O(1),常数级变量,没有其他额外辅助空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">FindGreatestSumOfSubArray</span><span class="params">(<span class="keyword">int</span>[] array)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> sum = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> maxSum = array[<span class="number">0</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < array.length;i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 状态转移:连续子数组和最大值</span></span><br><span class="line"> sum = Math.max(sum + array[i], array[i]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 维护最大值</span></span><br><span class="line"> maxSum = Math.max(maxSum, sum);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> maxSum;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="47-礼物的最大价值"><a href="#47-礼物的最大价值" class="headerlink" title="47.礼物的最大价值"></a>47.礼物的最大价值</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/2237b401eb9347d282310fc1c3adb134">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划</strong><br>如果我们现在已经身处最右下角的一个格子,获取了这个礼物,那我们肯定是加上来自左边累计的最大礼物价值与来自上边累计的最大礼物价值的较大值,这样我们能获取的礼物价值才会更大,因此我们用dp[i][j]表示从左上角到第i行第j列的格子总共能获取的最大价值,因此转移方程为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dp[i][j] = grid[i][j] + max(dp[i−1][j], dp[i][j−1]))</span><br></pre></td></tr></table></figure><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(mn),其中m、n分别为矩阵的边长,遍历整个矩阵</li><li>空间复杂度:O(1),原数组中修改,无额外辅助空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">maxValue</span> <span class="params">(<span class="keyword">int</span>[][] grid)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> m = grid.length;</span><br><span class="line"> <span class="keyword">int</span> n = grid[<span class="number">0</span>].length;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 第一列只能来自上方</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i < m;i++) {</span><br><span class="line"> grid[i][<span class="number">0</span>] += grid[i - <span class="number">1</span>][<span class="number">0</span>];</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 第一行只能来自左边</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i < n;i++) {</span><br><span class="line"> grid[<span class="number">0</span>][i] += grid[<span class="number">0</span>][i - <span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 遍历后续每一个位置</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i < m;i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">1</span>;j < n;j++) {</span><br><span class="line"> <span class="comment">// 增加来自左边的与上边的之间的较大值</span></span><br><span class="line"> grid[i][j] += Math.max(grid[i - <span class="number">1</span>][j], grid[i][j - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> grid[m - <span class="number">1</span>][n - <span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="48-最长不含重复字符的子字符串"><a href="#48-最长不含重复字符的子字符串" class="headerlink" title="48.最长不含重复字符的子字符串"></a>48.最长不含重复字符的子字符串</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/48d2ff79b8564c40a50fa79f9d5fa9c7">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划+哈希表</strong><br>如果对于某个前面的子串,如果我们新加入一个字符,与前面的都不重复,那么最长无重复子串肯定就是在前面的基础上加1,如果与前面重复了,那就是当前位置减去它重复之前字符出现的位置的长度。因此我们使用动态规划递推。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),其中n为字符串长度,遍历一次字符串</li><li>空间复杂度:O(n),辅助数组dp的大小为字符串长度,哈希表的最大空间为字符串长度</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">lengthOfLongestSubstring</span> <span class="params">(String s)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 哈希表记录窗口内非重复的字符及其下标</span></span><br><span class="line"> HashMap<Character, Integer> mp = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> res = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// dp[i]表示以下标i结尾的字符串最长不含重复子串的长度</span></span><br><span class="line"> <span class="keyword">int</span>[] dp = <span class="keyword">new</span> <span class="keyword">int</span>[s.length() + <span class="number">1</span>];</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i <= s.length();i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 哈希表中没有,说明不重复</span></span><br><span class="line"> <span class="keyword">if</span> (!mp.containsKey(s.charAt(i - <span class="number">1</span>))) {</span><br><span class="line"> <span class="comment">// 前一个加1</span></span><br><span class="line"> dp[i] = dp[i - <span class="number">1</span>] + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 遇到重复字符</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> dp[i] = Math.min(dp[i - <span class="number">1</span>] + <span class="number">1</span>, i - mp.get(s.charAt(i - <span class="number">1</span>)));</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 加入哈希表</span></span><br><span class="line"> mp.put(s.charAt(i - <span class="number">1</span>), i);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 维护最大值</span></span><br><span class="line"> res = Math.max(res, dp[i]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="49-丑数"><a href="#49-丑数" class="headerlink" title="49.丑数"></a>49.丑数</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/6aa9e04fc3794f68acf8778237ba065b">牛客网</a><br><strong>思路</strong><br><strong>方法:动态规划</strong><br>我们知道丑数是由1开始的每个丑数依次乘上2、3、5得到,而我们每次只需要在其中找到最小的一个,一共找n次即可。我们可以用i、j、k三个下标表示在已经找到的丑数中那个数分别被乘2、乘3、乘5有无被记录过,然后依次找n个数字就可以了。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),只需要遍历一次</li><li>空间复杂度:O(n),记录丑数的数组最大长度为n</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 寻找三个数中的最小值</span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">findMin</span><span class="params">(<span class="keyword">int</span> x, <span class="keyword">int</span> y, <span class="keyword">int</span> z)</span> </span>{</span><br><span class="line"> <span class="keyword">int</span> res = Math.min(x, y);</span><br><span class="line"> res = Math.min(res, z);</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">GetUglyNumber_Solution</span><span class="params">(<span class="keyword">int</span> index)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 排除0</span></span><br><span class="line"> <span class="keyword">if</span> (index == <span class="number">0</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="comment">// 按顺序记录丑数</span></span><br><span class="line"> ArrayList<Integer> list = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> </span><br><span class="line"> list.add(<span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 记录这是第几个丑数</span></span><br><span class="line"> <span class="keyword">int</span> count = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 分别代表要乘上2 3 5的下标</span></span><br><span class="line"> <span class="keyword">int</span> i = <span class="number">0</span>, j = <span class="number">0</span>, k = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> (count < index) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 找到三个数中最小的丑数</span></span><br><span class="line"> list.add(findMin(list.get(i) * <span class="number">2</span>, list.get(j) * <span class="number">3</span>, list.get(k) * <span class="number">5</span>));</span><br><span class="line"> </span><br><span class="line"> count++;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 由2与已知丑数相乘得到的丑数,那该下标及之前的在2这里都用不上了</span></span><br><span class="line"> <span class="keyword">if</span> (list.get(count - <span class="number">1</span>) == list.get(i) * <span class="number">2</span>) {</span><br><span class="line"> i++;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 由3与已知丑数相乘得到的丑数,那该下标及之前的在3这里都用不上了</span></span><br><span class="line"> <span class="keyword">if</span> (list.get(count - <span class="number">1</span>) == list.get(j) * <span class="number">3</span>) {</span><br><span class="line"> j++;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 由5与已知丑数相乘得到的丑数,那该下标及之前的在5这里都用不上了</span></span><br><span class="line"> <span class="keyword">if</span> (list.get(count - <span class="number">1</span>) == list.get(k) * <span class="number">5</span>) {</span><br><span class="line"> k++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> list.get(count - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="66-构建乘积数组"><a href="#66-构建乘积数组" class="headerlink" title="66.构建乘积数组"></a>66.构建乘积数组</h3><p><strong>题目</strong><br><strong>题目主要信息:</strong></p><ul><li>给定一个数组A,要求返回数组B,数组B每个元素等于数组A所有元素除了对应下标以外的全部元素的乘积</li><li>即B[i]=A[0]∗A[1]∗…∗A[i−1]∗A[i+1]∗…∗A[n−1]</li><li>程序不能使用除法</li></ul><figure class="highlight plain"><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">输入:[1,2,3,4,5]</span><br><span class="line">返回值:[120,60,40,30,24]</span><br></pre></td></tr></table></figure><p><strong>思路</strong><br><strong>方法:双向遍历</strong><br>矩阵中由对角线1将其分成了上三角和下三角。我们先看下三角,如果我们累乘的时候,B[1]是在B[0]的基础上乘了新增的一个A[0],B[2]是在B[1]的基础上乘了新增的一个A[1],那我们可以遍历数组的过程中不断将数组B的前一个数与数组A的前一个数相乘就得到了下三角中数组B的当前数。同理啊,我们在上三角中,用一个变量存储从右到左的累乘,每次只会多乘上一个数字。这样,两次遍历就可以解决。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),其中n为数组A的长度,遍历两次数组</li><li>空间复杂度:O(n),数组B为返回必要空间,不属于额外空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span>[] multiply(<span class="keyword">int</span>[] A) {</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化数组B</span></span><br><span class="line"> <span class="keyword">int</span>[] B = <span class="keyword">new</span> <span class="keyword">int</span>[A.length];</span><br><span class="line"> </span><br><span class="line"> B[<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 先乘左边,从左到右</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i < A.length;i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 每多一位由数组B左边的元素多乘一个前面A的元素</span></span><br><span class="line"> B[i] = B[i - <span class="number">1</span>] * A[i - <span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> temp = <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 再乘右边,从右到左</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = A.length - <span class="number">1</span>;i >= <span class="number">0</span>;i--) {</span><br><span class="line"> <span class="comment">// temp为右边的累乘</span></span><br><span class="line"> B[i] *= temp;</span><br><span class="line"> temp *= A[i];</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> B;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h3 id="10-1斐波那契数列"><a href="#10-1斐波那契数列" class="headerlink" title="10.1斐波那契数列"></a>10.1斐波那契数列</h3><p><strong>题目</strong><br><a href="https:</summary>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
<category term="剑指offer刷题" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer%E5%88%B7%E9%A2%98/"/>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>堆栈队列</title>
<link href="https://huanqgingyu.top/2022/01/20/%E5%A0%86%E6%A0%88%E9%98%9F%E5%88%97/"/>
<id>https://huanqgingyu.top/2022/01/20/%E5%A0%86%E6%A0%88%E9%98%9F%E5%88%97/</id>
<published>2022-01-19T16:00:00.000Z</published>
<updated>2022-08-28T15:18:59.306Z</updated>
<content type="html"><![CDATA[<h3 id="31-栈的压入、弹出序列"><a href="#31-栈的压入、弹出序列" class="headerlink" title="31.栈的压入、弹出序列"></a>31.栈的压入、弹出序列</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/d77d11405cc7470d82554cb392585106">牛客网</a><br><strong>思路</strong><br><strong>方法:</strong>栈<br>用一个栈来模拟两个序列是否符合入栈出栈的次序,对于入栈序列,只要栈为空,序列肯定要依次入栈,当遇到一个元素等于当前的出栈序列的元素,那我们就放弃入栈,让它先出来。</p><p><strong>注:</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.Stack;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">IsPopOrder</span><span class="params">(<span class="keyword">int</span> [] pushA,<span class="keyword">int</span> [] popA)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 创建辅助栈</span></span><br><span class="line"> Stack<Integer> s = <span class="keyword">new</span> Stack<>();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">int</span> n = pushA.length;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化入栈的下标</span></span><br><span class="line"> <span class="keyword">int</span> j = <span class="number">0</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 遍历出栈的数组</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < n;i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 入栈:栈为空或者栈顶不等于出栈数组</span></span><br><span class="line"> <span class="keyword">while</span> ((j < n) && (s.isEmpty() || (s.peek() != popA[i]))) {</span><br><span class="line"> s.push(pushA[j]);</span><br><span class="line"> j++;</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">if</span> (s.peek() == popA[i]) {</span><br><span class="line"> s.pop();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 不匹配序列</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="40-最小的k个数"><a href="#40-最小的k个数" class="headerlink" title="40.最小的k个数"></a>40.最小的k个数</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf">牛客网</a><br><strong>思路</strong><br><strong>方法:堆排序</strong><br>要找到最小的k个元素,只需要准备k个数字,之后每次遇到一个数字能够快速的与这k个数字中最大的值比较,每次将最大的值替换掉,那么最后剩余的就是k个最小的数字了。使用优先队列(大根堆),只要限制堆的大小为k,那么堆顶就是k个数字的中最大值,如果需要替换,将这个最大值拿出,加入新的元素就好了。<br>由于<strong>PriorityQueue默认是小根堆</strong>,所以要重写比较器来实现大根堆。</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="comment">// 构建一个大顶堆</span></span><br><span class="line">PriorityQueue<Integer> q = <span class="keyword">new</span> PriorityQueue<>((o1, o2) -> (o2 - o1));</span><br></pre></td></tr></table></figure><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="comment">// 较小元素入堆</span></span><br><span class="line"><span class="keyword">if</span> (q.peek() > input[i]) {</span><br><span class="line"> q.poll();</span><br><span class="line"> q.offer(input[i]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>注:</strong>因为使用大顶堆才能控制住堆内的元素都是<=顶值的,这样就能找到最小的k个值。</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ArrayList<Integer> <span class="title">GetLeastNumbers_Solution</span><span class="params">(<span class="keyword">int</span> [] input, <span class="keyword">int</span> k)</span> </span>{</span><br><span class="line"> ArrayList<Integer> res = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 排除特殊情况</span></span><br><span class="line"> <span class="keyword">if</span> ((k == <span class="number">0</span>) || (input.length == <span class="number">0</span>)) {</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 构建大顶堆</span></span><br><span class="line"> PriorityQueue<Integer> q = <span class="keyword">new</span> PriorityQueue<>((o1, o2) -> (o2 - o1));</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 构建一个k个大小的堆</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < k;i++) {</span><br><span class="line"> q.offer(input[i]);</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">for</span> (<span class="keyword">int</span> i = k;i < input.length;i++) {</span><br><span class="line"> <span class="keyword">if</span> (q.peek() > input[i]) {</span><br><span class="line"> q.poll();</span><br><span class="line"> q.offer(input[i]);</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">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < k;i++) {</span><br><span class="line"> res.add(q.poll());</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="41-1数据流中的中位数"><a href="#41-1数据流中的中位数" class="headerlink" title="41.1数据流中的中位数"></a>41.1数据流中的中位数</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/9be0172896bd43948f8a32fb954e1be1">牛客网</a><br><strong>思路</strong><br><strong>插入排序:</strong><br>每进来一个数字就用插入排序将已有的数字进行排序,然后利用计数器找到中位数。<br>时间复杂度:Insert函数O(n),不管遍历还是插入都是O(n),GetMedian函数O(1),直接访问。<br>空间复杂度:O(n),记录输入流的数组<br><strong>堆排序:</strong><br>创建两个堆把数字分成左右两个部分,并且保证右堆比左堆中的数字都要大,所以要左堆是大根堆,右堆是小根堆,这样就能够完成将数据分成左小右大的两部分。以为将数据变得完全有序,所以相对插入排序来说减少了部分时间开销。<br>时间复杂度:Insert函数O(log2n),维护堆的复杂度,GetMedian函数O(1),直接访问。<br>空间复杂度:O(n),两个堆的空间,虽是两个,但是一个堆最多n/2<br>堆排序实现代码:</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 定义两个堆,左边为大堆,右边为小堆</span></span><br><span class="line"> PriorityQueue<Integer> left = <span class="keyword">new</span> PriorityQueue<>((o1, o2) -> (o2 - o1));</span><br><span class="line"> PriorityQueue<Integer> right = <span class="keyword">new</span> PriorityQueue<>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 记录插入了多少个数字</span></span><br><span class="line"> <span class="keyword">int</span> n = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Insert</span><span class="params">(Integer num)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> n++;</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (n % <span class="number">2</span> == <span class="number">0</span>) { </span><br><span class="line"> <span class="comment">/* 先将数字放入左堆,左堆根代表所有</span></span><br><span class="line"><span class="comment"> 插入左堆中数字的最大值,然后将左堆根</span></span><br><span class="line"><span class="comment"> 从左堆删除放入右堆中,这样就能保证所</span></span><br><span class="line"><span class="comment"> 有的右堆数字比左堆大</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> left.offer(num);</span><br><span class="line"> right.offer(left.poll());</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> right.offer(num);</span><br><span class="line"> left.offer(right.poll());</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">public</span> Double <span class="title">GetMedian</span><span class="params">()</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 当有偶数个数字时,返回左右堆根的平均值</span></span><br><span class="line"> <span class="keyword">if</span> (n % <span class="number">2</span> == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> ((left.peek() + right.peek()) / <span class="number">2.0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 当有基数个数字时返回左堆根,因为从左堆开始放数字</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> (<span class="keyword">double</span>)left.peek();</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="41-2字符流中第一个不重复的字符"><a href="#41-2字符流中第一个不重复的字符" class="headerlink" title="41.2字符流中第一个不重复的字符"></a>41.2字符流中第一个不重复的字符</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/00de97733b8e4f97a3fb5c680ee10720">牛客网</a><br><strong>思路</strong><br><strong>方法:哈希表+字符串</strong><br>又是一个找到是否重复的问题。我们还是可以用哈希表来记录各个字符出现的次数,根据这样只要是字符串最前面且哈希表中次数为1的字符就是我们要找的。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),每次插入字符都是O(1),每次查询需要遍历字符串O(n)</li><li>空间复杂度:O(n),字符一定在ASCII范围内,因此哈希表大小为常数,但是记录的字符串长度为n</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化字符串和hashmap</span></span><br><span class="line"> <span class="keyword">private</span> StringBuffer s = <span class="keyword">new</span> StringBuffer();</span><br><span class="line"> <span class="keyword">private</span> HashMap<Character, Integer> mp = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> </span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Insert</span><span class="params">(<span class="keyword">char</span> ch)</span> </span>{</span><br><span class="line"> s.append(ch);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 将字符和字符对应的出现次数加入map容器</span></span><br><span class="line"> mp.put(ch, (mp.getOrDefault(ch, <span class="number">0</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="keyword">public</span> <span class="keyword">char</span> <span class="title">FirstAppearingOnce</span><span class="params">()</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 查询字符串中每个字符出现的次数并比较</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < s.length();i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 如果字符出现的次数等于1则返回这个字符</span></span><br><span class="line"> <span class="keyword">if</span> (mp.get(s.charAt(i)) == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> s.charAt(i);</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">return</span> <span class="string">'#'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="59-滑动窗口的最大值"><a href="#59-滑动窗口的最大值" class="headerlink" title="59.滑动窗口的最大值"></a>59.滑动窗口的最大值</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/1624bc35a45c42c0bc17d17fa0cba788">牛客网</a><br><strong>思路</strong><br><strong>方法:双向队列</strong><br>我们都知道,若是一个数字A进入窗口后,若是比窗口内其他数字都大,那么这个数字之前的数字都没用了,因为它们必定会比A早离开窗口,在A离开之前都争不过A,所以A在进入时依次从尾部排除掉之前的小值再进入,而每次窗口移动要弹出窗口最前面值,因此队首也需要弹出,所以我们选择双向队列。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),数组长度为n,只遍历一遍数组</li><li>空间复杂度:O(m),窗口长度m,双向队列最长时,将窗口填满</li></ul><p><strong>注:</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.*;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ArrayList<Integer> <span class="title">maxInWindows</span><span class="params">(<span class="keyword">int</span> [] num, <span class="keyword">int</span> size)</span> </span>{</span><br><span class="line"> ArrayList<Integer> res = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 窗口大于数组长度的时候,返回空</span></span><br><span class="line"> <span class="keyword">if</span> ((size <= num.length) && (size != <span class="number">0</span>)) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 双向队列</span></span><br><span class="line"> ArrayDeque<Integer> dq = <span class="keyword">new</span> ArrayDeque<>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 先遍历第一个窗口</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < size;i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 去掉比自己先进队列的小于自己的值</span></span><br><span class="line"> <span class="keyword">while</span> (!dq.isEmpty() && num[dq.peekLast()] < num[i]) {</span><br><span class="line"> dq.pollLast();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// dq.offer(i)等同于dq.offerLast(i);</span></span><br><span class="line"> dq.offer(i);</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">for</span> (<span class="keyword">int</span> i = size;i < num.length;i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 取窗口内的最大值</span></span><br><span class="line"> res.add(num[dq.peekFirst()]);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// (i - size + 1)为窗口的下标的最小值</span></span><br><span class="line"> <span class="keyword">while</span> (!dq.isEmpty() && (dq.peekFirst() < (i - size + <span class="number">1</span>))) {</span><br><span class="line"> <span class="comment">// 弹出窗口移走后的值</span></span><br><span class="line"> dq.pollFirst();</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">while</span> (!dq.isEmpty() && num[dq.peekLast()] < num[i]) {</span><br><span class="line"> dq.pollLast();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> dq.offer(i);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> res.add(num[dq.pollFirst()]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h3 id="31-栈的压入、弹出序列"><a href="#31-栈的压入、弹出序列" class="headerlink" title="31.栈的压入、弹出序列"></a>31.栈的压入、弹出序列</h3><p><strong>题目</strong><br><a href</summary>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
<category term="剑指offer刷题" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer%E5%88%B7%E9%A2%98/"/>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>数组与矩阵</title>
<link href="https://huanqgingyu.top/2022/01/20/%E6%95%B0%E7%BB%84%E4%B8%8E%E7%9F%A9%E9%98%B5/"/>
<id>https://huanqgingyu.top/2022/01/20/%E6%95%B0%E7%BB%84%E4%B8%8E%E7%9F%A9%E9%98%B5/</id>
<published>2022-01-19T16:00:00.000Z</published>
<updated>2022-08-28T15:22:22.227Z</updated>
<content type="html"><![CDATA[<h3 id="3-数组中重复的数字"><a href="#3-数组中重复的数字" class="headerlink" title="3.数组中重复的数字"></a>3.数组中重复的数字</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/6fe361ede7e54db1b84adc81d09d8524">牛客网</a><br><strong>思路</strong></p><ol><li>刚开始看到这个题目时第一反应可能是利用两个循环,外循环遍历数组确定要找的重复值,内循环遍历接下来的数组寻找重复值。但这种方法的时间复杂度太大,效率太低。</li><li>仔细阅读题目可以看到数字的范围在0~(n-1)之间,也就是说若没有重复的数字,在长度为n的数组里将填充着0~(n-1)范围内所有的数,若有重复的数字,数组中0~(n-1)之间的数肯定填不满。由此可以在外部再创建一个数组,在遍历原数组的同时把原数组上的数字存储到新建数组对应下标当中,也就是新建数组上的数字和其下标的值是相同的。但这样时间复杂度虽然变成了O(n)但是空间复杂度也变成了O(n)。</li><li>利用思路2中所分析的特性,不创建新的数组,在原数组中进行数据的交换,具体就是从头开始遍历数组,取下标i,i 上的值为numbers[i],将numbers[i]与数组下标为numbers[i]上的值交换,直到交换到i上的值等于i时进行下一个值遍历。这种方法能够让数组下标和对应的值相等,而且还不用开辟新的数组空间。时间复杂度为O(n),空间复杂度为O(1)。<br>在这里采用思路3最好。</li></ol><p><strong>Java</strong><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="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">duplicate</span> <span class="params">(<span class="keyword">int</span>[] numbers)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历数组</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < numbers.length;i++) {</span><br><span class="line"></span><br><span class="line"><span class="comment">// 若数组上的值与其下标不相等</span></span><br><span class="line"><span class="keyword">if</span> (numbers[i] != i) {</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果遍历到的数与这个数对应的下标上的数相等就表示有重复的数,返回重复数</span></span><br><span class="line"><span class="keyword">if</span> (numbers[i] == numbers[numbers[i]]) {</span><br><span class="line"><span class="keyword">return</span> numbers[i];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 将下标为i 和下标为numbers[i]上的值进行交换</span></span><br><span class="line">swap(numbers, i, numbers[i]);</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 没有重复的数返回-1</span></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="keyword">private</span> <span class="keyword">void</span> <span class="title">swap</span><span class="params">(<span class="keyword">int</span>[] numbers, <span class="keyword">int</span> i, <span class="keyword">int</span> j)</span> </span>{</span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">*功能:交换数组numbers中下标为i 和下标为j 中的值</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">int</span> t = numbers[i];</span><br><span class="line">numbers[i] = numbers[j];</span><br><span class="line">numbers[j] = t;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><strong>Python3</strong><br><figure class="highlight python"><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 class="class"><span class="keyword">class</span> <span class="title">Solution</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">duplicate</span>(<span class="params">self , numbers: List[int]</span>) -> int:</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(len(numbers)):</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(numbers[i] != i):</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> numbers[i] == numbers[numbers[i]]:</span><br><span class="line"> <span class="keyword">return</span> numbers[i]</span><br><span class="line"> </span><br><span class="line"> self.swap(numbers, i, numbers[i])</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 class="function"><span class="keyword">def</span> <span class="title">swap</span>(<span class="params">self, numbers, i, j</span>):</span></span><br><span class="line"> t = numbers[i]</span><br><span class="line"> numbers[i] = numbers[j]</span><br><span class="line"> numbers[j] = t</span><br></pre></td></tr></table></figure></p><h3 id="4-二维数组中的查找"><a href="#4-二维数组中的查找" class="headerlink" title="4.二维数组中的查找"></a>4.二维数组中的查找</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e">牛客网</a><br><strong>思路</strong><br><strong>题目特点:</strong>给定的数组每一行和每一列中的值都是递增。<br>该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。因此,从<strong>右上角开始查找</strong>,就可以根据 target 和当前元素的大小关系来快速地缩小查找区间,每次减少一行或者一列的元素。当前元素的查找区间为左下角的所有元素。</p><p><strong>Java</strong><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">find</span><span class="params">(<span class="keyword">int</span> target, <span class="keyword">int</span>[][] array)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果数组为空,返回false</span></span><br><span class="line"><span class="keyword">if</span> (array == <span class="keyword">null</span>) {</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化横纵坐标值(二维数组右上角)</span></span><br><span class="line"><span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line"><span class="keyword">int</span> j = array[<span class="number">0</span>].length - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 当横纵坐标都满足边界范围内,进行循环查找</span></span><br><span class="line"><span class="keyword">while</span> ((i != array.length) && (j != -<span class="number">1</span>)) {</span><br><span class="line"><span class="keyword">if</span> (target == array[i][j]) {</span><br><span class="line"><span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (target < array[i][j]) {</span><br><span class="line">j--;</span><br><span class="line">} <span class="keyword">else</span> <span class="keyword">if</span> (target > array[i][j]) {</span><br><span class="line">i++;</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 找不到,返回false</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><strong>Python3</strong><br><figure class="highlight python"><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 class="class"><span class="keyword">class</span> <span class="title">Solution</span>:</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">Find</span>(<span class="params">self , target: int, array: List[List[int]]</span>) -> bool:</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (array == []):</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"> i = <span class="number">0</span></span><br><span class="line"> j = len(array[<span class="number">0</span>]) - <span class="number">1</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> ((i != len(array)) <span class="keyword">and</span> (j != <span class="number">-1</span>)):</span><br><span class="line"> <span class="keyword">if</span> (target == array[i][j]):</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">True</span></span><br><span class="line"> <span class="keyword">elif</span> (target < array[i][j]):</span><br><span class="line"> j = j - <span class="number">1</span></span><br><span class="line"> <span class="keyword">elif</span> (target > array[i][j]):</span><br><span class="line"> i = i + <span class="number">1</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> <span class="literal">False</span></span><br></pre></td></tr></table></figure></p><h3 id="5-替换空格"><a href="#5-替换空格" class="headerlink" title="5.替换空格"></a>5.替换空格</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/0e26e5551f2b489b9f58bc83aa4b6c68">牛客网</a><br><strong>思路</strong><br>创建一个全新的字符串或者字符列表,然后再遍历输入的字符串,当遍历到的值不是空格时把遍历到的字符填充到新的字符串中,若是空格则把”%20”填入到新字符串中。</p><p>注:在Java中要掌握String、StringBuffer、StringBuilder类之间的关系和这些类中常用的方法。</p><p><strong>Java</strong><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="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> String <span class="title">replaceSpace</span> <span class="params">(String s)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建一个StringBuffer对象用来操作字符串</span></span><br><span class="line">StringBuffer stringBuffer = <span class="keyword">new</span> StringBuffer();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历字符串并对新字符串赋值</span></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < s.length();i++) { </span><br><span class="line"></span><br><span class="line"><span class="comment">// 如果遍历到了空格字符,则把"%20"加入新字符串中</span></span><br><span class="line"><span class="keyword">if</span> (s.charAt(i) == <span class="string">' '</span>) {</span><br><span class="line">stringBuffer.append(<span class="string">"%20"</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 否则把原字符加入到新字符串中</span></span><br><span class="line"><span class="keyword">else</span> {</span><br><span class="line">stringBuffer.append(s.charAt(i));</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">return</span> stringBuffer.toStirng();</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><strong>Python3</strong><br><figure class="highlight python"><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="class"><span class="keyword">class</span> <span class="title">Solution</span>:</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">replaceSpace</span>(<span class="params">self, s: str</span>) -> str:</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 创建一个空字符串列表</span></span><br><span class="line">result_str = []</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历字符串并赋值到字符列表</span></span><br><span class="line"><span class="keyword">for</span> str_char <span class="keyword">in</span> str:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果遍历到字符串为' '则将'%20'加入到字符串列表</span></span><br><span class="line"><span class="keyword">if</span> str_char == <span class="string">' '</span>:</span><br><span class="line">result_str.append(<span class="string">'%20'</span>)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line">result_str.append(str_char)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将字符串列表拼成字符串并返回</span></span><br><span class="line"><span class="keyword">return</span> <span class="string">''</span>.join(result_str)</span><br></pre></td></tr></table></figure></p><h3 id="29-顺时针打印矩阵"><a href="#29-顺时针打印矩阵" class="headerlink" title="29.顺时针打印矩阵"></a>29.顺时针打印矩阵</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/9b4c81a02cd34f76be2659fa0d54342a">牛客网</a><br><strong>思路</strong><br><strong>方法:</strong>边界模拟法<br>这道题就是一个简单的模拟,我们想象有一个矩阵,从第一个元素开始,往右到底后再往下到底后再往左到底后再往上,结束这一圈,进入下一圈螺旋。</p><p><strong>注:</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ArrayList<Integer> <span class="title">printMatrix</span><span class="params">(<span class="keyword">int</span> [][] matrix)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化集合</span></span><br><span class="line"> ArrayList<Integer> res = <span class="keyword">new</span> ArrayList<Integer>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 若输入数组为空的情况</span></span><br><span class="line"> <span class="keyword">if</span> (matrix.length == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> res;</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">int</span> up = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> down = matrix.length - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">int</span> left = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">int</span> right = matrix[<span class="number">0</span>].length - <span class="number">1</span>;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 循环遍历二维数组直到边界重合</span></span><br><span class="line"> <span class="keyword">while</span> ((up <= down) && (left <= right)) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 上边界从左到右</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = left;i <= right;i++) {</span><br><span class="line"> res.add(matrix[up][i]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> up++;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (up > down) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 右边界从上到下</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = up;i <= down;i++) {</span><br><span class="line"> res.add(matrix[i][right]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> right--;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (left > right) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 下边界从右到左</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = right;i >= left;i--) {</span><br><span class="line"> res.add(matrix[down][i]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> down--;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (up > down) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 左边界从下到上</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = down;i >= up;i--) {</span><br><span class="line"> res.add(matrix[i][left]);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> left++;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (left > right) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 返回数组集合</span></span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="50-第一个只出现一次的字符位置"><a href="#50-第一个只出现一次的字符位置" class="headerlink" title="50.第一个只出现一次的字符位置"></a>50.第一个只出现一次的字符位置</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/1c82e8cf713b4bbeb2a5b31cf5b0417c">牛客网</a><br><strong>思路</strong><br>统计频率可以建立一个哈希表,遍历字符串的同时,统计每个字符出现的频率,然后再从头遍历一次字符串,在哈希表中查看每个字符串的频率,找到第一个只出现一次的字符串,返回位置,如果没找到返回-1即可。</p><p><strong>注:</strong>难点在于怎么建立字符键与出现次数的hashmap。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.HashMap;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">FirstNotRepeatingChar</span><span class="params">(String str)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 新建hashmap</span></span><br><span class="line"> HashMap<Character, Integer> mp = <span class="keyword">new</span> HashMap<>();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 遍历字符串,找到不同字符出现的次数</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < str.length();i++) {</span><br><span class="line"> mp.put(str.charAt(i), (mp.getOrDefault(str.charAt(i), <span class="number">0</span>) + <span class="number">1</span>));</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 根据字符键从hashmap中查找次数</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < str.length();i++) {</span><br><span class="line"> <span class="keyword">if</span> (mp.get(str.charAt(i)) == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> i;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h3 id="3-数组中重复的数字"><a href="#3-数组中重复的数字" class="headerlink" title="3.数组中重复的数字"></a>3.数组中重复的数字</h3><p><strong>题目</strong><br><a href="https:</summary>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
<category term="剑指offer刷题" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer%E5%88%B7%E9%A2%98/"/>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>链表</title>
<link href="https://huanqgingyu.top/2022/01/20/%E9%93%BE%E8%A1%A8/"/>
<id>https://huanqgingyu.top/2022/01/20/%E9%93%BE%E8%A1%A8/</id>
<published>2022-01-19T16:00:00.000Z</published>
<updated>2022-08-28T15:21:24.172Z</updated>
<content type="html"><![CDATA[<h3 id="6-从尾到头打印链表"><a href="#6-从尾到头打印链表" class="headerlink" title="6.从尾到头打印链表"></a>6.从尾到头打印链表</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035">牛客网</a><br><strong>思路</strong><br>这里提供两种解题方法:</p><ol><li>创建一个额外的栈,然后从头到尾遍历链表,遍历的时候将遍历到的链表值存入栈中,最后再将栈中元素取出就能完成相反的顺序打印链表。</li><li>采用头插法。就是定义一个头结点,然后遍历链表,采用头插法将原链表拆除并从头部逐个插入到新链表中,遍历新链表就能得到相反顺序的元素。<br>这里实现头插法:</li></ol><p><strong>Java</strong><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"><span class="function"><span class="keyword">public</span> ArrayList<Integer> <span class="title">printListFromTailToHead</span> <span class="params">(ListNode listNode)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化头结点和p指针</span></span><br><span class="line">ListNode head = <span class="keyword">new</span> ListNode(-<span class="number">1</span>);</span><br><span class="line">ListNode p = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 遍历原链表并构建新链表</span></span><br><span class="line"><span class="keyword">while</span> (listNode != <span class="keyword">null</span>) {</span><br><span class="line"></span><br><span class="line"><span class="comment">// 头插法构建新链表</span></span><br><span class="line">p = listNode.next;</span><br><span class="line">listNode.next = head.next;</span><br><span class="line">head.next = listNode;</span><br><span class="line">listNode = p;</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">ArrayList<Integer> printList = <span class="keyword">new</span> ArrayList<Integer>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 头指针指向新链表第一个值</span></span><br><span class="line">head = head.next;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 向列表中添加新链表各节点的值</span></span><br><span class="line"><span class="keyword">while</span> (head != <span class="keyword">null</span>) {</span><br><span class="line">printList.add(head.val);</span><br><span class="line">head = head.next;</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">return</span> printList;</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p><p><strong>Python3</strong><br><figure class="highlight python"><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="class"><span class="keyword">class</span> <span class="title">Solution</span>:</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">printListFromTailToHead</span>(<span class="params">self, listNode: ListNode</span>) -> List[int]:</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化头结点</span></span><br><span class="line">head = ListNode(<span class="number">-1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> listNode <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 头插法创建新链表</span></span><br><span class="line">p = listNode.next</span><br><span class="line">listNode.next = head.next</span><br><span class="line">head.next = listNode</span><br><span class="line">listNode = p</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化输出列表</span></span><br><span class="line">printList = []</span><br><span class="line"></span><br><span class="line"><span class="comment"># 头结点指向新链表第一个值</span></span><br><span class="line">head = head.next</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将新链表的值加入到列表中</span></span><br><span class="line"><span class="keyword">while</span> head <span class="keyword">is</span> <span class="keyword">not</span> <span class="literal">None</span>:</span><br><span class="line">printList.append(head.val)</span><br><span class="line">head = head.next</span><br><span class="line"></span><br><span class="line"><span class="comment"># 返回列表</span></span><br><span class="line"><span class="keyword">return</span> printList</span><br></pre></td></tr></table></figure></p><h3 id="18-2删除链表中重复的节点"><a href="#18-2删除链表中重复的节点" class="headerlink" title="18.2删除链表中重复的节点"></a>18.2删除链表中重复的节点</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/fc533c45b73a41b0b44ccba763f866ef">牛客网</a><br><strong>思路</strong><br><strong>方法:直接比较删除</strong><br>这是一个升序链表,重复的节点都连在一起,我们就可以很轻易地比较到重复的节点,然后将所有的连续相同的节点都跳过,连接不相同的第一个节点。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),其中n为链表节点数,只有一次遍历</li><li>空间复杂度:O(1),只开辟了临时指针,常数级空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">deleteDuplication</span><span class="params">(ListNode pHead)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 空链表</span></span><br><span class="line"> <span class="keyword">if</span> (pHead == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 在链表前加一个表头</span></span><br><span class="line"> ListNode res = <span class="keyword">new</span> ListNode(<span class="number">0</span>);</span><br><span class="line"> res.next = pHead;</span><br><span class="line"> </span><br><span class="line"> ListNode q = res;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> ((q.next != <span class="keyword">null</span>) && (q.next.next != <span class="keyword">null</span>)) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 遇到相邻两个节点值相同</span></span><br><span class="line"> <span class="keyword">if</span> (q.next.val == q.next.next.val) {</span><br><span class="line"> <span class="keyword">int</span> temp = q.next.val;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 将所有相同的都跳过</span></span><br><span class="line"> <span class="keyword">while</span> ((q.next != <span class="keyword">null</span>) && (q.next.val == temp)) {</span><br><span class="line"> q.next = q.next.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> q = q.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 class="comment">// 返回时去掉表头</span></span><br><span class="line"> <span class="keyword">return</span> res.next;</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="22-链表中倒数第K个节点"><a href="#22-链表中倒数第K个节点" class="headerlink" title="22.链表中倒数第K个节点"></a>22.链表中倒数第K个节点</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/886370fe658f41b498d40fb34ae76ff9">牛客网</a><br><strong>思路</strong><br><strong>方法:快慢双指针</strong><br>第一个指针先移动k步,然后第二个指针再从头开始,这个时候这两个指针同时移动,当第一个指针到链表的末尾的时候,返回第二个指针即可。</p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),总共遍历n个链表元素</li><li>空间复杂度:O(1),常数级指针变量,无额外辅助空间使用</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">FindKthToTail</span> <span class="params">(ListNode pHead, <span class="keyword">int</span> k)</span> </span>{</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化快慢指针</span></span><br><span class="line"> ListNode fast = pHead;</span><br><span class="line"> ListNode slow = pHead;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 快指针先行k步</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>;i < k;i++) {</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (fast != <span class="keyword">null</span>) {</span><br><span class="line"> fast = fast.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 达不到k步说明链表过短,没有倒数k</span></span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</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="comment">// 快慢指针同步,快指针先到底,慢指针指向倒数第k个</span></span><br><span class="line"> <span class="keyword">while</span> (fast != <span class="keyword">null</span>) {</span><br><span class="line"> fast = fast.next;</span><br><span class="line"> slow = slow.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> slow;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="23-链表中环的入口节点"><a href="#23-链表中环的入口节点" class="headerlink" title="23.链表中环的入口节点"></a>23.链表中环的入口节点</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/253d2c59ec3e4bc68da16833f79a38e4">牛客网</a><br><strong>思路</strong><br><strong>方法:双指针</strong><br>根据题干,不说别的,我们能发现这道题需要完成两个任务:</p><ol><li>判断链表是否有环。</li><li>在有环的链表中找到环的入口。</li></ol><p>那我们现在假定已经是一个有环的链表了,那么这个链表中怎么找到环的入口呢?在慢指针进入链表环之前,快指针已经进入了环,且在里面循环,这才能在慢指针进入环之后,快指针追到了慢指针,不妨假设快指针在环中走了n圈,慢指针在环中走了m圈,它们才相遇,而进入环之前的距离为x,环入口到相遇点的距离为y,相遇点到环入口的距离为z。快指针一共走了x+n(y+z)+y步,慢指针一共走了x+m(y+z)+y,这个时候快指针走的倍数是慢指针的两倍,则x+n(y+z)+y=2(x+m(y+z)+y),这时候x+y=(n−2m)(y+z),因为环的大小是y+z,说明从链表头经过环入口到达相遇地方经过的距离等于整数倍环的大小:那我们从头开始遍历到相遇位置,和从相遇位置开始在环中遍历,会使用相同的步数,而双方最后都会经过入口到相遇位置这y个节点,那说明这y个节点它们就是重叠遍历的,那它们从入口位置就相遇了,这我们不就找到了吗?</p><p><strong>时间复杂度分析:</strong></p><ul><li>时间复杂度:O(n),最坏情况下遍历链表两次</li><li>空间复杂度:O(1),使用了常数个指针,没有额外辅助空间</li></ul><p><strong>注:</strong>以上描述了以fast比slow超过一个环为例描述了这个过程,经过推算,超过多个环和超过一个环的规律是一样的都是当fast重头开始,slow从相遇点开始顺时针遍历,两者以相同的速度会在环口相遇。</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">EntryNodeOfLoop</span><span class="params">(ListNode pHead)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (pHead == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 定义快慢指针</span></span><br><span class="line"> ListNode fast = pHead;</span><br><span class="line"> ListNode slow = pHead;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> ((fast != <span class="keyword">null</span>) && (fast.next != <span class="keyword">null</span>)) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 快指针是慢指针的两倍速度</span></span><br><span class="line"> fast = fast.next.next;</span><br><span class="line"> slow = slow.next;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 记录快慢指针第一次相遇的结点</span></span><br><span class="line"> <span class="keyword">if</span> (fast == slow) {</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 若是快指针指向null,则不存在环</span></span><br><span class="line"> <span class="keyword">if</span> ((fast == <span class="keyword">null</span>) || (fast.next == <span class="keyword">null</span>)) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 重新指向链表头部</span></span><br><span class="line"> fast = pHead;</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 与第一次相遇的结点相同速度出发,相遇结点为入口结点</span></span><br><span class="line"> <span class="keyword">while</span> (fast != slow) {</span><br><span class="line"> fast = fast.next;</span><br><span class="line"> slow = slow.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> fast;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="24-反转链表"><a href="#24-反转链表" class="headerlink" title="24.反转链表"></a>24.反转链表</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/75e878df47f24fdc9dc3e400ec6058ca">牛客网</a><br><strong>思路</strong><br><strong>方法:头插法</strong></p><p><strong>时间复杂度分析:</strong></p><ul><li>时间复杂度:O(n),遍历一次链表</li><li>空间复杂度:O(1)</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">ReverseList</span><span class="params">(ListNode head)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化头结点</span></span><br><span class="line"> ListNode newList = <span class="keyword">new</span> ListNode(-<span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> (head != <span class="keyword">null</span>) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 头插法新建链表操作</span></span><br><span class="line"> ListNode next = head.next;</span><br><span class="line"> head.next = newList.next;</span><br><span class="line"> newList.next = head;</span><br><span class="line"> head = next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> newList.next;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="25-合并两个排序的链表"><a href="#25-合并两个排序的链表" class="headerlink" title="25.合并两个排序的链表"></a>25.合并两个排序的链表</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/d8b6b4358f774294a89de2a6ac4d9337">牛客网</a><br><strong>思路</strong><br><strong>方法:迭代归并</strong></p><p><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(m+n),m,n分别为两个单链表的长度</li><li>空间复杂度:O(1)</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">Merge</span><span class="params">(ListNode list1,ListNode list2)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化头结点</span></span><br><span class="line"> ListNode head = <span class="keyword">new</span> ListNode(-<span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化遍历指针</span></span><br><span class="line"> ListNode cur = head;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> ((list1 != <span class="keyword">null</span>) && (list2 != <span class="keyword">null</span>)) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 遍历指针的下个值指向值更小节点,并且对应链表指针后移</span></span><br><span class="line"> <span class="keyword">if</span> (list1.val <= list2.val) {</span><br><span class="line"> cur.next = list1;</span><br><span class="line"> list1 = list1.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> cur.next = list2;</span><br><span class="line"> list2 = list2.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 遍历指针后移</span></span><br><span class="line"> cur = cur.next;</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">if</span> (list1 == <span class="keyword">null</span>) {</span><br><span class="line"> cur.next = list2;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (list2 == <span class="keyword">null</span>) {</span><br><span class="line"> cur.next = list1;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> head.next;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="35-复杂链表的复制"><a href="#35-复杂链表的复制" class="headerlink" title="35.复杂链表的复制"></a>35.复杂链表的复制</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/f836b2c43afc4b35ad6adc41ec941dba">牛客网</a><br><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(n),其中n为链表长度,遍历三次链表,第一次遍历n个节点,第二次、第三次遍历2n个节点</li><li>空间复杂度:O(1),常数级变量,无额外辅助空间</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> RandomListNode <span class="title">Clone</span><span class="params">(RandomListNode pHead)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (pHead == <span class="keyword">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 插入新节点并串联</span></span><br><span class="line"> RandomListNode cur = pHead;</span><br><span class="line"> <span class="keyword">while</span> (cur != <span class="keyword">null</span>) {</span><br><span class="line"> RandomListNode clone = <span class="keyword">new</span> RandomListNode(cur.label);</span><br><span class="line"> clone.next = cur.next;</span><br><span class="line"> cur.next = clone;</span><br><span class="line"> cur = clone.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 建立random链接</span></span><br><span class="line"> cur = pHead;</span><br><span class="line"> <span class="keyword">while</span> (cur != <span class="keyword">null</span>) {</span><br><span class="line"> RandomListNode clone = cur.next;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span> (cur.random != <span class="keyword">null</span>) {</span><br><span class="line"> clone.random = cur.random.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> cur = clone.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 拆分</span></span><br><span class="line"> cur = pHead;</span><br><span class="line"> RandomListNode pCloneHead = cur.next;</span><br><span class="line"> <span class="keyword">while</span> (cur.next != <span class="keyword">null</span>) {</span><br><span class="line"> </span><br><span class="line"> RandomListNode next = cur.next;</span><br><span class="line"> cur.next = next.next;</span><br><span class="line"> cur = next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> pCloneHead;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="52-两个链表的第一个公共节点"><a href="#52-两个链表的第一个公共节点" class="headerlink" title="52.两个链表的第一个公共节点"></a>52.两个链表的第一个公共节点</h3><p><strong>题目</strong><br><a href="https://www.nowcoder.com/practice/6ab1d9a29e88450685099d45c9e31e46">牛客网</a><br><strong>思路:</strong><br>设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。<br>当访问链表 A 的指针访问到链表尾部时,令它从链表 B 的头部重新开始访问链表 B;同样地,当访问链表 B 的指针访问到链表尾部时,令它从链表 A 的头部重新开始访问链表 A。这样就能控制访问 A 和 B 两个链表的指针能同时访问到交点。<br><strong>复杂度分析:</strong></p><ul><li>时间复杂度:O(m+n), m,n分别为链表A,B的长度,最坏情况下,公共结点为最后一个,需要遍历m+n个结点</li><li>空间复杂度:O(1)</li></ul><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Solution</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">FindFirstCommonNode</span><span class="params">(ListNode pHead1, ListNode pHead2)</span> </span>{</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化两个用于遍历的头结点</span></span><br><span class="line"> ListNode l1 = pHead1;</span><br><span class="line"> ListNode l2 = pHead2;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">while</span> (l1 != l2) {</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 判断l1的遍历方向</span></span><br><span class="line"> <span class="keyword">if</span> (l1 == <span class="keyword">null</span>) {</span><br><span class="line"> l1 = pHead2;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> l1 = l1.next;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 判断l2的遍历方向</span></span><br><span class="line"> <span class="keyword">if</span> (l2 == <span class="keyword">null</span>) {</span><br><span class="line"> l2 = pHead1;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">else</span> {</span><br><span class="line"> l2 = l2.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 class="keyword">return</span> l1;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h3 id="6-从尾到头打印链表"><a href="#6-从尾到头打印链表" class="headerlink" title="6.从尾到头打印链表"></a>6.从尾到头打印链表</h3><p><strong>题目</strong><br><a href="https:</summary>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
<category term="剑指offer刷题" scheme="https://huanqgingyu.top/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/%E5%89%91%E6%8C%87offer%E5%88%B7%E9%A2%98/"/>
<category term="数据结构与算法" scheme="https://huanqgingyu.top/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/"/>
</entry>
<entry>
<title>Java实现死锁</title>
<link href="https://huanqgingyu.top/2022/01/15/Java%E5%AE%9E%E7%8E%B0%E6%AD%BB%E9%94%81/"/>
<id>https://huanqgingyu.top/2022/01/15/Java%E5%AE%9E%E7%8E%B0%E6%AD%BB%E9%94%81/</id>
<published>2022-01-14T16:00:00.000Z</published>
<updated>2022-08-28T15:13:10.471Z</updated>
<content type="html"><![CDATA[<p><strong>java实现死锁</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeadLockDemo</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Object resource1 = <span class="keyword">new</span> Object();</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Object resource2 = <span class="keyword">new</span> Object();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">synchronized</span> (resource1) {</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource1"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 线程休眠,保证线程2先获得资源2</span></span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"waiting get resource2"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">synchronized</span> (resource2) {</span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource2"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T1"</span>).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">synchronized</span> (resource2) {</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource2"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 线程休眠,保证线程1先获得资源1</span></span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"waiting get resource1"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">synchronized</span> (resource1) {</span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource1"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T2"</span>).start();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>程序结果</strong></p><figure class="highlight plain"><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">Thread[T1,5,main]get resource1</span><br><span class="line">Thread[T2,5,main]get resource2</span><br><span class="line">Thread[T1,5,main]waiting get resource2</span><br><span class="line">Thread[T2,5,main]waiting get resource1</span><br></pre></td></tr></table></figure><p><strong>死锁原因</strong></p><p>T1和T2各自同时占有resource1和resource2,而又分别要使用resource2和resource1,因此发生死锁。</p><p><strong>解决方法</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeadLockDemo</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Object resource1 = <span class="keyword">new</span> Object();</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Object resource2 = <span class="keyword">new</span> Object();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>{</span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">synchronized</span> (resource1) {</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource1"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"waiting get resource2"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">synchronized</span> (resource2) {</span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource2"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T1"</span>).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">synchronized</span> (resource1) {</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource2"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"waiting get resource1"</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">synchronized</span> (resource2) {</span><br><span class="line"> System.out.println(Thread.currentThread() + <span class="string">"get resource1"</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T2"</span>).start();</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>程序结果</strong></p><figure class="highlight plain"><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">Thread[T1,5,main]get resource1</span><br><span class="line">Thread[T1,5,main]waiting get resource2</span><br><span class="line">Thread[T1,5,main]get resource2</span><br><span class="line">Thread[T2,5,main]get resource2</span><br><span class="line">Thread[T2,5,main]waiting get resource1</span><br><span class="line">Thread[T2,5,main]get resource1</span><br></pre></td></tr></table></figure><p><strong>分析</strong></p><p>可以让两个线程按照顺序依次使用resource1和resource2</p>]]></content>
<summary type="html"><p><strong>java实现死锁</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span clas</summary>
<category term="Java基础" scheme="https://huanqgingyu.top/categories/Java%E5%9F%BA%E7%A1%80/"/>
<category term="Java" scheme="https://huanqgingyu.top/tags/Java/"/>
<category term="多线程" scheme="https://huanqgingyu.top/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java实现生产者消费者问题</title>
<link href="https://huanqgingyu.top/2022/01/15/Java%E5%AE%9E%E7%8E%B0%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98/"/>
<id>https://huanqgingyu.top/2022/01/15/Java%E5%AE%9E%E7%8E%B0%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85%E9%97%AE%E9%A2%98/</id>
<published>2022-01-14T16:00:00.000Z</published>
<updated>2022-08-28T15:15:52.268Z</updated>
<content type="html"><![CDATA[<p><strong>生产者消费者synchronized版实现</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProducerAndConsumer</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"></span><br><span class="line"> Resources re = <span class="keyword">new</span> Resources();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i <= <span class="number">10</span>;i++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 生产资源</span></span><br><span class="line"> re.produce();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T1"</span>).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i <= <span class="number">10</span>;i++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 消费资源</span></span><br><span class="line"> re.consume();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T2"</span>).start();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Resources</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 初始化资源</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> resource = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">produce</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当还有资源的时候等待</span></span><br><span class="line"> <span class="keyword">while</span> (resource != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 生产资源的逻辑</span></span><br><span class="line"> resource++;</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + resource);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通知</span></span><br><span class="line"> <span class="keyword">this</span>.notifyAll();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">consume</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当没有资源的时候等待</span></span><br><span class="line"> <span class="keyword">while</span> (resource == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">this</span>.wait();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 消耗资源的逻辑</span></span><br><span class="line"> resource--;</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + resource);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通知</span></span><br><span class="line"> <span class="keyword">this</span>.notifyAll();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><strong>为什么使用 while 而不是使用 if ?</strong></p><p>因为多线程中可能存在虚假唤醒问题,如果用 if 那当遇到虚假唤醒时直接停止 wait 进入到下一步程序中,但如果用 while 遇到虚假唤醒时程序会再次对 resource 进行判断资源条件是否满足以防止程序错误地进入下一步执行。</p><p><strong>生产者消费者JUC版实现</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Condition; </span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.Lock; </span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.ReentrantLock;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ProducerAndConsumer</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception </span>{</span><br><span class="line"></span><br><span class="line"> Resources re = <span class="keyword">new</span> Resources();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i <= <span class="number">10</span>;i++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 生产资源</span></span><br><span class="line"> re.produce();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T1"</span>).start();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>;i <= <span class="number">10</span>;i++) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 消费资源</span></span><br><span class="line"> re.consume();</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"T2"</span>).start();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Resources</span> </span>{</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 初始化资源</span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">int</span> resource = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 初始化锁</span></span><br><span class="line"> <span class="keyword">private</span> Lock lock = <span class="keyword">new</span> ReentrantLock(); </span><br><span class="line"> <span class="keyword">private</span> Condition condition = lock.newCondition();</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">produce</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"></span><br><span class="line"> lock.lock();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当还有资源的时候等待</span></span><br><span class="line"> <span class="keyword">while</span> (resource != <span class="number">0</span>) {</span><br><span class="line"> condition.await();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 生产资源的逻辑</span></span><br><span class="line"> resource++;</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + resource);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通知</span></span><br><span class="line"> condition.signalAll();</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</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">public</span> <span class="keyword">void</span> <span class="title">consume</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>{</span><br><span class="line"></span><br><span class="line"> lock.lock();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当还有资源的时候等待</span></span><br><span class="line"> <span class="keyword">while</span> (resource == <span class="number">0</span>) {</span><br><span class="line"> condition.await();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 生产资源的逻辑</span></span><br><span class="line"> resource--;</span><br><span class="line"></span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">":"</span> + resource);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 通知</span></span><br><span class="line"> condition.signalAll();</span><br><span class="line"></span><br><span class="line"> } <span class="keyword">catch</span> (Exception e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> } <span class="keyword">finally</span> {</span><br><span class="line"> lock.unlock();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p><strong>生产者消费者synchronized版实现</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><</summary>
<category term="Java基础" scheme="https://huanqgingyu.top/categories/Java%E5%9F%BA%E7%A1%80/"/>
<category term="Java" scheme="https://huanqgingyu.top/tags/Java/"/>
<category term="多线程" scheme="https://huanqgingyu.top/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java刷题常用方法和功能</title>
<link href="https://huanqgingyu.top/2021/09/02/Java%E5%88%B7%E9%A2%98%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E5%92%8C%E5%8A%9F%E8%83%BD/"/>
<id>https://huanqgingyu.top/2021/09/02/Java%E5%88%B7%E9%A2%98%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E5%92%8C%E5%8A%9F%E8%83%BD/</id>
<published>2021-09-01T16:00:00.000Z</published>
<updated>2022-08-11T14:05:30.649Z</updated>
<content type="html"><![CDATA[<p>用java刷算法题的时候会用到一些常用的方法,这些方法包括字符串、数组、集合的方法。<br><strong>Java获取数组的长度</strong><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">String[][] data = <span class="keyword">new</span> String[<span class="number">2</span>][<span class="number">5</span>];</span><br><span class="line">System.out.println(<span class="string">"第一维数组长度: "</span> + data.length);</span><br><span class="line">System.out.println(<span class="string">"第二维数组长度: "</span> + data[<span class="number">0</span>].length);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 字符数组转成字符串</span></span><br><span class="line"><span class="keyword">char</span>[] ch = {<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>};</span><br><span class="line">String str = <span class="keyword">new</span> String(ch);</span><br></pre></td></tr></table></figure><br><strong>String类常用方法</strong></p><ul><li>int length() 返回字符串长度</li><li>char charAt(int index) 返回指定下标位置的字符</li><li>char[] toCharArray() 将字符串转换为字符数组</li><li>String[] split(String regex) 将字符串按照分割符分割成字符串数组</li><li>Boolean equals(String str) 判断字符串是否相等</li><li>String substring(int beginIndex, int endIndex) 截取字符串(不包括endIndex)</li><li>String substring(int beginIndex) 截取字符串(包括beginIndex)<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></pre></td><td class="code"><pre><span class="line">String str = <span class="string">"Hello world!"</span>;</span><br><span class="line">String str1 = <span class="string">"yun"</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">int</span> l = str.length(); <span class="comment">// l:12</span></span><br><span class="line"><span class="keyword">char</span> c = str.charAt(<span class="number">1</span>); <span class="comment">// c:'e'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">char</span>[] cArray = str.toCharArray();</span><br><span class="line"><span class="comment">// cArray:['H','e','l','l','o',' ','w','o','r','l','d','!']</span></span><br><span class="line"></span><br><span class="line">String[] arr = str.split(<span class="string">" "</span>);</span><br><span class="line"><span class="comment">// arr:["Hello","world!"]</span></span><br><span class="line"></span><br><span class="line">Boolean eq = str1.equals(str); <span class="comment">// eq:false</span></span><br><span class="line">String str2 = str.substring(<span class="number">0</span>, <span class="number">2</span>); <span class="comment">// str2:"He"</span></span><br><span class="line">String str3 = str.substring(<span class="number">2</span>); <span class="comment">// str3:"llo world!"</span></span><br></pre></td></tr></table></figure><strong>StringBuffer</strong><br>StringBuffer 类和 String 类最大的区别在于它的内容和长度都是可以改变的。StringBuffer 类似一个字符容器,当在其中添加或删除字符时,所操作的都是这个字符容器,因此并不会产生新的 StringBuffer 对象。所以当要处理字符串增、删、改操作时一般都是将String类对象转换成StringBuffer 类对象然后再转换成字符串作为返回值。<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="comment">// 初始化StringBuffer 类对象(第一种)</span></span><br><span class="line">StringBuffer strBuffer = <span class="keyword">new</span> StringBuffer(<span class="string">"abc"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 初始化StringBuffer 类对象(第二种)</span></span><br><span class="line">String str = <span class="string">"abc"</span>;</span><br><span class="line">StringBuffer strBuffer = <span class="keyword">new</span> StringBuffer(str);</span><br></pre></td></tr></table></figure><strong>StringBuffer类常用方法</strong></li><li>int length() 返回字符串实际长度 </li><li>char charAt(int index) 返回指定下标位置的字符</li><li>StringBuffer append(char c) 添加字符到StringBuffer对象中末尾</li><li>StringBuffer append(String str) 添加字符串到StringBuffer对象中末尾</li><li>StringBuffer insert(int offset, String str) 在StringBuffer对象中的offset位置插入字符串str</li><li>StringBuffer deleteCharAt(int index) 移除StringBuffer对象中指定位置的字符</li><li>StringBuffer delete(int start, int end) 删除StringBuffer对象中指定范围的字符或字符串,区间[start,end)</li><li>StringBuffer replace(int start, int end, String s) 将StringBuffer对象中指定范围的字符或字符串用新的字符串s进行替换,区间[start, end)</li><li>void setCharAt(int index, char ch) 修改指定位置index处的字符</li><li>String toString() 返回StringBuffer缓冲区中的字符串对象</li><li>StringBuffer reverse() 将此StringBuffer对象用其反转形式取代 <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></pre></td><td class="code"><pre><span class="line">String str = <span class="string">"Hello world!"</span>;</span><br><span class="line">StringBuffer strBuffer = <span class="keyword">new</span> StringBuffer(str);</span><br><span class="line"></span><br><span class="line">strBuffer.insert(<span class="number">1</span>, <span class="string">"ttt"</span>); <span class="comment">// strBuffer:"Htttello world!"</span></span><br><span class="line">strBuffer.delete(<span class="number">1</span>, <span class="number">4</span>); <span class="comment">// strBuffer:"Hello world!"</span></span><br><span class="line">strBuffer.deleteCharAt(<span class="number">1</span>); <span class="comment">// strBuffer:"Hllo world!"</span></span><br><span class="line">strBuffer.setCharAt(<span class="number">1</span>, <span class="string">'t'</span>); <span class="comment">// strBuffer:"Htlo world!"</span></span><br><span class="line">strBuffer.replace(<span class="number">0</span>, <span class="number">2</span>, <span class="string">"Hel"</span>); <span class="comment">// strBuffer:"Hello world!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/****************测试append()***************/</span></span><br><span class="line">StringBuffer sBuffer = <span class="keyword">new</span> StringBuffer(<span class="string">"aaa"</span>); <span class="comment">// sBuffer:aaa</span></span><br><span class="line">sBuffer.append(<span class="string">'b'</span>); <span class="comment">// sBuffer:aaab</span></span><br><span class="line">sBuffer.append(<span class="string">"ccc"</span>); <span class="comment">// sBuffer:aaabccc</span></span><br><span class="line">String str = <span class="string">"ddd"</span>;</span><br><span class="line">sBuffer.append(str); <span class="comment">// sBuffer:aaabcccddd</span></span><br></pre></td></tr></table></figure><strong>注:</strong>java的String和StringBuffer两个类来封装对字符串的各种操作,存放于java.lang包中,java.lang 包下的类不用导包,在运行时,java.lang包下的类默认导入。<br><strong>List容器常用方法</strong></li><li>add(Object element) 向列表的尾部添加指定的元素</li><li>add(int index, Object element) 在列表的指定位置插入指定元素</li><li>size() 返回列表中的元素个数</li><li>get(int index) 返回列表中指定位置的元素,index从0开始</li><li>set(int i, Object element) 将索引i位置元素替换为元素element并返回被替换的元素</li><li>clear() 从列表中移除所有元素</li><li>isEmpty() 判断列表是否包含元素,不包含元素则返回 true,否则返回false</li><li>remove(int index) 移除列表中指定位置的元素,并返回被删元素</li></ul><p>将数组转成list集合:<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">String[] array = { <span class="string">"a"</span>, <span class="string">"b"</span>, <span class="string">"c"</span>};</span><br><span class="line">ArrayList<String> arrayList = <span class="keyword">new</span> ArrayList<String>(Arrays.asList(array));</span><br></pre></td></tr></table></figure></p><p><strong>Stack容器常用方法</strong></p><ul><li>Boolean empty() 若栈为空则返回true,否则返回false</li><li>E peek() 查看当前的栈顶元素</li><li>E pop() 删除当前的栈顶元素,并将其作为返回值返回</li><li>E push(E e) 添加该数据到栈顶</li></ul><p><strong>PriorityQueue容器常用方法</strong></p><ul><li>offer(E e) 将指定元素插入此优先队列</li><li>poll() 移除并返回堆顶元素</li><li>peek() 获取堆顶元素</li><li>size() 返回元素个数</li><li>clear() 清空</li></ul><p>offer()方法和add()方法的区别:两者都是往队列尾部插入元素,不同的是,当超出队列界限的时候,add()方法是抛出异常让你处理,而offer()方法是直接返回false</p><p>PriorityQueue初始化时默认创建的是小根堆,所以创建大根堆的方法为:<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">PriorityQueue<Integer> q = <span class="keyword">new</span> PriorityQueue<>((o1, o2) -> (o2 - o1));</span><br></pre></td></tr></table></figure></p><p><strong>ArrayDeque容器常用方法</strong></p><ul><li>offer() - 将指定的元素插入ArrayDeque双端队列的末尾</li><li>offerFirst() - 在ArrayDeque双端队列的开始处插入指定的元素</li><li>offerLast() - 将指定的元素插入ArrayDeque双端队列的末尾</li><li>getFirst() - 返回ArrayDeque双端队列的第一个元素</li><li>getLast() - 返回ArrayDeque双端队列的最后一个元素</li><li>peek() - 返回ArrayDeque双端队列的第一个元素</li><li>peekFirst() - 返回ArrayDeque双端队列的第一个元素(等效于peek())</li><li>peekLast() - 返回ArrayDeque双端队列的最后一个元素</li><li>poll() - 返回并删除ArrayDeque双端队列的第一个元素</li><li>pollFirst() - 返回并删除ArrayDeque双端队列的第一个元素(等效于poll())</li><li>pollLast() - 返回并删除ArrayDeque双端队列的最后一个元素</li><li>size() - 返回ArrayDeque双端队列的长度</li></ul><p><strong>Map容器常用方法</strong></p><ul><li>V get(Object key) 返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回null</li><li>V put(K key, V value) 在容器中新增键值对,若存在相同的键则覆盖原来的值,返回原来的值</li><li>V remove(Object key) 如果存在一个键的映射关系,则将其从此映射中移除,返回移除的值,不存在则返回null</li><li>Boolean containsKey(Object key) 如果 Map包含指定键的映射,则返回 true</li><li>V getOrDefault(Object key, V defaultValue) 当Map集合中有这个key时,就返回key对应的值,如果没有就返回默认值defaultValue。</li><li>Set<K> keySet() 返回此映射中包含的键的Set集合</K></li></ul><p>Map容器的迭代操作:<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">Map<String, String> map = <span class="keyword">new</span> HashMap<String, String>();</span><br><span class="line">map.put(<span class="string">"一"</span>, <span class="string">"one"</span>);</span><br><span class="line">map.put(<span class="string">"二"</span>, <span class="string">"two"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取key的set集合</span></span><br><span class="line">Set<String> keySet = map.keySet();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建迭代器</span></span><br><span class="line">Iterator<String> it = keySet.iterator();</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(it.hasNext()) {</span><br><span class="line"> <span class="comment">//获取每一个key</span></span><br><span class="line"> String key = it.next();</span><br><span class="line"> <span class="comment">//通过key获取对应的value</span></span><br><span class="line"> String value = map.get(key);</span><br><span class="line"> System.out.println(key+<span class="string">"="</span>+value);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html"><p>用java刷算法题的时候会用到一些常用的方法,这些方法包括字符串、数组、集合的方法。<br><strong>Java获取数组的长度</strong><br><figure class="highlight java"><table><tr><td class="gutter</summary>
<category term="Java基础" scheme="https://huanqgingyu.top/categories/Java%E5%9F%BA%E7%A1%80/"/>
<category term="java" scheme="https://huanqgingyu.top/tags/java/"/>
</entry>
<entry>
<title>python列表常用方法和功能</title>
<link href="https://huanqgingyu.top/2021/06/05/python%E5%88%97%E8%A1%A8%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E5%92%8C%E5%8A%9F%E8%83%BD/"/>
<id>https://huanqgingyu.top/2021/06/05/python%E5%88%97%E8%A1%A8%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95%E5%92%8C%E5%8A%9F%E8%83%BD/</id>
<published>2021-06-04T16:00:00.000Z</published>
<updated>2022-08-11T14:04:28.519Z</updated>
<content type="html"><![CDATA[<p>python的列表list是非常好用的容易,在刷算法题时用起来比较方便。在这里介绍python列表中的常用方法和常用的功能(主要是刷算法题时常用到)。<br><strong>list常用方法</strong></p><ul><li>append(data) 在列表<strong>末尾</strong>追加数据。</li><li>insert(index, data) 在列表中指定位置加入数据(注:index是下标号,data是所要插入的 数据)。</li><li>pop() 删除列表<strong>末尾</strong>的元素。</li><li>pop(index) 删除列表中指定位置的元素。</li><li>index(data) 返回指定元素在列表中的索引号。</li><li>remove(data) 删除指定的元素。</li><li>clear() 清空列表 。</li><li>reverse() 将列表里的元素进行前后顺序调换(翻转)。</li><li>extend(list) 拼接两个列表(将括号中的list拼接到原列表的后面)。<figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line">testList = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]</span><br><span class="line"></span><br><span class="line">testList.append(<span class="string">'d'</span>)</span><br><span class="line"><span class="comment"># testList:['a', 'b', 'c', 'd']</span></span><br><span class="line"></span><br><span class="line">testList.insert(<span class="number">0</span>, <span class="string">'e'</span>)</span><br><span class="line"><span class="comment"># testList:['e', 'a', 'b', 'c', 'd']</span></span><br><span class="line"></span><br><span class="line">testList.pop()</span><br><span class="line"><span class="comment"># testList:['e', 'a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line">testList.pop(<span class="number">0</span>)</span><br><span class="line"><span class="comment"># testList:['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line">bIndex = testList.index(<span class="string">'b'</span>)</span><br><span class="line"><span class="comment"># bIndex:1</span></span><br><span class="line"></span><br><span class="line">testList.reverse()</span><br><span class="line"><span class="comment"># testList:['c', 'b', 'a']</span></span><br><span class="line">testList.reverse()</span><br><span class="line"><span class="comment"># testList:['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line">testList.remove(<span class="string">'b'</span>)</span><br><span class="line"><span class="comment"># testList:['a', 'c']</span></span><br><span class="line"></span><br><span class="line">testList.clear()</span><br><span class="line"><span class="comment"># testList:[]</span></span><br><span class="line"></span><br><span class="line">testList1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]</span><br><span class="line">testList2 = [<span class="string">'d'</span>, <span class="string">'e'</span>, <span class="string">'f'</span>]</span><br><span class="line"></span><br><span class="line">testList1.extend(testList2)</span><br><span class="line"><span class="comment"># testList1:['a', 'b', 'c', 'd', 'e', 'f']</span></span><br><span class="line"><span class="comment"># testList2:['d', 'e', 'f']</span></span><br></pre></td></tr></table></figure><strong>list常用功能</strong><figure class="highlight python"><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">testList = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取列表的长度</span></span><br><span class="line">len(testList)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 遍历列表</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> testList:</span><br><span class="line">print(i)</span><br></pre></td></tr></table></figure><strong>list与字符串的转换</strong><br>在做算法题中尝尝要将字符串与列表相互转换来处理字符串。</li></ul><ol><li>list列表转为str字符串<br>当list中存的是字符串的时候,一般是通过.join()函数去转换:<figure class="highlight python"><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">testList = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>]</span><br><span class="line"><span class="comment"># testList:['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line">testStr1 = <span class="string">''</span>.join(testList)</span><br><span class="line"><span class="comment"># testStr1:abc</span></span><br><span class="line"></span><br><span class="line">testStr2 = <span class="string">','</span>.join(testList)</span><br><span class="line"><span class="comment"># testStr2:a,b,c</span></span><br></pre></td></tr></table></figure>‘,’.join(testList)表示将列表中的字符串用’,’拼接成一个字符串’’中间可以没有字符,代表不用任何字符将字符串拼接。</li><li>str转为list列表<figure class="highlight python"><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">testStr1 = <span class="string">'abc'</span></span><br><span class="line">testStr2 = <span class="string">'a b c'</span></span><br><span class="line">testStr3 = <span class="string">'a,b,c'</span></span><br><span class="line"></span><br><span class="line">testList1 = list(testStr1)</span><br><span class="line"><span class="comment"># testList1:['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line">testList2 = testStr2.split()</span><br><span class="line"><span class="comment"># testList2:['a', 'b', 'c']</span></span><br><span class="line"></span><br><span class="line">testList3 = testStr3.split(<span class="string">','</span>)</span><br><span class="line"><span class="comment"># testList3:['a', 'b', 'c']</span></span><br></pre></td></tr></table></figure>python用list()函数将字符串按照每个字符为一个元素组成一个字符列表。<br>python字符串的split()方法将字符串按照所给字符(或者字符串)分割组成列表,其中里面可以不写分割字符则默认按照空格字符分割字符串。</li></ol>]]></content>
<summary type="html"><p>python的列表list是非常好用的容易,在刷算法题时用起来比较方便。在这里介绍python列表中的常用方法和常用的功能(主要是刷算法题时常用到)。<br><strong>list常用方法</strong></p>
<ul>
<li>append(data) </summary>
<category term="Python基础" scheme="https://huanqgingyu.top/categories/Python%E5%9F%BA%E7%A1%80/"/>
<category term="python" scheme="https://huanqgingyu.top/tags/python/"/>
</entry>
<entry>
<title>Git常用命令汇总</title>
<link href="https://huanqgingyu.top/2021/06/02/Git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E6%B1%87%E6%80%BB/"/>
<id>https://huanqgingyu.top/2021/06/02/Git%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E6%B1%87%E6%80%BB/</id>
<published>2021-06-01T16:00:00.000Z</published>
<updated>2022-08-11T13:45:44.692Z</updated>
<content type="html"><![CDATA[<p>git init —— 初始化仓库</p><p>git status —— 查看仓库的状态</p><p>git add —— 向暂存区中添加文件</p><ul><li>git add [文件1] [文件2] […] —— 添加一个或多个文件到暂存区</li><li>git add [目录] —— 添加指定目录到暂存区</li><li>git add . —— 添加当前目录下的所有文件到暂存区 </li></ul><p>git commit —— 保存仓库的历史记录</p><ul><li>git commit —— 记述详细提交信息</li><li>git commit -m “[提交信息]” —— 提交概述信息</li><li>git commit —amend —— 修改提交信息</li></ul><p>git log —— 查看提交日志</p><ul><li>git log —— 查看完整日志</li><li>git log —pretty=short —— 查看简述日志</li><li>git log [目录名/文件名] —— 只显示该目录/文件的日志</li><li>git log -p —— 查看提交所带来的改动</li><li>git log -p [录名/文件名] —— 只查看该目录/文件提交所带来的改动</li><li>git log —graph —— 以图标形式查看分支</li></ul><p>git diff —— 查看更改前后的差别</p><ul><li>git diff —— 查看工作树和暂存区的差别</li><li>git diff HEAD —— 查看本次提交与上次提交之间的差别</li></ul><p>git branch —— 显示分支一览表</p><ul><li>git branch —— 显示所有分支(“*”表示我们当前所在的分支)</li><li>git branch [分支名] —— 创建一个新的分支</li><li>git branch -a —— 显示分支一览表,同时显示本地仓库和远程仓库的分支信息</li><li>git branch -D [分支名] —— 删除对应分支 </li></ul><p>git checkout —— 切换分支</p><ul><li>git checkout [分支名] —— 切换到指定的分支</li><li>git checkout - ——切换至上一个分支</li><li>git checkout -b [分支名] —— 创建并切换到指定分支</li><li>git checkout -b [分支名] [远程主机名]/[远程分支名] —— 以远程主机的指定分支为来源,在本地仓库中创建指定分支名分支</li></ul><p>git merge —— 合并分支</p><ul><li>git merge —no-ff [分支名] —— 创建合并并提交</li></ul><p>git reset —— 回溯历史版本</p><ul><li>git reset —hard [目标时间点的哈希值] —— 回溯到指定哈希值对应的时间点上</li></ul><p>git rebase -i —— 压缩历史</p><ul><li>git rebase -i HEAD~2 —— 选定当前分支中包含HEAD(最新提交)在内的两个最新历史记录为对象,并在编辑器中打开</li></ul><p>git remote add —— 添加远程仓库</p><ul><li>git remote add [标识符] [远程仓库路径] —— 将GitHub上创建的仓库设置为本地仓库的远程仓库</li></ul><p>git push —— 推送至远程仓库</p><ul><li>git push -u [远程主机名] [本地分支名]:[远程分支名] —— 将本地分支版本上传到远程并合并</li><li>git push -u [远程主机名] [本地分支名] —— 将本地分支版本上传到远程并合并(如果本地分支名与远程分支名相同,则可以省略冒号)</li></ul><p>git clone —— 获取远程仓库</p><p>git fetch [远程仓库标识符] —— 获取最新的数据</p>]]></content>
<summary type="html"><p>git init —— 初始化仓库</p>
<p>git status —— 查看仓库的状态</p>
<p>git add —— 向暂存区中添加文件</p>
<ul>
<li>git add [文件1] [文件2] […] —— 添加一个或多个文件到暂存区</li>
<li</summary>
<category term="开发工具" scheme="https://huanqgingyu.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
<category term="Git" scheme="https://huanqgingyu.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/Git/"/>
<category term="Git" scheme="https://huanqgingyu.top/tags/Git/"/>
</entry>
<entry>
<title>箕斗卸煤检测</title>
<link href="https://huanqgingyu.top/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/"/>
<id>https://huanqgingyu.top/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/</id>
<published>2021-05-11T08:37:58.000Z</published>
<updated>2022-08-11T13:33:33.707Z</updated>
<content type="html"><![CDATA[<h3 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h3><p>利用卷积神经网络,训练相关数据,使得自己的系统能够实时识别箕斗是否卸煤,是否在位。需要输出三个状态即 箕斗未在位,箕斗在位,箕斗卸煤。<br><strong>卸煤过程:</strong><br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img1.png" alt><br><strong>卸煤结束:</strong><br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img2.png" alt><br><strong>箕斗不在位:</strong><br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img3.png" alt></p><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p>这是个简单的分类问题,把箕斗的状态分为三种状态,所以可以设置输出值为100,010,001。因为训练时不用输入整张图片进行训练,所以选择有关键特征的小图来进行运算。</p><h3 id="数据准备"><a href="#数据准备" class="headerlink" title="数据准备"></a>数据准备</h3><h4 id="截取短视屏"><a href="#截取短视屏" class="headerlink" title="截取短视屏"></a>截取短视屏</h4><p>视频大部分是一个多小时时长的,因为视频中大部分都是相同的箕斗不在位的状态,所以从中截取一小部分时间的视频来制作数据集。因为视屏的帧率是25,所以一分钟的视屏都有1500张图片。在这里我截取了两段视屏来制作数据集,第一段就是正常卸煤状态的视屏,第二段是箕斗右侧其它箕斗卸煤的视屏,在这里目标箕斗一直是处于不在位的状态,但是与之前一段不同的是这段视屏中有红色的光存在,如图所示:<br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img4.png" alt><br>视屏截取代码如下:</p><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">capture_video</span>(<span class="params">video_path, result_video_path, video, result_video, start_time, end_time</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 功能:截取短视频</span></span><br><span class="line"><span class="string"> 参数:</span></span><br><span class="line"><span class="string"> video_path:需要截取的视频路径</span></span><br><span class="line"><span class="string"> result_video_path:截取后的视频存放的路径</span></span><br><span class="line"><span class="string"> video:需要截取的视频的名称(不带后缀)</span></span><br><span class="line"><span class="string"> result_video:截取了的视频的名称(不带后缀)</span></span><br><span class="line"><span class="string"> start_time:截取开始时间(单位s)</span></span><br><span class="line"><span class="string"> end_time:截取结束时间(单位s)</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 打印出起始和结束时间</span></span><br><span class="line"> print(<span class="string">"The path of the video to be processed is:{}"</span>.format(video_path))</span><br><span class="line"> print(<span class="string">"start_time={}"</span>.format(start_time))</span><br><span class="line"> print(<span class="string">"end_time={}"</span>.format(end_time))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取视屏</span></span><br><span class="line"> cap = cv2.VideoCapture(video_path + video)</span><br><span class="line"> print(<span class="string">"The video load was successful!"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取视屏帧率</span></span><br><span class="line"> fps_video = cap.get(cv2.CAP_PROP_FPS)</span><br><span class="line"> print(<span class="string">"fps_video={}"</span>.format(fps_video))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 设置写入视屏的编码格式</span></span><br><span class="line"> fourcc = cv2.VideoWriter_fourcc(*<span class="string">"mp4v"</span>)</span><br><span class="line"> print(<span class="string">"fourcc={}"</span>.format(fourcc))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取视屏宽度和高度</span></span><br><span class="line"> frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))</span><br><span class="line"> frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))</span><br><span class="line"> print(<span class="string">"frame_width={},frame_height={}"</span>.format(frame_width, frame_height))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 设置写视屏的对象</span></span><br><span class="line"> videoWriter = cv2.VideoWriter(result_video_path + result_video, fourcc, fps_video, (frame_width, frame_height))</span><br><span class="line"> print(<span class="string">"The object for writing video is set."</span>)</span><br><span class="line"> print(<span class="string">"Will enter a loop to read frame images!"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 初始化一个计数器</span></span><br><span class="line"> acount = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> print(<span class="string">"It goes into the cycle!"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (cap.isOpened()):</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取视屏里的图片</span></span><br><span class="line"> ret, frame = cap.read()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 如果视屏没有读取结束</span></span><br><span class="line"> <span class="keyword">if</span> ret == <span class="literal">True</span>:</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 计数器加一</span></span><br><span class="line"> acount += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 截取相应时间内的视频信息</span></span><br><span class="line"> <span class="keyword">if</span>(acount > (start_time * fps_video) <span class="keyword">and</span> acount <= (end_time * fps_video)):</span><br><span class="line"> <span class="comment"># 将图片写入视屏</span></span><br><span class="line"> videoWriter.write(frame)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(acount == (end_time * fps_video)):</span><br><span class="line"> print(<span class="string">"The video was captured successfully!"</span>)</span><br><span class="line"> print(<span class="string">"The processed video is stored:{}"</span>.format(result_video_path))</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># 写入视屏结束</span></span><br><span class="line"> videoWriter.release()</span><br><span class="line"> print(<span class="string">"Start time or end time exceeds the video time!"</span>)</span><br><span class="line"> print(<span class="string">"Video capture failed!"</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> video_path = <span class="string">"jidou_video/"</span></span><br><span class="line"> result_video_path = <span class="string">"video/"</span></span><br><span class="line"> video = <span class="string">"wskip2020-12-03-20.mp4"</span></span><br><span class="line"> result_video = <span class="string">"first_video.mp4"</span></span><br><span class="line"> start_time = <span class="number">5</span> * <span class="number">60</span> + <span class="number">30</span></span><br><span class="line"> end_time = <span class="number">6</span> * <span class="number">60</span> + <span class="number">30</span></span><br><span class="line"> capture_video(video_path, result_video_path, video, result_video, start_time, end_time)</span><br></pre></td></tr></table></figure><h4 id="获取图片"><a href="#获取图片" class="headerlink" title="获取图片"></a>获取图片</h4><p>截取到了关键短视频后就是把短视频拆分成一帧一帧的图片。拆分视频的代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">apart</span>(<span class="params">video_path, video_name, image_path</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 功能:将视频拆分成图片</span></span><br><span class="line"><span class="string"> 参数:</span></span><br><span class="line"><span class="string"> video_path:要拆分的视频路径</span></span><br><span class="line"><span class="string"> video_name:要拆分的视频名字(不带后缀)</span></span><br><span class="line"><span class="string"> image_path:拆分后图片的存放路径</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 在这里把后缀接上</span></span><br><span class="line"> video = os.path.join(video_path, video_name + <span class="string">'.mp4'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 提取视频的频率,每1帧提取一个</span></span><br><span class="line"> frameFrequency = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(image_path):</span><br><span class="line"> <span class="comment">#如果文件目录不存在则创建目录</span></span><br><span class="line"> os.makedirs(image_path)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取视屏</span></span><br><span class="line"> use_video = cv2.VideoCapture(video)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 初始化计数器</span></span><br><span class="line"> acount = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 开始循环抽取图片</span></span><br><span class="line"> print(<span class="string">'Start extracting images!'</span>)</span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> res, image = use_video.read()</span><br><span class="line"> acount += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 如果提取完图片,则退出循环</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> res:</span><br><span class="line"> print(<span class="string">'not res , not image'</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将图片写入文件夹中</span></span><br><span class="line"> cv2.imwrite(image_path + str(acount) + <span class="string">'.jpg'</span>, image)</span><br><span class="line"> print(image_path + str(acount) + <span class="string">'.jpg'</span>)</span><br><span class="line"></span><br><span class="line"> print(<span class="string">'End of image extraction!'</span>)</span><br><span class="line"> use_video.release()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> video_path = <span class="string">'video/'</span></span><br><span class="line"> video_name = <span class="string">'first_video'</span></span><br><span class="line"> image_path = <span class="string">'image/'</span></span><br><span class="line"> apart(video_path, video_name, image_path)</span><br></pre></td></tr></table></figure></p><h4 id="标定数据"><a href="#标定数据" class="headerlink" title="标定数据"></a>标定数据</h4><p>因为在训练时需要将训练的图片和对应的输出结果进行匹配,所以要对图片进行标定。代码如下:<br><figure class="highlight python"><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="function"><span class="keyword">def</span> <span class="title">image_tag</span>(<span class="params">labels_path, start_number, end_number, skip_state</span>):</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">功能:对图片进行数据标定</span></span><br><span class="line"><span class="string">参数:</span></span><br><span class="line"><span class="string">labels_path:存放标签数据的路径</span></span><br><span class="line"><span class="string">start_number:开始标记数据号码</span></span><br><span class="line"><span class="string">end_number:结束标记号码</span></span><br><span class="line"><span class="string">skip_state:箕斗状态(有1、2、3)三种状态</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 开始检索标定</span></span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(end_number - start_number + <span class="number">1</span>):</span><br><span class="line"><span class="keyword">with</span> open(labels_path + <span class="string">'%s.txt'</span> % str(start_number + i), <span class="string">'w'</span>) <span class="keyword">as</span> out_file:</span><br><span class="line"><span class="keyword">if</span> skip_state == <span class="number">1</span>:</span><br><span class="line">out_file.write(<span class="string">" "</span>.join([<span class="string">'1'</span>, <span class="string">'0'</span>, <span class="string">'0'</span>]))</span><br><span class="line"><span class="keyword">elif</span> skip_state == <span class="number">2</span>:</span><br><span class="line">out_file.write(<span class="string">" "</span>.join([<span class="string">'0'</span>, <span class="string">'1'</span>, <span class="string">'0'</span>]))</span><br><span class="line"><span class="keyword">elif</span> skip_state == <span class="number">3</span>:</span><br><span class="line">out_file.write(<span class="string">" "</span>.join([<span class="string">'0'</span>, <span class="string">'0'</span>, <span class="string">'1'</span>]))</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">labels_path = <span class="string">'labels/'</span></span><br><span class="line">start_number = <span class="number">1501</span></span><br><span class="line">end_number = <span class="number">1675</span></span><br><span class="line">skip_state = <span class="number">1</span></span><br><span class="line">image_tag(labels_path, start_number, end_number, skip_state)</span><br></pre></td></tr></table></figure><br>因为训练时对应的图片的输出值是从txt文件里获取得到的,所以每张图片都制作一个txt文件来存放标定的数据。这里为了标定数据简单,就将图片和标定的数据的文件名按照数字的序号来写。</p><h4 id="写训练集"><a href="#写训练集" class="headerlink" title="写训练集"></a>写训练集</h4><p>这部主要是将需要训练的txt文件名(不带后缀)放入训练文档中。代码如下:<br><figure class="highlight python"><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="function"><span class="keyword">def</span> <span class="title">write_data</span>(<span class="params">file_path, file_type, number</span>):</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">功能:将需要训练的数据的文件名写入文档中</span></span><br><span class="line"><span class="string">参数:</span></span><br><span class="line"><span class="string">file_path:训练文件名集存放的路径</span></span><br><span class="line"><span class="string">file_type:文件类型,主要有训练和测试两种</span></span><br><span class="line"><span class="string">number:需要用到的文件个数</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="keyword">with</span> open(file_path + <span class="string">'%s.txt'</span> % (file_type), <span class="string">'w'</span>) <span class="keyword">as</span> out_file:</span><br><span class="line"><span class="keyword">for</span> i <span class="keyword">in</span> range(number):</span><br><span class="line">out_file.write(str(i + <span class="number">1</span>) + <span class="string">'\n'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line">file_path = <span class="string">'ImageSets/Main/'</span></span><br><span class="line">file_type = <span class="string">'train'</span></span><br><span class="line">number = <span class="number">1675</span></span><br><span class="line">write_data(file_path, file_type, number)</span><br></pre></td></tr></table></figure></p><h4 id="设置训练数据"><a href="#设置训练数据" class="headerlink" title="设置训练数据"></a>设置训练数据</h4><p>在数据准备阶段先设置需要训练的数据,主要目的是通过pytorch来对训练集进行封装。代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> Dataset</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">dataset</span>(<span class="params">Dataset</span>):</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self, is_train = True</span>):</span></span><br><span class="line"><span class="comment"># 设置存储着文件名的列表</span></span><br><span class="line">self.file_names = []</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果是要进行训练读取训练数据文档,否则读取验证数据文档</span></span><br><span class="line"><span class="keyword">if</span> is_train:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将训练数据的文档名存储到文件列表中,.strip()去除字符串前后的空格或换行符</span></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"ImageSets/Main/train.txt"</span>, <span class="string">'r'</span>) <span class="keyword">as</span> f:</span><br><span class="line">self.file_names = [x.strip() <span class="keyword">for</span> x <span class="keyword">in</span> f]</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将验证数据的文档名存储到文件列表中</span></span><br><span class="line"><span class="keyword">with</span> open(<span class="string">"ImageSets/Main/val.txt"</span>, <span class="string">'r'</span>) <span class="keyword">as</span> f:</span><br><span class="line">self.file_names = [x.strip() <span class="keyword">for</span> x <span class="keyword">in</span> f]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 图片存储路径</span></span><br><span class="line">self.img_path = <span class="string">"image/"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># label数据文档存储数据</span></span><br><span class="line">self.label_path = <span class="string">"labels/"</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__len__</span>(<span class="params">self</span>):</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">功能:返回文件名的个数</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="comment"># 返回文件名的个数</span></span><br><span class="line"><span class="keyword">return</span> len(self.file_names)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__getitem__</span>(<span class="params">self, item</span>):</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">功能:对图像数据进行规范化处理</span></span><br><span class="line"><span class="string">参数:</span></span><br><span class="line"><span class="string">——picture_index:图片在文件名当中的索引</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line">img = cv2.imread(self.img_path + self.file_names[item] + <span class="string">'.jpg'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将图片进行裁剪,h(420~670),w(495~745)</span></span><br><span class="line">img = img[<span class="number">420</span>:<span class="number">670</span>, <span class="number">495</span>:<span class="number">745</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将原图片中的(h,w,c)转换成(c,w,h)</span></span><br><span class="line">img = img.transpose(<span class="number">2</span>, <span class="number">1</span>, <span class="number">0</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将图片进行归一化</span></span><br><span class="line">img = img / <span class="number">255.</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">with</span> open(self.label_path + self.file_names[item] + <span class="string">".txt"</span>) <span class="keyword">as</span> f:</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将.txt文档中的内容放入列表中</span></span><br><span class="line">line = [x.split() <span class="keyword">for</span> x <span class="keyword">in</span> f]</span><br><span class="line">label = [float(x) <span class="keyword">for</span> y <span class="keyword">in</span> line <span class="keyword">for</span> x <span class="keyword">in</span> y]</span><br><span class="line"></span><br><span class="line">labels = np.zeros((<span class="number">3</span>))</span><br><span class="line"></span><br><span class="line">labels[<span class="number">0</span>:<span class="number">3</span>] = np.array([label[<span class="number">0</span>], label[<span class="number">1</span>], label[<span class="number">2</span>]])</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> img, labels</span><br></pre></td></tr></table></figure></p><h3 id="设计网络"><a href="#设计网络" class="headerlink" title="设计网络"></a>设计网络</h3><p>代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">net</span>(<span class="params">nn.Module</span>):</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self</span>):</span></span><br><span class="line">super(net, self).__init__()</span><br><span class="line"></span><br><span class="line">self.Conv_layers1 = nn.Sequential(</span><br><span class="line">nn.Conv2d(<span class="number">3</span>, <span class="number">128</span>, <span class="number">12</span>, <span class="number">2</span>),</span><br><span class="line">nn.LeakyReLU(),</span><br><span class="line">nn.MaxPool2d(<span class="number">2</span>, <span class="number">2</span>)</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">self.Conv_layers2 = nn.Sequential(</span><br><span class="line">nn.Conv2d(<span class="number">128</span>, <span class="number">256</span>, <span class="number">6</span>, <span class="number">2</span>),</span><br><span class="line">nn.LeakyReLU(),</span><br><span class="line">nn.MaxPool2d(<span class="number">2</span>, <span class="number">2</span>)</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">self.Conn_layers1 = nn.Sequential(</span><br><span class="line">nn.Linear(<span class="number">14</span> * <span class="number">14</span> * <span class="number">256</span>, <span class="number">1000</span>),</span><br><span class="line">nn.LeakyReLU()</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line">self.Conn_layers2 = nn.Sequential(</span><br><span class="line">nn.Linear(<span class="number">1000</span>, <span class="number">3</span>),</span><br><span class="line">nn.Sigmoid()</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">forward</span>(<span class="params">self, input</span>):</span></span><br><span class="line">input = self.Conv_layers1(input)</span><br><span class="line">input = self.Conv_layers2(input)</span><br><span class="line">input = input.view(input.size()[<span class="number">0</span>], <span class="number">-1</span>)</span><br><span class="line">input = self.Conn_layers1(input)</span><br><span class="line">input = self.Conn_layers2(input)</span><br><span class="line">output = input.reshape(<span class="number">-1</span>, <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> output</span><br></pre></td></tr></table></figure></p><h3 id="损失函数"><a href="#损失函数" class="headerlink" title="损失函数"></a>损失函数</h3><p>pytorch有自带的损失函数,所以也可以用框架内自带的损失函数。代码如下:<br><figure class="highlight python"><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">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">loss_function</span>(<span class="params">predict, labels</span>):</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="string">功能:计算损失值</span></span><br><span class="line"><span class="string">参数:</span></span><br><span class="line"><span class="string">——predict:net的预测结果(batchsize,5),其中predict为tensor</span></span><br><span class="line"><span class="string">——labels:样本数据(batchsize,5),其中labels为tensor</span></span><br><span class="line"><span class="string">返回值:</span></span><br><span class="line"><span class="string">——loss:平均损失</span></span><br><span class="line"><span class="string">'''</span></span><br><span class="line"><span class="comment"># 设置初始损失值</span></span><br><span class="line">loss = <span class="number">0.</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取batchsize</span></span><br><span class="line">batch = labels.size()[<span class="number">0</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 计算每个批次的总损失</span></span><br><span class="line"><span class="keyword">for</span> batch_size <span class="keyword">in</span> range(batch):</span><br><span class="line">loss = loss + torch.sum((predict[batch_size, <span class="number">0</span>:<span class="number">3</span>] - labels[batch_size, <span class="number">0</span>:<span class="number">3</span>]) ** <span class="number">2</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 求平均损失</span></span><br><span class="line">loss = loss / batch</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> loss</span><br></pre></td></tr></table></figure></p><h3 id="训练网络"><a href="#训练网络" class="headerlink" title="训练网络"></a>训练网络</h3><p>这里我采用的是GPU来训练网络,如果用CPU可以将.cuda()去掉。代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> loss_function <span class="keyword">as</span> LF</span><br><span class="line"><span class="keyword">import</span> dataset <span class="keyword">as</span> D</span><br><span class="line"><span class="keyword">import</span> net <span class="keyword">as</span> N</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> Dataset, DataLoader</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置迭代次数</span></span><br><span class="line">epoch = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置批量的大小</span></span><br><span class="line">batch_size = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置学习率</span></span><br><span class="line">lr = <span class="number">0.01</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载数据</span></span><br><span class="line">train_data = D.dataset(is_train = <span class="literal">True</span>)</span><br><span class="line">data = DataLoader(train_data, batch_size = batch_size, shuffle = <span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化一个网络对象</span></span><br><span class="line">net = N.net().cuda()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化优化器</span></span><br><span class="line">optimizer = torch.optim.SGD(net.parameters(), lr = lr, momentum = <span class="number">0.9</span>, weight_decay = <span class="number">0.0005</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行迭代训练</span></span><br><span class="line"><span class="keyword">for</span> e <span class="keyword">in</span> range(epoch):</span><br><span class="line"><span class="keyword">for</span> i, (inputs, labels) <span class="keyword">in</span> enumerate(data):</span><br><span class="line">inputs = inputs.float().cuda()</span><br><span class="line">labels = labels.float().cuda()</span><br><span class="line">predict = net(inputs)</span><br><span class="line">loss = LF.loss_function(predict, labels)</span><br><span class="line">print(<span class="string">"epoch={},i={},loss={}"</span>.format(e, i, str(loss)))</span><br><span class="line">optimizer.zero_grad()</span><br><span class="line">loss.backward()</span><br><span class="line">optimizer.step()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 存储模型</span></span><br><span class="line">torch.save(net, <span class="string">"weight/weight.pkl"</span>)</span><br></pre></td></tr></table></figure><br>最后得到的损失如下:<br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img8.jpeg" alt><br>从图中可以看出除了小部分数据有点跳动大部分的损失还是比较低的。</p><h3 id="视频预测"><a href="#视频预测" class="headerlink" title="视频预测"></a>视频预测</h3><p>将需要预测的视频放入网络中并将状态文本打在视频上。代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 利用训练好的权重导入到网络中并测试视屏效果</span></span><br><span class="line"><span class="comment"># 而且能够在视屏中实时用绿线表示出显示后的效果</span></span><br><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> net <span class="keyword">as</span> N</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">predict_video</span>(<span class="params">video_path, result_video_path</span>):</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"><span class="string">功能:对视频进行预测并输出</span></span><br><span class="line"><span class="string">参数:</span></span><br><span class="line"><span class="string">video_path:要进行预测的视频路径</span></span><br><span class="line"><span class="string">result_video_path:预测结果的视频存放的路径</span></span><br><span class="line"><span class="string">"""</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置批次数,为了能讲数据放入训练网络中</span></span><br><span class="line">batch_size = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化文件名</span></span><br><span class="line">video = <span class="string">"first_video.mp4"</span></span><br><span class="line">result_video = <span class="string">"result_first_video_fast.mp4"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载权重</span></span><br><span class="line">net = torch.load(<span class="string">"weight/weight.pkl"</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 读取视屏</span></span><br><span class="line">cap = cv2.VideoCapture(video_path + video)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取视屏帧率</span></span><br><span class="line">fps_video = cap.get(cv2.CAP_PROP_FPS)</span><br><span class="line">print(<span class="string">"fps_video={}"</span>.format(fps_video))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置写入视屏的编码格式</span></span><br><span class="line">fourcc = cv2.VideoWriter_fourcc(*<span class="string">"mp4v"</span>)</span><br><span class="line">print(<span class="string">"fourcc={}"</span>.format(fourcc))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取视屏宽度和高度</span></span><br><span class="line">frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))</span><br><span class="line">frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))</span><br><span class="line">print(<span class="string">"frame_width={},frame_height{}"</span>.format(frame_width, frame_height))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置写视屏的对象</span></span><br><span class="line">videoWriter = cv2.VideoWriter(result_video_path + result_video, fourcc, fps_video, (frame_width, frame_height))</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span> (cap.isOpened()):</span><br><span class="line">ret, frame = cap.read()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 如果视屏没有结束</span></span><br><span class="line"><span class="keyword">if</span> ret == <span class="literal">True</span>:</span><br><span class="line"><span class="comment"># 将图片进行剪切</span></span><br><span class="line">inputs = frame[<span class="number">420</span>:<span class="number">670</span>, <span class="number">495</span>:<span class="number">745</span>]</span><br><span class="line">inputs = inputs.transpose(<span class="number">2</span>, <span class="number">1</span>, <span class="number">0</span>)</span><br><span class="line">inputs = inputs / <span class="number">255.0</span></span><br><span class="line"></span><br><span class="line">inputs = np.array([inputs])</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行输入数据类型转换</span></span><br><span class="line">inputs = torch.from_numpy(inputs)</span><br><span class="line">inputs = inputs.float().cuda()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 得到预测数据</span></span><br><span class="line">predict = net(inputs)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 取最大值索引</span></span><br><span class="line">predict = torch.argmax(predict)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 判断预测的值并给文本变量赋予相应的状态</span></span><br><span class="line"><span class="keyword">if</span> predict == <span class="number">0</span>:</span><br><span class="line">text = <span class="string">"The skip is not in position!"</span></span><br><span class="line"><span class="keyword">elif</span> predict == <span class="number">1</span>:</span><br><span class="line">text = <span class="string">"The skip is discharging coal!"</span></span><br><span class="line"><span class="keyword">elif</span> predict == <span class="number">2</span>:</span><br><span class="line">text = <span class="string">"The skip is in position!"</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 在图片上显示红色文本</span></span><br><span class="line">cv2.putText(frame, text, (<span class="number">200</span>, <span class="number">100</span>), cv2.FONT_HERSHEY_COMPLEX, <span class="number">2.0</span>, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">255</span>), <span class="number">5</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 在图片中显示出绿色的矩形框</span></span><br><span class="line">cv2.rectangle(frame, (<span class="number">495</span>, <span class="number">420</span>), (<span class="number">745</span>, <span class="number">670</span>), (<span class="number">0</span>, <span class="number">255</span>, <span class="number">0</span>), <span class="number">2</span>, <span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 显示视频</span></span><br><span class="line">cv2.imshow(<span class="string">'Frame'</span>,frame)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 刷新视频</span></span><br><span class="line">cv2.waitKey(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 按q键退出</span></span><br><span class="line"><span class="keyword">if</span> cv2.waitKey(<span class="number">25</span>) & <span class="number">0xFF</span> == ord(<span class="string">'q'</span>):</span><br><span class="line"><span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 将图片写入视屏</span></span><br><span class="line">videoWriter.write(frame)</span><br><span class="line"><span class="keyword">else</span>:</span><br><span class="line"><span class="comment"># 写入视屏结束</span></span><br><span class="line">videoWriter.release()</span><br><span class="line"><span class="keyword">break</span></span><br></pre></td></tr></table></figure><br>最后处理好的结果如下所示:<br><strong>箕斗不在位:</strong><br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img5.png" alt><br><strong>箕斗卸煤:</strong><br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img6.png" alt><br><strong>箕斗在位:</strong><br><img src= "/img/loading.gif" data-lazy-src="/2021/05/11/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/img7.png" alt></p>]]></content>
<summary type="html"><h3 id="需求"><a href="#需求" class="headerlink" title="需求"></a>需求</h3><p>利用卷积神经网络,训练相关数据,使得自己的系统能够实时识别箕斗是否卸煤,是否在位。需要输出三个状态即 箕斗未在位,箕斗在位,箕斗卸煤。<br</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="箕斗卸煤检测" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/%E7%AE%95%E6%96%97%E5%8D%B8%E7%85%A4%E6%A3%80%E6%B5%8B/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
</entry>
<entry>
<title>模型的保存与加载</title>
<link href="https://huanqgingyu.top/2021/05/05/Pytorch%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BF%9D%E5%AD%98%E4%B8%8E%E5%8A%A0%E8%BD%BD/"/>
<id>https://huanqgingyu.top/2021/05/05/Pytorch%E6%A8%A1%E5%9E%8B%E7%9A%84%E4%BF%9D%E5%AD%98%E4%B8%8E%E5%8A%A0%E8%BD%BD/</id>
<published>2021-05-04T16:00:00.000Z</published>
<updated>2022-08-11T14:14:17.433Z</updated>
<content type="html"><![CDATA[<p><strong>方式1:</strong><br>model_save.py代码如下:</p><figure class="highlight python"><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">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入vgg16网络</span></span><br><span class="line">vgg16 = torchvision.models.vgg16(pretrained=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存模型</span></span><br><span class="line">torch.save(vgg16, <span class="string">"vgg16_method1.pth"</span>)</span><br></pre></td></tr></table></figure><p>model_load.py代码如下:<br><figure class="highlight python"><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">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 使用这种加载方式时需要将网络的类引用进来</span></span><br><span class="line"><span class="keyword">from</span> model_save <span class="keyword">import</span> *</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型</span></span><br><span class="line">model = torch.load(<span class="string">"vgg16_method1.pth"</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>vgg16_method1.pth是模型结构+模型参数。<br>这种方式保存了整个网络的结构和其中的参数,所以保存的模型比较大。</p><hr><p><strong>方式2:</strong><br><figure class="highlight python"><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">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torchvision</span><br><span class="line"></span><br><span class="line"><span class="comment"># 保存模型,只保存参数</span></span><br><span class="line">torch.save(vgg16.state_dict(), <span class="string">"vgg16_method2.pth"</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><br>model_load.py代码如下:<br><figure class="highlight python"><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">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="comment"># 导入vgg16网络</span></span><br><span class="line">vgg16 = torchvision.models.vgg16(pretrained=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载模型</span></span><br><span class="line">vgg16.load_state_dict(torch.load(<span class="string">"vgg16_method2.pth"</span>))</span><br><span class="line"><span class="comment"># model = torch.load("vgg16_method2.pth")</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><br>vgg16_method2.pth只有模型参数。<br>这种方式以字典的形式来保存网络的参数,因为只有参数没有网络,所以保存的模型较小。官方推荐使用。</p>]]></content>
<summary type="html"><p><strong>方式1:</strong><br>model_save.py代码如下:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1<</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="Pytorch" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/Pytorch/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="Pytorch" scheme="https://huanqgingyu.top/tags/Pytorch/"/>
</entry>
<entry>
<title>OpenCV截取指定时间范围内的视频(python)</title>
<link href="https://huanqgingyu.top/2021/05/01/OpenCV%E6%88%AA%E5%8F%96%E6%8C%87%E5%AE%9A%E6%97%B6%E9%97%B4%E8%8C%83%E5%9B%B4%E5%86%85%E7%9A%84%E8%A7%86%E9%A2%91%EF%BC%88python%EF%BC%89/"/>
<id>https://huanqgingyu.top/2021/05/01/OpenCV%E6%88%AA%E5%8F%96%E6%8C%87%E5%AE%9A%E6%97%B6%E9%97%B4%E8%8C%83%E5%9B%B4%E5%86%85%E7%9A%84%E8%A7%86%E9%A2%91%EF%BC%88python%EF%BC%89/</id>
<published>2021-04-30T16:00:00.000Z</published>
<updated>2022-08-11T13:40:15.775Z</updated>
<content type="html"><![CDATA[<p>本文通过OpenCV将视频裁剪成指定时间范围内的小视频,代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">capture_video</span>(<span class="params">video_path, result_video_path, video, result_video, start_time, end_time</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 功能:截取短视频</span></span><br><span class="line"><span class="string"> 参数:</span></span><br><span class="line"><span class="string"> video_path:需要截取的视频路径</span></span><br><span class="line"><span class="string"> result_video_path:截取后的视频存放的路径</span></span><br><span class="line"><span class="string"> video:需要截取的视频的名称(不带后缀)</span></span><br><span class="line"><span class="string"> result_video:截取了的视频的名称(不带后缀)</span></span><br><span class="line"><span class="string"> start_time:截取开始时间(单位s)</span></span><br><span class="line"><span class="string"> end_time:截取结束时间(单位s)</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取视频</span></span><br><span class="line"> cap = cv2.VideoCapture(video_path + video)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取视频帧率</span></span><br><span class="line"> fps_video = cap.get(cv2.CAP_PROP_FPS)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 设置写入视频的编码格式</span></span><br><span class="line"> fourcc = cv2.VideoWriter_fourcc(*<span class="string">"mp4v"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取视频宽度和高度</span></span><br><span class="line"> frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))</span><br><span class="line"> frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 设置写视频的对象</span></span><br><span class="line"> videoWriter = cv2.VideoWriter(result_video_path + result_video, fourcc, fps_video, (frame_width, frame_height))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 初始化一个计数器</span></span><br><span class="line"> count = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (cap.isOpened()):</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取视屏里的图片</span></span><br><span class="line"> ret, frame = cap.read()</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 如果视屏没有读取结束</span></span><br><span class="line"> <span class="keyword">if</span> ret == <span class="literal">True</span>:</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 计数器加一</span></span><br><span class="line"> count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 截取相应时间内的视频信息</span></span><br><span class="line"> <span class="keyword">if</span>(count > (start_time * fps_video) <span class="keyword">and</span> count <= (end_time * fps_video)):</span><br><span class="line"> <span class="comment"># 将图片写入视屏</span></span><br><span class="line"> videoWriter.write(frame)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(count == (end_time * fps_video)):</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> <span class="comment"># 写入视屏结束</span></span><br><span class="line"> videoWriter.release()</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> video_path = <span class="string">"original_video/"</span></span><br><span class="line"> result_video_path = <span class="string">"result_video/"</span></span><br><span class="line"> video = <span class="string">"test_video.mp4"</span></span><br><span class="line"> result_video = <span class="string">"result_test_video.mp4"</span></span><br><span class="line"> start_time = <span class="number">5</span> * <span class="number">60</span> + <span class="number">30</span></span><br><span class="line"> end_time = <span class="number">6</span> * <span class="number">60</span> + <span class="number">30</span></span><br><span class="line"> capture_video(video_path, result_video_path, video, result_video, start_time, end_time)</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html"><p>本文通过OpenCV将视频裁剪成指定时间范围内的小视频,代码如下:<br><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br><</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="OpenCV" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/OpenCV/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="OpenCV" scheme="https://huanqgingyu.top/tags/OpenCV/"/>
</entry>
<entry>
<title>OpenCV把视频拆分成图片(python)</title>
<link href="https://huanqgingyu.top/2021/05/01/OpenCV%E6%8A%8A%E8%A7%86%E9%A2%91%E6%8B%86%E5%88%86%E6%88%90%E5%9B%BE%E7%89%87%EF%BC%88python%EF%BC%89/"/>
<id>https://huanqgingyu.top/2021/05/01/OpenCV%E6%8A%8A%E8%A7%86%E9%A2%91%E6%8B%86%E5%88%86%E6%88%90%E5%9B%BE%E7%89%87%EF%BC%88python%EF%BC%89/</id>
<published>2021-04-30T16:00:00.000Z</published>
<updated>2022-08-11T13:42:20.594Z</updated>
<content type="html"><![CDATA[<p>本文通过OpenCV将视频裁剪成指定时间范围内的小视频,代码如下:<br>OpenCV把视频拆分成一帧一帧的图片并存放入指定的目录下,代码如下:<br><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> os</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">apart</span>(<span class="params">video_path, video_name, image_path</span>):</span></span><br><span class="line"> <span class="string">"""</span></span><br><span class="line"><span class="string"> 功能:将视频拆分成图片</span></span><br><span class="line"><span class="string"> 参数:</span></span><br><span class="line"><span class="string"> video_path:要拆分的视频路径</span></span><br><span class="line"><span class="string"> video_name:要拆分的视频名字(不带后缀)</span></span><br><span class="line"><span class="string"> image_path:拆分后图片的存放路径</span></span><br><span class="line"><span class="string"> """</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 在这里把后缀接上</span></span><br><span class="line"> video = os.path.join(video_path, video_name + <span class="string">'.mp4'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 提取视频的频率,每1帧提取一个</span></span><br><span class="line"> frameFrequency = <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> os.path.exists(image_path):</span><br><span class="line"> <span class="comment">#如果文件目录不存在则创建目录</span></span><br><span class="line"> os.makedirs(image_path)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取视频</span></span><br><span class="line"> use_video = cv2.VideoCapture(video)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 初始化计数器</span></span><br><span class="line"> count = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 开始循环抽取图片</span></span><br><span class="line"> print(<span class="string">'Start extracting images!'</span>)</span><br><span class="line"> <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"> res, image = use_video.read()</span><br><span class="line"> count += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 如果提取完图片,则退出循环</span></span><br><span class="line"> <span class="keyword">if</span> <span class="keyword">not</span> res:</span><br><span class="line"> print(<span class="string">'not res , not image'</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将图片写入文件夹中</span></span><br><span class="line"> cv2.imwrite(image_path + str(count) + <span class="string">'.jpg'</span>, image)</span><br><span class="line"> print(image_path + str(count) + <span class="string">'.jpg'</span>)</span><br><span class="line"></span><br><span class="line"> print(<span class="string">'End of image extraction!'</span>)</span><br><span class="line"> use_video.release()</span><br><span class="line"></span><br><span class="line"><span class="keyword">if</span> __name__ == <span class="string">'__main__'</span>:</span><br><span class="line"> video_path = <span class="string">'video/'</span></span><br><span class="line"> video_name = <span class="string">'first_video'</span></span><br><span class="line"> image_path = <span class="string">'image/'</span></span><br><span class="line"> apart(video_path, video_name, image_path)</span><br></pre></td></tr></table></figure></p>]]></content>
<summary type="html"><p>本文通过OpenCV将视频裁剪成指定时间范围内的小视频,代码如下:<br>OpenCV把视频拆分成一帧一帧的图片并存放入指定的目录下,代码如下:<br><figure class="highlight python"><table><tr><td class="gutter</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="OpenCV" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/OpenCV/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="OpenCV" scheme="https://huanqgingyu.top/tags/OpenCV/"/>
</entry>
<entry>
<title>sublime_text3显示空格和Tab</title>
<link href="https://huanqgingyu.top/2021/03/31/sublime-text3%E6%98%BE%E7%A4%BA%E7%A9%BA%E6%A0%BC%E5%92%8CTab/"/>
<id>https://huanqgingyu.top/2021/03/31/sublime-text3%E6%98%BE%E7%A4%BA%E7%A9%BA%E6%A0%BC%E5%92%8CTab/</id>
<published>2021-03-30T16:16:47.000Z</published>
<updated>2022-08-11T12:56:04.040Z</updated>
<content type="html"><![CDATA[<h3 id="sublime-text-3显示空格和Tab"><a href="#sublime-text-3显示空格和Tab" class="headerlink" title="sublime text 3显示空格和Tab"></a>sublime text 3显示空格和Tab</h3><p>找到Sublime Text —> Preferences —> Settings</p><p>在Preferences.sublime-settings — User中加入:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">"draw_white_space": "all"</span><br></pre></td></tr></table></figure><br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/sublime-text3%E6%98%BE%E7%A4%BA%E7%A9%BA%E6%A0%BC%E5%92%8CTab/show_blank_tab.png" alt><br>注意:属性和属性之间用’,’隔开!</p>]]></content>
<summary type="html"><h3 id="sublime-text-3显示空格和Tab"><a href="#sublime-text-3显示空格和Tab" class="headerlink" title="sublime text 3显示空格和Tab"></a>sublime text 3显示空格和T</summary>
<category term="开发工具" scheme="https://huanqgingyu.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/"/>
<category term="Sublime" scheme="https://huanqgingyu.top/categories/%E5%BC%80%E5%8F%91%E5%B7%A5%E5%85%B7/Sublime/"/>
<category term="sublime" scheme="https://huanqgingyu.top/tags/sublime/"/>
</entry>
<entry>
<title>手写数字识别(识别纸上的手写数字)</title>
<link href="https://huanqgingyu.top/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/"/>
<id>https://huanqgingyu.top/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/</id>
<published>2021-03-30T16:03:33.000Z</published>
<updated>2022-08-11T13:25:13.794Z</updated>
<content type="html"><![CDATA[<h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/problem3.png" alt></p><h3 id="题目分析"><a href="#题目分析" class="headerlink" title="题目分析"></a>题目分析</h3><p>题目的意思是要识别自己用手在纸上写的数字。从特征上来看,手写数字相比于普通的电脑上的数字最大的 不同就是数字的边缘会发生不同幅度的抖动。而且,在MNIST数据集中的数字是边缘为黑色的,然后数字是不同灰度的白色的,如下所示:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/data_singal.png" alt><br>在数据集中,每个数据都是$28*28$的灰度图,并且黑色部分都是零,其余白色的灰度值并不统一。因为如果训练时背景都是统一的时候我们测试用的图片背景也必须是统一的,否则基本无法识别出来。除非训练的时候换各种不同的背景大数据进行训练,这样特征就不会依托着背景而存在,剩下的就是要识别的物体自己所拥有的特征了。所以在这里我要做的就是在图片预处理的时候尽量让图片处理成接近测试图片的样子。</p><h3 id="训练网络"><a href="#训练网络" class="headerlink" title="训练网络"></a>训练网络</h3><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.optim <span class="keyword">as</span> optim</span><br><span class="line"><span class="keyword">from</span> torchvision <span class="keyword">import</span> datasets, transforms</span><br><span class="line"><span class="keyword">from</span> torch.autograd <span class="keyword">import</span> Variable</span><br><span class="line"><span class="keyword">from</span> torch.utils.data <span class="keyword">import</span> DataLoader</span><br><span class="line"></span><br><span class="line"><span class="comment"># 下载训练集</span></span><br><span class="line">train_dataset = datasets.MNIST(root=<span class="string">'./data/'</span>,</span><br><span class="line"> train=<span class="literal">True</span>,</span><br><span class="line"> transform=transforms.ToTensor(),</span><br><span class="line"> download=<span class="literal">False</span>)</span><br><span class="line"><span class="comment"># 下载测试集</span></span><br><span class="line">test_dataset = datasets.MNIST(root=<span class="string">'./data/'</span>,</span><br><span class="line"> train=<span class="literal">False</span>,</span><br><span class="line"> transform=transforms.ToTensor(),</span><br><span class="line"> download=<span class="literal">False</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置批次数</span></span><br><span class="line">batch_size = <span class="number">100</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 装载训练集</span></span><br><span class="line">train_loader = torch.utils.data.DataLoader(dataset = train_dataset,</span><br><span class="line"> batch_size = batch_size,</span><br><span class="line"> shuffle=<span class="literal">True</span>)</span><br><span class="line"><span class="comment"># 装载测试集</span></span><br><span class="line">test_loader = torch.utils.data.DataLoader(dataset = test_dataset,</span><br><span class="line"> batch_size = batch_size,</span><br><span class="line"> shuffle = <span class="literal">True</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 自定义手写数字识别网络</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">net</span>(<span class="params">nn.Module</span>):</span></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self</span>):</span></span><br><span class="line"> super(net, self).__init__()</span><br><span class="line"></span><br><span class="line"> self.Conn_layers = nn.Sequential(</span><br><span class="line"> nn.Linear(<span class="number">784</span>, <span class="number">100</span>),</span><br><span class="line"> nn.Sigmoid(),</span><br><span class="line"> nn.Linear(<span class="number">100</span>, <span class="number">10</span>),</span><br><span class="line"> nn.Sigmoid()</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="function"><span class="keyword">def</span> <span class="title">forward</span>(<span class="params">self, input</span>):</span></span><br><span class="line"> output = self.Conn_layers(input)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义学习率</span></span><br><span class="line">LR = <span class="number">0.1</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义一个网络对象</span></span><br><span class="line">net = net()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 损失函数使用交叉熵</span></span><br><span class="line">loss_function = nn.CrossEntropyLoss()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 优化函数使用 SGD</span></span><br><span class="line">optimizer = optim.SGD(</span><br><span class="line"> net.parameters(),</span><br><span class="line"> lr = LR,</span><br><span class="line"> momentum = <span class="number">0.9</span>,</span><br><span class="line"> weight_decay = <span class="number">0.0005</span></span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义迭代次数</span></span><br><span class="line">epoch = <span class="number">20</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行迭代训练</span></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> range(epoch):</span><br><span class="line"> <span class="keyword">for</span> i, data <span class="keyword">in</span> enumerate(train_loader):</span><br><span class="line"> inputs, labels = data</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 转换下输入形状</span></span><br><span class="line"> inputs = inputs.reshape(batch_size, <span class="number">784</span>)</span><br><span class="line"></span><br><span class="line"> inputs, labels = Variable(inputs), Variable(labels)</span><br><span class="line"> outputs = net(inputs)</span><br><span class="line"> loss = loss_function(outputs, labels)</span><br><span class="line"> optimizer.zero_grad()</span><br><span class="line"> loss.backward()</span><br><span class="line"> optimizer.step()</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 初始化正确结果数为0</span></span><br><span class="line"> test_result = <span class="number">0</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 用测试数据进行测试</span></span><br><span class="line"> <span class="keyword">for</span> data_test <span class="keyword">in</span> test_loader:</span><br><span class="line"> images, labels = data_test</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 转换下输入形状</span></span><br><span class="line"> images = images.reshape(batch_size, <span class="number">784</span>)</span><br><span class="line"></span><br><span class="line"> images, labels = Variable(images), Variable(labels)</span><br><span class="line"> output_test = net(images)</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 对一个批次的数据的准确性进行判断</span></span><br><span class="line"> <span class="keyword">for</span> i <span class="keyword">in</span> range(len(labels)):</span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 如果输出结果的最大值的索引与标签内正确数据相等,准确个数累加</span></span><br><span class="line"> <span class="keyword">if</span> torch.argmax(output_test[i]) == labels[i]:</span><br><span class="line"> test_result += <span class="number">1</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment"># 打印每次迭代后正确的结果数</span></span><br><span class="line"> print(<span class="string">"Epoch {} : {} / {}"</span>.format(epoch, test_result, len(test_dataset)))</span><br><span class="line"> </span><br><span class="line"><span class="comment"># 保存权重模型</span></span><br><span class="line">torch.save(net, <span class="string">'weight/test.pkl'</span>)</span><br></pre></td></tr></table></figure><p>至此,对手写数字网络的训练已经结束,且训练的准确性为:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/answer1.png" alt><br>这个网络比较粗糙,所以准确性也只是一般,但如果要精确起来后面有很多文章可做。</p><h3 id="图像预处理"><a href="#图像预处理" class="headerlink" title="图像预处理"></a>图像预处理</h3><p>因为我们手机拍的照片和训练集的图片有很大的区别,所以无法将手机上拍的照片直接丢到训练好的网络模型中进行识别,需要先对图片进行预处理。有几点需要对原图进行改变:</p><ol><li>图片的大小:肯定得将拍摄到的图片转换成$28*28$尺寸大小的图片。</li><li>图片的通道数:由于MNIST是灰度图,所以原图的channel也得转换成1。</li><li>图片的背景:图片的背景得转换成MNIST相同的黑色,这样识别结果准确性更高。</li><li>数字的颜色:毋庸置疑,数字的颜色得变成MNIST相同的白色。</li><li>数字颜色中间深边缘前:观察MNIST的白色部分并不都是255全白,而是有渐变色的,这个渐变色模拟起来比较困难,算是难度最大的一点了。<br>接下来直接上代码了:<figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> cv2</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">image_preprocessing</span>():</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 读取图片</span></span><br><span class="line"> img = cv2.imread(<span class="string">"picture/test8.jpeg"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># =====================图像处理======================== #</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 转换成灰度图像</span></span><br><span class="line"> gray_img = cv2.cvtColor(img , cv2.COLOR_BGR2GRAY)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 进行高斯滤波</span></span><br><span class="line"> gauss_img = cv2.GaussianBlur(gray_img, (<span class="number">5</span>,<span class="number">5</span>), <span class="number">0</span>, <span class="number">0</span>, cv2.BORDER_DEFAULT)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 边缘检测</span></span><br><span class="line"> img_edge1 = cv2.Canny(gauss_img, <span class="number">100</span>, <span class="number">200</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># ==================================================== #</span></span><br><span class="line"> <span class="comment"># =====================图像分割======================== #</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取原始图像的宽和高</span></span><br><span class="line"> high = img.shape[<span class="number">0</span>]</span><br><span class="line"> width = img.shape[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 分别初始化高和宽的和</span></span><br><span class="line"> add_width = np.zeros(high, dtype = int)</span><br><span class="line"> add_high = np.zeros(width, dtype = int)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 计算每一行的灰度图的值的和</span></span><br><span class="line"> <span class="keyword">for</span> h <span class="keyword">in</span> range(high):</span><br><span class="line"> <span class="keyword">for</span> w <span class="keyword">in</span> range(width):</span><br><span class="line"> add_width[h] = add_width[h] + img_edge1[h][w]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 计算每一列的值的和</span></span><br><span class="line"> <span class="keyword">for</span> w <span class="keyword">in</span> range(width):</span><br><span class="line"> <span class="keyword">for</span> h <span class="keyword">in</span> range(high):</span><br><span class="line"> add_high[w] = add_high[w] + img_edge1[h][w]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 初始化上下边界为宽度总值最大的值的索引</span></span><br><span class="line"> acount_high_up = np.argmax(add_width)</span><br><span class="line"> acount_high_down = np.argmax(add_width)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将上边界坐标值上移,直到没有遇到白色点停止,此为数字的上边界</span></span><br><span class="line"> <span class="keyword">while</span> add_width[acount_high_up] != <span class="number">0</span>:</span><br><span class="line"> acount_high_up = acount_high_up + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将下边界坐标值下移,直到没有遇到白色点停止,此为数字的下边界</span></span><br><span class="line"> <span class="keyword">while</span> add_width[acount_high_down] != <span class="number">0</span>:</span><br><span class="line"> acount_high_down = acount_high_down - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 初始化左右边界为宽度总值最大的值的索引</span></span><br><span class="line"> acount_width_left = np.argmax(add_high)</span><br><span class="line"> acount_width_right = np.argmax(add_high)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将左边界坐标值左移,直到没有遇到白色点停止,此为数字的左边界</span></span><br><span class="line"> <span class="keyword">while</span> add_high[acount_width_left] != <span class="number">0</span>:</span><br><span class="line"> acount_width_left = acount_width_left - <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将右边界坐标值右移,直到没有遇到白色点停止,此为数字的右边界</span></span><br><span class="line"> <span class="keyword">while</span> add_high[acount_width_right] != <span class="number">0</span>:</span><br><span class="line"> acount_width_right = acount_width_right + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 求出宽和高的间距</span></span><br><span class="line"> width_spacing = acount_width_right - acount_width_left</span><br><span class="line"> high_spacing = acount_high_up - acount_high_down</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 求出宽和高的间距差</span></span><br><span class="line"> poor = width_spacing - high_spacing</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将数字进行正方形分割,目的是方便之后进行图像压缩</span></span><br><span class="line"> <span class="keyword">if</span> poor > <span class="number">0</span>:</span><br><span class="line"> tailor_image = img[acount_high_down - poor // <span class="number">2</span> - <span class="number">5</span>:acount_high_up + poor - poor // <span class="number">2</span> + <span class="number">5</span>, acount_width_left - <span class="number">5</span>:acount_width_right + <span class="number">5</span>]</span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> tailor_image = img[acount_high_down - <span class="number">5</span>:acount_high_up + <span class="number">5</span>, acount_width_left + poor // <span class="number">2</span> - <span class="number">5</span>:acount_width_right - poor + poor // <span class="number">2</span> + <span class="number">5</span>]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># ==================================================== #</span></span><br><span class="line"> <span class="comment"># ======================小图处理======================= #</span></span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将裁剪后的图片进行灰度化</span></span><br><span class="line"> gray_img = cv2.cvtColor(tailor_image , cv2.COLOR_BGR2GRAY)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 高斯去噪</span></span><br><span class="line"> gauss_img = cv2.GaussianBlur(gray_img, (<span class="number">5</span>,<span class="number">5</span>), <span class="number">0</span>, <span class="number">0</span>, cv2.BORDER_DEFAULT)</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将图像形状调整到28*28大小</span></span><br><span class="line"> zoom_image = cv2.resize(gauss_img, (<span class="number">28</span>, <span class="number">28</span>))</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 获取图像的高和宽</span></span><br><span class="line"> high = zoom_image.shape[<span class="number">0</span>]</span><br><span class="line"> wide = zoom_image.shape[<span class="number">1</span>]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 将图像每个点的灰度值进行阈值比较</span></span><br><span class="line"> <span class="keyword">for</span> h <span class="keyword">in</span> range(high):</span><br><span class="line"> <span class="keyword">for</span> w <span class="keyword">in</span> range(wide):</span><br><span class="line"></span><br><span class="line"> <span class="comment"># 若灰度值大于100,则判断为背景并赋值0,否则将深灰度值变白处理</span></span><br><span class="line"> <span class="keyword">if</span> zoom_image[h][w] > <span class="number">100</span>:</span><br><span class="line"> zoom_image[h][w] = <span class="number">0</span></span><br><span class="line"> <span class="keyword">else</span>:</span><br><span class="line"> zoom_image[h][w] = <span class="number">255</span> - zoom_image[h][w]</span><br><span class="line"></span><br><span class="line"> <span class="comment"># ==================================================== #</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> zoom_image</span><br></pre></td></tr></table></figure>在此,我在纸上写了个6,如下图所示:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/test6.jpeg" alt><br>然后是对图像进行分割,首先要介绍下我分割图像的方法。下面是一张进行canny边缘检测后的6:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/canny_test6.png" alt><br>在这里这个6有个特点,就是被白边给包围着了,因为白色的灰度值为255,黑色的灰度值为0,所以我就假设以高为很坐标,然后每个高对应着的宽的灰度值进行相加。所以会很明显发现就6这个字的整体的值比较聚集,当然有可能有零星的散点,但并不影响对6所在位置的判断。最后以高为例,得到的值的坐标图如下:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/figure.png" alt><br>因为最大值比较容易找到,所以就找到最大值然后向两边延伸,当发现值为零时就可以把边界给标定出来了。<br>最后进行分割分割注意的是后面对图像进行裁剪的时候是将宽和高较长的一边减去较短的一边然后除以2平分给较短的一边的两侧,为了防止边缘检测没有包裹着数字,于是在数字四周都加了五个像素点进行裁剪,最后裁剪出来的效果如下:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/little_test6.png" alt><br>这个图片就是上述代码中的tailor_image所显示出来的图片,因为显示图片的代码只作为测试使用,而且又很简单,这里就没有展示出来。<br>好了,接下来就是要对辛辛苦苦裁剪出来的小图进行图像进行处理了,首先还是最基本的灰度化和高斯滤波处理,然后就是对图像进行大小转换,因为MNIST数据形状就是$28<em>28$所以也要将输入图片转换成$28</em>28$的大小。大小转换完成后,就是要完成把灰度图转换成背景为0,然后数字变成白色的图片,因为这样和MNIST数据集里的数字图片特别的像。在这里我用了阈值控制的方法将背景变成黑色的。至于这100当然是将图片的灰度值打出来后观察得出来的。但是这种方法是比较危险的,因为这样的鲁棒性并不强,但后面如果要加强鲁棒性则同样可以用边缘检测把数字包裹住,然后数字之外的背景清零,这确实是一个很好的思路,但在这里就建议的用阈值控制的方法来实现背景黑化了。黑化背景后当然就是将数字白化了,之前有将数字部分都是255值,但发现识别的效果并不理想,所以这里我采用了用255-原先数字的值,这样如果原先的数字黑度深的部分就会变成白色程度深,就简单的实现了数字边缘浅,中间深的变换。最后处理得到的图像如下:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/end_test6.png" alt><br>虽说看起来没有第一张图那么完美,但大概还是能达到验证数据所需的要求了。至此,数据预处理已经完成了,接下来就是激动的预测了。</li></ol><h3 id="预测"><a href="#预测" class="headerlink" title="预测"></a>预测</h3><p>预测代码如下:<br><figure class="highlight python"><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">import</span> torch</span><br><span class="line"></span><br><span class="line"><span class="comment"># pretreatment.py为上面图片预处理的文件名,导入图片预处理文件</span></span><br><span class="line"><span class="keyword">import</span> pretreatment <span class="keyword">as</span> PRE</span><br><span class="line"></span><br><span class="line"><span class="comment"># 加载网络模型</span></span><br><span class="line">net = torch.load(<span class="string">'weight/test.pkl'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 得到返回的待预测图片值,就是pretreatment.py中的zoom_image</span></span><br><span class="line">img = PRE.image_preprocessing()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将待预测图片转换形状</span></span><br><span class="line">inputs = img.reshape(<span class="number">-1</span>, <span class="number">784</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 输入数据转换成tensor张量类型,并转换成浮点类型</span></span><br><span class="line">inputs = torch.from_numpy(inputs)</span><br><span class="line">inputs = inputs.float()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 丢入网络进行预测,得到预测数据</span></span><br><span class="line">predict = net(inputs)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印对应的最后的预测结果</span></span><br><span class="line">print(<span class="string">"The number in this picture is {}"</span>.format(torch.argmax(predict).detach().numpy()))</span><br></pre></td></tr></table></figure><br>最后得到结果如图所示:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB%EF%BC%88%E8%AF%86%E5%88%AB%E7%BA%B8%E4%B8%8A%E7%9A%84%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%EF%BC%89/answer.png" alt><br>这样,整个手写数字识别基本已经完成了。</p>]]></content>
<summary type="html"><h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E6%89%8B%E5%8</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="手写数字识别" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="手写数字识别" scheme="https://huanqgingyu.top/tags/%E6%89%8B%E5%86%99%E6%95%B0%E5%AD%97%E8%AF%86%E5%88%AB/"/>
</entry>
<entry>
<title>前馈神经网络解决XOR问题</title>
<link href="https://huanqgingyu.top/2021/03/31/%E5%89%8D%E9%A6%88%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%86%B3XOR%E9%97%AE%E9%A2%98/"/>
<id>https://huanqgingyu.top/2021/03/31/%E5%89%8D%E9%A6%88%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%86%B3XOR%E9%97%AE%E9%A2%98/</id>
<published>2021-03-30T16:03:20.000Z</published>
<updated>2022-08-11T13:20:56.919Z</updated>
<content type="html"><![CDATA[<h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E5%89%8D%E9%A6%88%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%86%B3XOR%E9%97%AE%E9%A2%98/problem2.png" alt></p><h3 id="网络设计"><a href="#网络设计" class="headerlink" title="网络设计"></a>网络设计</h3><p>根据题意可以做出如下设计:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E5%89%8D%E9%A6%88%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%86%B3XOR%E9%97%AE%E9%A2%98/answer1.jpeg" alt></p><h3 id="手算"><a href="#手算" class="headerlink" title="手算"></a>手算</h3><p>手算结果如下:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E5%89%8D%E9%A6%88%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%86%B3XOR%E9%97%AE%E9%A2%98/answer2.jpeg" alt></p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.optim <span class="keyword">as</span> optim</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化数据</span></span><br><span class="line">data = np.array([[<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">1</span>],</span><br><span class="line">[<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">0</span>]], dtype=<span class="string">'float32'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将输入输出值导入对应的列表</span></span><br><span class="line">x = data[:, :<span class="number">2</span>]</span><br><span class="line">y = data[:, <span class="number">2</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义异或网络类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">XOR_net</span>(<span class="params">nn.Module</span>):</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self</span>):</span></span><br><span class="line">super(XOR_net, self).__init__()</span><br><span class="line"></span><br><span class="line">self.Conn_layer = nn.Sequential(</span><br><span class="line">nn.Linear(<span class="number">2</span>, <span class="number">2</span>),</span><br><span class="line">nn.ReLU(),</span><br><span class="line">nn.Linear(<span class="number">2</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="keyword">def</span> <span class="title">forward</span>(<span class="params">self, x</span>):</span></span><br><span class="line">output1 = self.Conn_layer(x)</span><br><span class="line">output = output1.reshape(<span class="number">-1</span>, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义一个异或网络对象</span></span><br><span class="line">net = XOR_net()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将输入输出数值转换成tensor张量</span></span><br><span class="line">x = torch.Tensor(x.reshape(<span class="number">-1</span>, <span class="number">2</span>))</span><br><span class="line">y = torch.Tensor(y.reshape(<span class="number">-1</span>, <span class="number">1</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义一个均方损失函数对象</span></span><br><span class="line">criterion = nn.MSELoss(reduction=<span class="string">'mean'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化参数对象</span></span><br><span class="line">optimizer = optim.SGD(net.parameters(), lr=<span class="number">0.01</span>, momentum=<span class="number">0.9</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行迭代训练</span></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">1000</span>):</span><br><span class="line">out = net(x)</span><br><span class="line">loss = criterion(out, y)</span><br><span class="line">optimizer.zero_grad()</span><br><span class="line">loss.backward()</span><br><span class="line">optimizer.step()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义一个测试网络</span></span><br><span class="line">test = net(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印输入输出值</span></span><br><span class="line">print(<span class="string">"input is {}"</span>.format(x.detach().numpy()))</span><br><span class="line">print(<span class="string">'out is {}'</span>.format(test.detach().numpy()))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 层数脚下表</span></span><br><span class="line">i = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印每层的权重和偏执</span></span><br><span class="line"><span class="keyword">for</span> layer <span class="keyword">in</span> net.modules():</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印对应层的权重和偏执</span></span><br><span class="line"><span class="keyword">if</span> isinstance(layer, nn.Linear):</span><br><span class="line">print(<span class="string">"weight_{} is {}"</span>.format(i, layer.weight.detach().numpy()))</span><br><span class="line">print(<span class="string">"bias_{} is {}"</span>.format(i, layer.bias.detach().numpy()))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 层数计数加一</span></span><br><span class="line">i += <span class="number">1</span></span><br></pre></td></tr></table></figure><h3 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h3><p>代码运行结果如下:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E5%89%8D%E9%A6%88%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E8%A7%A3%E5%86%B3XOR%E9%97%AE%E9%A2%98/answer3.png" alt><br>不足的一点就是relu激活函数十分不稳定,所以有时可能得不到理想的结果。</p>]]></content>
<summary type="html"><h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E5%89%8D%E9%A</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="基础知识" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="神经网络" scheme="https://huanqgingyu.top/tags/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/"/>
</entry>
<entry>
<title>计算神经网络的值</title>
<link href="https://huanqgingyu.top/2021/03/31/%E8%AE%A1%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%80%BC/"/>
<id>https://huanqgingyu.top/2021/03/31/%E8%AE%A1%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%80%BC/</id>
<published>2021-03-30T16:02:52.000Z</published>
<updated>2022-08-11T13:18:08.932Z</updated>
<content type="html"><![CDATA[<h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E8%AE%A1%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%80%BC/problem1.png" alt></p><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p>在手写数字识别的网络中有十个输出值,网络是通过找到最大的输出值的概率来判断识别的数字是多少。比如识别结果1对应着输出值<script type="math/tex">\begin{bmatrix}0 &1 &0 &0 &0 &0 &0 &0 &0 &0 \end{bmatrix}</script>当然,根据题意这里的1是指值大于0.99,0是指值小于0.01。如果要将最后的结果转换成四位的二进制表示,上述输出值最后需要转换成<script type="math/tex">\begin{bmatrix}0 &0 &0 &1 \end{bmatrix}</script>。解题思路其实就等价于设计一个两层的网络,输入值就是那十个数,输出值就是对应的四位二进制数,然后对这个网络进行训练就能够得到合适的权重和偏置。</p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> torch</span><br><span class="line"><span class="keyword">import</span> numpy <span class="keyword">as</span> np</span><br><span class="line"><span class="keyword">import</span> torch.nn <span class="keyword">as</span> nn</span><br><span class="line"><span class="keyword">import</span> torch.optim <span class="keyword">as</span> optim</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化基本参数</span></span><br><span class="line">data = np.array([[<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 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">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><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">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><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">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">1</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">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 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">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">1</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 class="number">0</span>, <span class="number">1</span>, <span class="number">1</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">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">1</span>, <span class="number">1</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">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">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">1</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">0</span>, <span class="number">1</span>]], dtype=<span class="string">'float32'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取对应的输入值和输出值并放到相应的列表中</span></span><br><span class="line">x = data[:, :<span class="number">10</span>]</span><br><span class="line">y = data[:, <span class="number">10</span>:<span class="number">14</span>]</span><br><span class="line"></span><br><span class="line"><span class="comment"># 搭建一个两层的网络</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">net</span>(<span class="params">nn.Module</span>):</span></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">__init__</span>(<span class="params">self</span>):</span></span><br><span class="line">super(net, self).__init__()</span><br><span class="line"></span><br><span class="line">self.Conn_layers = nn.Sequential(</span><br><span class="line">nn.Linear(<span class="number">10</span>, <span class="number">4</span>),</span><br><span class="line">nn.Sigmoid()</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">def</span> <span class="title">forward</span>(<span class="params">self, x</span>):</span></span><br><span class="line">output1 = self.Conn_layers(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 转换成二维列表</span></span><br><span class="line">output = output1.reshape(<span class="number">-1</span>, <span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> output</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化网络对象</span></span><br><span class="line">net = net()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 将输入输出值转换成tensor张量</span></span><br><span class="line">x = torch.Tensor(x.reshape(<span class="number">-1</span>, <span class="number">10</span>))</span><br><span class="line">y = torch.Tensor(y.reshape(<span class="number">-1</span>, <span class="number">4</span>))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化一个交叉熵损失函数对象</span></span><br><span class="line">loss_function = nn.BCELoss()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 初始化参数对象</span></span><br><span class="line">optimizer = optim.SGD(net.parameters(), lr=<span class="number">0.01</span>, momentum=<span class="number">0.9</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 开始迭代训练</span></span><br><span class="line"><span class="keyword">for</span> epoch <span class="keyword">in</span> range(<span class="number">10000</span>):</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取输出值</span></span><br><span class="line">out = net(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取损失</span></span><br><span class="line">loss = loss_function(out, y)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 梯度清零</span></span><br><span class="line">optimizer.zero_grad()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 进行反向传播</span></span><br><span class="line">loss.backward()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 参数更新</span></span><br><span class="line">optimizer.step()</span><br><span class="line"></span><br><span class="line"><span class="comment"># 定义个测试网络(此时网络的参数已是训练好的参数)</span></span><br><span class="line">test = net(x)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印测试的输入输出值</span></span><br><span class="line">print(<span class="string">"input is {}"</span>.format(x.detach().numpy()))</span><br><span class="line">print(<span class="string">'out is {}'</span>.format(test.detach().numpy()))</span><br><span class="line"></span><br><span class="line"><span class="comment"># 获取网络每一层参数</span></span><br><span class="line"><span class="keyword">for</span> layer <span class="keyword">in</span> net.modules():</span><br><span class="line"><span class="keyword">if</span> isinstance(layer, nn.Linear):</span><br><span class="line"></span><br><span class="line"><span class="comment"># 打印权重和偏置</span></span><br><span class="line">print(<span class="string">"weight is {}"</span>.format(layer.weight.detach().numpy()))</span><br><span class="line">print(<span class="string">"bias is {}"</span>.format(layer.bias.detach().numpy()))</span><br></pre></td></tr></table></figure><h3 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h3><p>结果如下所示:<br><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E8%AE%A1%E7%AE%97%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C%E7%9A%84%E5%80%BC/answer.png" alt><br>从out里的值可以看到值是0.9以上的可以看做1,可以通过输入数据的后四位值来对结果进行检验。</p>]]></content>
<summary type="html"><h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p><img src= "/img/loading.gif" data-lazy-src="/2021/03/31/%E8%AE%A1%E7%A</summary>
<category term="计算机视觉" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="基础知识" scheme="https://huanqgingyu.top/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
<category term="计算机视觉" scheme="https://huanqgingyu.top/tags/%E8%AE%A1%E7%AE%97%E6%9C%BA%E8%A7%86%E8%A7%89/"/>
<category term="神经网络" scheme="https://huanqgingyu.top/tags/%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C/"/>
</entry>
</feed>