-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
821 lines (395 loc) · 621 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title></title>
<link href="/2024/05/18/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93/"/>
<url>/2024/05/18/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93/</url>
<content type="html"><![CDATA[<h1 id="1-redis缓存原理,为什么快?"><a href="#1-redis缓存原理,为什么快?" class="headerlink" title="1.redis缓存原理,为什么快?"></a>1.redis缓存原理,为什么快?</h1><h2 id="主要有以下几个原因:"><a href="#主要有以下几个原因:" class="headerlink" title="主要有以下几个原因:"></a>主要有以下几个原因:</h2><ol><li><h2 id="内存存储:Redis将数据存储在内存中而不是磁盘上,内存的读写速度比磁盘快得多。"><a href="#内存存储:Redis将数据存储在内存中而不是磁盘上,内存的读写速度比磁盘快得多。" class="headerlink" title="内存存储:Redis将数据存储在内存中而不是磁盘上,内存的读写速度比磁盘快得多。"></a><em>内存存储:Redis将数据存储在内存中而不是磁盘上,内存的读写速度比磁盘快得多。</em></h2></li><li><h2 id="非关系型:作为一个键值存储系统,Redis不需要像关系型数据库那样进行复杂的连接操作,这减少了时间复杂度。"><a href="#非关系型:作为一个键值存储系统,Redis不需要像关系型数据库那样进行复杂的连接操作,这减少了时间复杂度。" class="headerlink" title="非关系型:作为一个键值存储系统,Redis不需要像关系型数据库那样进行复杂的连接操作,这减少了时间复杂度。"></a><em>非关系型:作为一个键值存储系统,Redis不需要像关系型数据库那样进行复杂的连接操作,这减少了时间复杂度。</em></h2></li><li><h2 id="单线程模型:Redis使用单线程模型处理命令,避免了多线程的上下文切换和竞态条件,让每个操作都极为快速且安全。"><a href="#单线程模型:Redis使用单线程模型处理命令,避免了多线程的上下文切换和竞态条件,让每个操作都极为快速且安全。" class="headerlink" title="单线程模型:Redis使用单线程模型处理命令,避免了多线程的上下文切换和竞态条件,让每个操作都极为快速且安全。"></a><em>单线程模型:Redis使用单线程模型处理命令,避免了多线程的上下文切换和竞态条件,让每个操作都极为快速且安全。</em></h2></li><li><h2 id="高效的数据结构:Redis支持多种类型的数据结构如字符串、列表、集合、有序集合等,并且对它们进行了优化,使得操作它们非常快速。"><a href="#高效的数据结构:Redis支持多种类型的数据结构如字符串、列表、集合、有序集合等,并且对它们进行了优化,使得操作它们非常快速。" class="headerlink" title="高效的数据结构:Redis支持多种类型的数据结构如字符串、列表、集合、有序集合等,并且对它们进行了优化,使得操作它们非常快速。"></a><em>高效的数据结构:Redis支持多种类型的数据结构如字符串、列表、集合、有序集合等,并且对它们进行了优化,使得操作它们非常快速。</em></h2></li><li><h2 id="持久化选项:即使是以内存为主的存储系统,Redis也提供了RDB和AOF两种数据持久化方法,可以根据需要选择适合的持久化策略。"><a href="#持久化选项:即使是以内存为主的存储系统,Redis也提供了RDB和AOF两种数据持久化方法,可以根据需要选择适合的持久化策略。" class="headerlink" title="持久化选项:即使是以内存为主的存储系统,Redis也提供了RDB和AOF两种数据持久化方法,可以根据需要选择适合的持久化策略。"></a><em>持久化选项:即使是以内存为主的存储系统,Redis也提供了RDB和AOF两种数据持久化方法,可以根据需要选择适合的持久化策略。</em></h2></li><li><h2 id="内建的复制特性:Redis支持主从同步复制,用于数据备份、读写分离和提升系统的扩展性和可用性。"><a href="#内建的复制特性:Redis支持主从同步复制,用于数据备份、读写分离和提升系统的扩展性和可用性。" class="headerlink" title="内建的复制特性:Redis支持主从同步复制,用于数据备份、读写分离和提升系统的扩展性和可用性。"></a><em>内建的复制特性:Redis支持主从同步复制,用于数据备份、读写分离和提升系统的扩展性和可用性。</em></h2></li></ol><h2 id="string:字符串类型,可以存储普通字符串、JSON字符串,也可以存储对象系列化之后的字符串"><a href="#string:字符串类型,可以存储普通字符串、JSON字符串,也可以存储对象系列化之后的字符串" class="headerlink" title="string:字符串类型,可以存储普通字符串、JSON字符串,也可以存储对象系列化之后的字符串"></a><em>string:字符串类型,可以存储普通字符串、JSON字符串,也可以存储对象系列化之后的字符串</em></h2><h2 id="hash:哈希类型,类似于Java中的HashMap,比较适合存储对象"><a href="#hash:哈希类型,类似于Java中的HashMap,比较适合存储对象" class="headerlink" title="hash:哈希类型,类似于Java中的HashMap,比较适合存储对象"></a><em>hash:哈希类型,类似于Java中的HashMap,比较适合存储对象</em></h2><h2 id="list:列表类型,底层是一个顺序链表,可以从两端添加或移除元素,元素是有序的,可重复的"><a href="#list:列表类型,底层是一个顺序链表,可以从两端添加或移除元素,元素是有序的,可重复的" class="headerlink" title="list:列表类型,底层是一个顺序链表,可以从两端添加或移除元素,元素是有序的,可重复的"></a><em>list:列表类型,底层是一个顺序链表,可以从两端添加或移除元素,元素是有序的,可重复的</em></h2><h2 id="set:无序集合,没有重复元素"><a href="#set:无序集合,没有重复元素" class="headerlink" title="set:无序集合,没有重复元素"></a><em>set:无序集合,没有重复元素</em></h2><h2 id="zset:有序集合,没有重复元素,且集合中每个元素关联一个分数,可以根据分数进行排序"><a href="#zset:有序集合,没有重复元素,且集合中每个元素关联一个分数,可以根据分数进行排序" class="headerlink" title="zset:有序集合,没有重复元素,且集合中每个元素关联一个分数,可以根据分数进行排序"></a><em>zset:有序集合,没有重复元素,且集合中每个元素关联一个分数,可以根据分数进行排序</em></h2><h1 id="2-redis缓存淘汰机制是怎么样的?"><a href="#2-redis缓存淘汰机制是怎么样的?" class="headerlink" title="2.redis缓存淘汰机制是怎么样的?"></a>2.redis缓存淘汰机制是怎么样的?</h1><p>Redis 缓存淘汰机制(Cache Eviction Policy)用于管理内存使用,当 Redis 实例达到配置的最大内存限制时,Redis 会根据指定的策略淘汰(移除)一些键值对,以释放内存空间。以下是 Redis 提供的几种缓存淘汰策略及其工作原理:</p><h3 id="1-noeviction"><a href="#1-noeviction" class="headerlink" title="1. noeviction"></a>1. noeviction</h3><ul><li><h3 id="描述:当内存限制达到时,不会再进行任何操作。新的写操作将会报错(返回错误信息)。"><a href="#描述:当内存限制达到时,不会再进行任何操作。新的写操作将会报错(返回错误信息)。" class="headerlink" title="描述:当内存限制达到时,不会再进行任何操作。新的写操作将会报错(返回错误信息)。"></a><strong>描述</strong>:当内存限制达到时,不会再进行任何操作。新的写操作将会报错(返回错误信息)。</h3></li><li><strong>适用场景</strong>:适合不希望任何数据被自动删除的场景,例如需要严格控制数据集大小的情况下。</li></ul><h3 id="2-allkeys-lru"><a href="#2-allkeys-lru" class="headerlink" title="2. allkeys-lru"></a>2. allkeys-lru</h3><ul><li><h3 id="描述:在所有键中,优先移除最少使用(最近最少使用,Least-Recently-Used)的键。"><a href="#描述:在所有键中,优先移除最少使用(最近最少使用,Least-Recently-Used)的键。" class="headerlink" title="描述:在所有键中,优先移除最少使用(最近最少使用,Least Recently Used)的键。"></a><strong>描述</strong>:在所有键中,优先移除最少使用(最近最少使用,Least Recently Used)的键。</h3></li><li><strong>适用场景</strong>:适用于希望将最常用数据保存在缓存中的场景。</li></ul><h3 id="3-volatile-lru"><a href="#3-volatile-lru" class="headerlink" title="3. volatile-lru"></a>3. volatile-lru</h3><ul><li><h3 id="描述:在设置了过期时间的键中,优先移除最少使用的键。"><a href="#描述:在设置了过期时间的键中,优先移除最少使用的键。" class="headerlink" title="描述:在设置了过期时间的键中,优先移除最少使用的键。"></a><strong>描述</strong>:在设置了过期时间的键中,优先移除最少使用的键。</h3></li><li><strong>适用场景</strong>:适用于只希望淘汰那些有过期时间的键,并保留无过期时间的数据的场景。</li></ul><h3 id="4-allkeys-random"><a href="#4-allkeys-random" class="headerlink" title="4. allkeys-random"></a>4. allkeys-random</h3><ul><li><strong>描述</strong>:在所有键中,随机选择一些键进行移除。</li><li><strong>适用场景</strong>:适用于不关心具体哪一部分数据被移除的场景。</li></ul><h3 id="5-volatile-random"><a href="#5-volatile-random" class="headerlink" title="5. volatile-random"></a>5. volatile-random</h3><ul><li><strong>描述</strong>:在设置了过期时间的键中,随机选择一些键进行移除。</li><li><strong>适用场景</strong>:适用于只希望淘汰那些有过期时间的键,且不关心具体哪些键被移除的场景。</li></ul><h3 id="6-volatile-ttl"><a href="#6-volatile-ttl" class="headerlink" title="6. volatile-ttl"></a>6. volatile-ttl</h3><ul><li><strong>描述</strong>:在设置了过期时间的键中,优先移除那些 TTL (剩余生存时间)较短的键。</li><li><strong>适用场景</strong>:适用于希望优先移除即将过期的键,以便保留有效期较长的键的场景。</li></ul><h3 id="7-allkeys-lfu-Redis-4-0-及以后版本"><a href="#7-allkeys-lfu-Redis-4-0-及以后版本" class="headerlink" title="7. allkeys-lfu (Redis 4.0 及以后版本)"></a>7. allkeys-lfu (Redis 4.0 及以后版本)</h3><ul><li><strong>描述</strong>:在所有键中,优先移除使用频率最少的键(Least Frequently Used)。</li><li><strong>适用场景</strong>:适用于希望保留访问频率较高的数据的场景。</li></ul><h3 id="8-volatile-lfu-Redis-4-0-及以后版本"><a href="#8-volatile-lfu-Redis-4-0-及以后版本" class="headerlink" title="8. volatile-lfu (Redis 4.0 及以后版本)"></a>8. volatile-lfu (Redis 4.0 及以后版本)</h3><ul><li><strong>描述</strong>:在设置了过期时间的键中,优先移除使用频率最少的键。</li><li><strong>适用场景</strong>:适用于希望在有过期时间的键中,保留访问频率较高的数据的场景。</li></ul><h3 id="配置缓存淘汰策略"><a href="#配置缓存淘汰策略" class="headerlink" title="配置缓存淘汰策略"></a>配置缓存淘汰策略</h3><p>可以在 Redis 配置文件 <code>redis.conf</code> 中设置缓存淘汰策略:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">maxmemory-policy allkeys-lru</span><br></pre></td></tr></tbody></table></figure><p>或者在运行时使用 <code>CONFIG SET</code> 命令设置:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONFIG SET maxmemory-policy allkeys-lru</span><br></pre></td></tr></tbody></table></figure><h3 id="配置最大内存限制"><a href="#配置最大内存限制" class="headerlink" title="配置最大内存限制"></a>配置最大内存限制</h3><p>同样地,可以在 <code>redis.conf</code> 中设置最大内存限制:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">maxmemory 256mb</span><br></pre></td></tr></tbody></table></figure><p>或者在运行时使用 <code>CONFIG SET</code> 命令设置:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CONFIG SET maxmemory 256mb</span><br></pre></td></tr></tbody></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>Redis 提供了多种缓存淘汰策略,以满足不同场景下的需求。通过合理配置缓存淘汰策略和最大内存限制,可以有效地管理 Redis 实例的内存使用,确保在内存限制达到时系统的稳定性和性能。</p><h1 id="3-redis的io多路复用是什么?"><a href="#3-redis的io多路复用是什么?" class="headerlink" title="3.redis的io多路复用是什么?"></a>3.redis的io多路复用是什么?</h1><h2 id="Redis的I-O多路复用是一种高效的网络事件处理机制,它允许Redis服务器同时处理多个连接而不会阻塞。多路复用(multiplexing)意味着Redis可以通过一个单一的线程同时监听多个文件描述符(如套接字),并在任何一个描述符变得可读或可写时,及时作出响应。这样,Redis就能够高效地管理大量的并发连接。"><a href="#Redis的I-O多路复用是一种高效的网络事件处理机制,它允许Redis服务器同时处理多个连接而不会阻塞。多路复用(multiplexing)意味着Redis可以通过一个单一的线程同时监听多个文件描述符(如套接字),并在任何一个描述符变得可读或可写时,及时作出响应。这样,Redis就能够高效地管理大量的并发连接。" class="headerlink" title="Redis的I/O多路复用是一种高效的网络事件处理机制,它允许Redis服务器同时处理多个连接而不会阻塞。多路复用(multiplexing)意味着Redis可以通过一个单一的线程同时监听多个文件描述符(如套接字),并在任何一个描述符变得可读或可写时,及时作出响应。这样,Redis就能够高效地管理大量的并发连接。"></a>Redis的I/O多路复用是一种高效的网络事件处理机制,它允许Redis服务器同时处理多个连接而不会阻塞。多路复用(multiplexing)意味着Redis可以通过一个单一的线程同时监听多个文件描述符(如套接字),并在任何一个描述符变得可读或可写时,及时作出响应。这样,Redis就能够高效地管理大量的并发连接。</h2><h2 id="Redis使用的是基于事件驱动的I-O多路复用模型,主要依赖操作系统提供的多路复用系统调用,例如:"><a href="#Redis使用的是基于事件驱动的I-O多路复用模型,主要依赖操作系统提供的多路复用系统调用,例如:" class="headerlink" title="Redis使用的是基于事件驱动的I/O多路复用模型,主要依赖操作系统提供的多路复用系统调用,例如:"></a>Redis使用的是基于事件驱动的I/O多路复用模型,主要依赖操作系统提供的多路复用系统调用,例如:</h2><ul><li><h2 id="select:这是一个最早的多路复用接口,但它有一些限制,比如文件描述符数量限制和性能问题。"><a href="#select:这是一个最早的多路复用接口,但它有一些限制,比如文件描述符数量限制和性能问题。" class="headerlink" title="select:这是一个最早的多路复用接口,但它有一些限制,比如文件描述符数量限制和性能问题。"></a><strong>select</strong>:这是一个最早的多路复用接口,但它有一些限制,比如文件描述符数量限制和性能问题。</h2></li><li><h2 id="poll:它克服了一些select的缺点,但在大多数情况下,仍然不如更现代的接口高效。"><a href="#poll:它克服了一些select的缺点,但在大多数情况下,仍然不如更现代的接口高效。" class="headerlink" title="poll:它克服了一些select的缺点,但在大多数情况下,仍然不如更现代的接口高效。"></a><strong>poll</strong>:它克服了一些select的缺点,但在大多数情况下,仍然不如更现代的接口高效。</h2></li><li><h2 id="epoll:这是Linux特有的接口,提供了更高效的事件通知机制,非常适合高并发场景。"><a href="#epoll:这是Linux特有的接口,提供了更高效的事件通知机制,非常适合高并发场景。" class="headerlink" title="epoll:这是Linux特有的接口,提供了更高效的事件通知机制,非常适合高并发场景。"></a><strong>epoll</strong>:这是Linux特有的接口,提供了更高效的事件通知机制,非常适合高并发场景。</h2></li><li><h2 id="kqueue:这是FreeBSD、OpenBSD、macOS等系统的多路复用接口,类似于epoll,但在不同的操作系统上提供了不同的实现。"><a href="#kqueue:这是FreeBSD、OpenBSD、macOS等系统的多路复用接口,类似于epoll,但在不同的操作系统上提供了不同的实现。" class="headerlink" title="kqueue:这是FreeBSD、OpenBSD、macOS等系统的多路复用接口,类似于epoll,但在不同的操作系统上提供了不同的实现。"></a><strong>kqueue</strong>:这是FreeBSD、OpenBSD、macOS等系统的多路复用接口,类似于epoll,但在不同的操作系统上提供了不同的实现。</h2></li></ul><h2 id="Redis内部会根据操作系统的类型和特性选择最合适的多路复用机制。具体工作流程如下:"><a href="#Redis内部会根据操作系统的类型和特性选择最合适的多路复用机制。具体工作流程如下:" class="headerlink" title="Redis内部会根据操作系统的类型和特性选择最合适的多路复用机制。具体工作流程如下:"></a>Redis内部会根据操作系统的类型和特性选择最合适的多路复用机制。具体工作流程如下:</h2><ol><li><h2 id="事件注册:Redis将感兴趣的I-O事件(如客户端连接的读写事件)注册到多路复用器。"><a href="#事件注册:Redis将感兴趣的I-O事件(如客户端连接的读写事件)注册到多路复用器。" class="headerlink" title="事件注册:Redis将感兴趣的I/O事件(如客户端连接的读写事件)注册到多路复用器。"></a><strong>事件注册</strong>:Redis将感兴趣的I/O事件(如客户端连接的读写事件)注册到多路复用器。</h2></li><li><h2 id="事件等待:多路复用器等待这些事件的发生。期间,Redis处于非阻塞状态,不会因为等待某个I-O事件而停滞。"><a href="#事件等待:多路复用器等待这些事件的发生。期间,Redis处于非阻塞状态,不会因为等待某个I-O事件而停滞。" class="headerlink" title="事件等待:多路复用器等待这些事件的发生。期间,Redis处于非阻塞状态,不会因为等待某个I/O事件而停滞。"></a><strong>事件等待</strong>:多路复用器等待这些事件的发生。期间,Redis处于非阻塞状态,不会因为等待某个I/O事件而停滞。</h2></li><li><h2 id="事件处理:一旦有I-O事件发生,多路复用器会将这些事件通知Redis,Redis则根据事件类型执行相应的处理,如读数据、写数据、接受新连接等。"><a href="#事件处理:一旦有I-O事件发生,多路复用器会将这些事件通知Redis,Redis则根据事件类型执行相应的处理,如读数据、写数据、接受新连接等。" class="headerlink" title="事件处理:一旦有I/O事件发生,多路复用器会将这些事件通知Redis,Redis则根据事件类型执行相应的处理,如读数据、写数据、接受新连接等。"></a><strong>事件处理</strong>:一旦有I/O事件发生,多路复用器会将这些事件通知Redis,Redis则根据事件类型执行相应的处理,如读数据、写数据、接受新连接等。</h2></li><li><h2 id="事件循环:上述过程在一个循环中反复进行,这就是Redis的事件循环机制,使得Redis能够高效地处理高并发的网络请求。"><a href="#事件循环:上述过程在一个循环中反复进行,这就是Redis的事件循环机制,使得Redis能够高效地处理高并发的网络请求。" class="headerlink" title="事件循环:上述过程在一个循环中反复进行,这就是Redis的事件循环机制,使得Redis能够高效地处理高并发的网络请求。"></a><strong>事件循环</strong>:上述过程在一个循环中反复进行,这就是Redis的事件循环机制,使得Redis能够高效地处理高并发的网络请求。</h2></li></ol><h1 id="这种机制的关键优势在于:"><a href="#这种机制的关键优势在于:" class="headerlink" title="这种机制的关键优势在于:"></a>这种机制的关键优势在于:</h1><ul><li><h2 id="高效:避免了线程切换和锁的开销,充分利用了单线程的性能。"><a href="#高效:避免了线程切换和锁的开销,充分利用了单线程的性能。" class="headerlink" title="高效:避免了线程切换和锁的开销,充分利用了单线程的性能。"></a><strong>高效</strong>:避免了线程切换和锁的开销,充分利用了单线程的性能。</h2></li><li><h2 id="可伸缩:能够轻松处理数以万计的并发连接,而不会显著增加资源消耗。"><a href="#可伸缩:能够轻松处理数以万计的并发连接,而不会显著增加资源消耗。" class="headerlink" title="可伸缩:能够轻松处理数以万计的并发连接,而不会显著增加资源消耗。"></a><strong>可伸缩</strong>:能够轻松处理数以万计的并发连接,而不会显著增加资源消耗。</h2></li><li><h2 id="简单:简化了代码逻辑,降低了复杂性和维护成本。"><a href="#简单:简化了代码逻辑,降低了复杂性和维护成本。" class="headerlink" title="简单:简化了代码逻辑,降低了复杂性和维护成本。"></a><strong>简单</strong>:简化了代码逻辑,降低了复杂性和维护成本。</h2></li></ul><h2 id="通过I-O多路复用,Redis实现了高效的网络通信能力,是其高性能的核心原因之一。"><a href="#通过I-O多路复用,Redis实现了高效的网络通信能力,是其高性能的核心原因之一。" class="headerlink" title="通过I/O多路复用,Redis实现了高效的网络通信能力,是其高性能的核心原因之一。"></a>通过I/O多路复用,Redis实现了高效的网络通信能力,是其高性能的核心原因之一。</h2><h1 id="4-redis集群的数据一致性你是怎么解决的?"><a href="#4-redis集群的数据一致性你是怎么解决的?" class="headerlink" title="4.redis集群的数据一致性你是怎么解决的?"></a>4.redis集群的数据一致性你是怎么解决的?</h1><h2 id="Redis集群在设计时面临数据一致性的问题,因为它在分布式环境下运行,需要确保数据的一致性和可靠性。Redis采用多种机制和策略来解决数据一致性问题,主要包括以下几个方面:"><a href="#Redis集群在设计时面临数据一致性的问题,因为它在分布式环境下运行,需要确保数据的一致性和可靠性。Redis采用多种机制和策略来解决数据一致性问题,主要包括以下几个方面:" class="headerlink" title="Redis集群在设计时面临数据一致性的问题,因为它在分布式环境下运行,需要确保数据的一致性和可靠性。Redis采用多种机制和策略来解决数据一致性问题,主要包括以下几个方面:"></a>Redis集群在设计时面临数据一致性的问题,因为它在分布式环境下运行,需要确保数据的一致性和可靠性。Redis采用多种机制和策略来解决数据一致性问题,主要包括以下几个方面:</h2><h2 id="1-数据复制"><a href="#1-数据复制" class="headerlink" title="1. 数据复制"></a>1. 数据复制</h2><h2 id="Redis集群通过主从复制(replication)来保证数据的高可用性和一致性。在Redis集群中,每个数据分片(shard)都有一个主节点(master)和若干个从节点(slave)。数据写入操作首先在主节点上执行,然后异步地复制到从节点。这样,即使某个主节点发生故障,从节点也可以提供数据访问,从而提高了数据的可用性和一致性。"><a href="#Redis集群通过主从复制(replication)来保证数据的高可用性和一致性。在Redis集群中,每个数据分片(shard)都有一个主节点(master)和若干个从节点(slave)。数据写入操作首先在主节点上执行,然后异步地复制到从节点。这样,即使某个主节点发生故障,从节点也可以提供数据访问,从而提高了数据的可用性和一致性。" class="headerlink" title="Redis集群通过主从复制(replication)来保证数据的高可用性和一致性。在Redis集群中,每个数据分片(shard)都有一个主节点(master)和若干个从节点(slave)。数据写入操作首先在主节点上执行,然后异步地复制到从节点。这样,即使某个主节点发生故障,从节点也可以提供数据访问,从而提高了数据的可用性和一致性。"></a>Redis集群通过主从复制(replication)来保证数据的高可用性和一致性。在Redis集群中,每个数据分片(shard)都有一个主节点(master)和若干个从节点(slave)。数据写入操作首先在主节点上执行,然后异步地复制到从节点。这样,即使某个主节点发生故障,从节点也可以提供数据访问,从而提高了数据的可用性和一致性。</h2><h2 id="2-一致性哈希"><a href="#2-一致性哈希" class="headerlink" title="2. 一致性哈希"></a>2. 一致性哈希</h2><h2 id="Redis集群采用一致性哈希算法来分配和定位数据。每个键根据哈希函数映射到一个特定的节点,这样可以确保键的分布较为均匀,并且在节点增加或减少时,只需重新映射少量键,减少了数据移动的范围,保持了集群的一致性。"><a href="#Redis集群采用一致性哈希算法来分配和定位数据。每个键根据哈希函数映射到一个特定的节点,这样可以确保键的分布较为均匀,并且在节点增加或减少时,只需重新映射少量键,减少了数据移动的范围,保持了集群的一致性。" class="headerlink" title="Redis集群采用一致性哈希算法来分配和定位数据。每个键根据哈希函数映射到一个特定的节点,这样可以确保键的分布较为均匀,并且在节点增加或减少时,只需重新映射少量键,减少了数据移动的范围,保持了集群的一致性。"></a>Redis集群采用一致性哈希算法来分配和定位数据。每个键根据哈希函数映射到一个特定的节点,这样可以确保键的分布较为均匀,并且在节点增加或减少时,只需重新映射少量键,减少了数据移动的范围,保持了集群的一致性。</h2><h2 id="3-故障转移(Failover)"><a href="#3-故障转移(Failover)" class="headerlink" title="3. 故障转移(Failover)"></a>3. 故障转移(Failover)</h2><h2 id="Redis集群具备自动故障转移功能。当检测到某个主节点故障时,集群会自动将该主节点的一个从节点提升为新的主节点,并重新分配客户端请求。这通过Redis-Sentinel机制来实现,Sentinel负责监控Redis节点的状态,并在检测到故障时执行故障转移操作,保证数据的一致性和可用性。"><a href="#Redis集群具备自动故障转移功能。当检测到某个主节点故障时,集群会自动将该主节点的一个从节点提升为新的主节点,并重新分配客户端请求。这通过Redis-Sentinel机制来实现,Sentinel负责监控Redis节点的状态,并在检测到故障时执行故障转移操作,保证数据的一致性和可用性。" class="headerlink" title="Redis集群具备自动故障转移功能。当检测到某个主节点故障时,集群会自动将该主节点的一个从节点提升为新的主节点,并重新分配客户端请求。这通过Redis Sentinel机制来实现,Sentinel负责监控Redis节点的状态,并在检测到故障时执行故障转移操作,保证数据的一致性和可用性。"></a>Redis集群具备自动故障转移功能。当检测到某个主节点故障时,集群会自动将该主节点的一个从节点提升为新的主节点,并重新分配客户端请求。这通过Redis Sentinel机制来实现,Sentinel负责监控Redis节点的状态,并在检测到故障时执行故障转移操作,保证数据的一致性和可用性。</h2><h2 id="4-配置共识协议"><a href="#4-配置共识协议" class="headerlink" title="4. 配置共识协议"></a>4. 配置共识协议</h2><h2 id="Redis集群使用Gossip协议来传播节点状态信息,使用集群配置共识协议(Cluster-Configuration-Agreement)来确保所有节点在配置变更时达成一致。这避免了因网络分区或其他原因导致的集群状态不一致问题。"><a href="#Redis集群使用Gossip协议来传播节点状态信息,使用集群配置共识协议(Cluster-Configuration-Agreement)来确保所有节点在配置变更时达成一致。这避免了因网络分区或其他原因导致的集群状态不一致问题。" class="headerlink" title="Redis集群使用Gossip协议来传播节点状态信息,使用集群配置共识协议(Cluster Configuration Agreement)来确保所有节点在配置变更时达成一致。这避免了因网络分区或其他原因导致的集群状态不一致问题。"></a>Redis集群使用Gossip协议来传播节点状态信息,使用集群配置共识协议(Cluster Configuration Agreement)来确保所有节点在配置变更时达成一致。这避免了因网络分区或其他原因导致的集群状态不一致问题。</h2><h2 id="5-客户端重定向"><a href="#5-客户端重定向" class="headerlink" title="5. 客户端重定向"></a>5. 客户端重定向</h2><h2 id="在Redis集群中,客户端可能会向错误的节点发送请求。为了解决这个问题,Redis集群通过ASK和MOVED重定向机制告知客户端请求的正确目标节点。客户端在收到重定向信息后,会重新路由请求到正确的节点,从而确保数据的一致性。"><a href="#在Redis集群中,客户端可能会向错误的节点发送请求。为了解决这个问题,Redis集群通过ASK和MOVED重定向机制告知客户端请求的正确目标节点。客户端在收到重定向信息后,会重新路由请求到正确的节点,从而确保数据的一致性。" class="headerlink" title="在Redis集群中,客户端可能会向错误的节点发送请求。为了解决这个问题,Redis集群通过ASK和MOVED重定向机制告知客户端请求的正确目标节点。客户端在收到重定向信息后,会重新路由请求到正确的节点,从而确保数据的一致性。"></a>在Redis集群中,客户端可能会向错误的节点发送请求。为了解决这个问题,Redis集群通过ASK和MOVED重定向机制告知客户端请求的正确目标节点。客户端在收到重定向信息后,会重新路由请求到正确的节点,从而确保数据的一致性。</h2><h2 id="6-写操作的传播和确认"><a href="#6-写操作的传播和确认" class="headerlink" title="6. 写操作的传播和确认"></a>6. 写操作的传播和确认</h2><h2 id="Redis集群通过部分同步(partial-synchronization)和心跳机制(heartbeat)来确保写操作在所有副本节点上的传播和确认。主节点会定期将写操作日志(Replication-Log)发送给从节点,从节点收到并确认后,主节点才会认为数据写入成功。"><a href="#Redis集群通过部分同步(partial-synchronization)和心跳机制(heartbeat)来确保写操作在所有副本节点上的传播和确认。主节点会定期将写操作日志(Replication-Log)发送给从节点,从节点收到并确认后,主节点才会认为数据写入成功。" class="headerlink" title="Redis集群通过部分同步(partial synchronization)和心跳机制(heartbeat)来确保写操作在所有副本节点上的传播和确认。主节点会定期将写操作日志(Replication Log)发送给从节点,从节点收到并确认后,主节点才会认为数据写入成功。"></a>Redis集群通过部分同步(partial synchronization)和心跳机制(heartbeat)来确保写操作在所有副本节点上的传播和确认。主节点会定期将写操作日志(Replication Log)发送给从节点,从节点收到并确认后,主节点才会认为数据写入成功。</h2><h2 id="7-数据持久化"><a href="#7-数据持久化" class="headerlink" title="7. 数据持久化"></a>7. 数据持久化</h2><h2 id="尽管Redis主要是内存数据库,但它也提供数据持久化选项(如RDB快照和AOF日志),以防止数据丢失。在集群环境下,每个节点可以独立进行持久化操作,确保即使发生崩溃或重启,数据也能得到恢复。"><a href="#尽管Redis主要是内存数据库,但它也提供数据持久化选项(如RDB快照和AOF日志),以防止数据丢失。在集群环境下,每个节点可以独立进行持久化操作,确保即使发生崩溃或重启,数据也能得到恢复。" class="headerlink" title="尽管Redis主要是内存数据库,但它也提供数据持久化选项(如RDB快照和AOF日志),以防止数据丢失。在集群环境下,每个节点可以独立进行持久化操作,确保即使发生崩溃或重启,数据也能得到恢复。"></a>尽管Redis主要是内存数据库,但它也提供数据持久化选项(如RDB快照和AOF日志),以防止数据丢失。在集群环境下,每个节点可以独立进行持久化操作,确保即使发生崩溃或重启,数据也能得到恢复。</h2><h2 id="通过上述机制,Redis集群能够在分布式环境下有效地解决数据一致性问题,提供高性能、高可用的数据服务。"><a href="#通过上述机制,Redis集群能够在分布式环境下有效地解决数据一致性问题,提供高性能、高可用的数据服务。" class="headerlink" title="通过上述机制,Redis集群能够在分布式环境下有效地解决数据一致性问题,提供高性能、高可用的数据服务。"></a>通过上述机制,Redis集群能够在分布式环境下有效地解决数据一致性问题,提供高性能、高可用的数据服务。</h2><h1 id="5-布隆过滤器是你自己写的还是第三方,自己写的误差知道是多少吗?有考虑过吗?"><a href="#5-布隆过滤器是你自己写的还是第三方,自己写的误差知道是多少吗?有考虑过吗?" class="headerlink" title="5.布隆过滤器是你自己写的还是第三方,自己写的误差知道是多少吗?有考虑过吗?"></a>5.布隆过滤器是你自己写的还是第三方,自己写的误差知道是多少吗?有考虑过吗?</h1><h2 id="布隆过滤器(Bloom-Filter)在Redis中是自行实现的,而不是使用第三方库。Redis模块提供了对布隆过滤器的支持,通过Redis的模块系统可以扩展功能,例如redis-bloom模块。"><a href="#布隆过滤器(Bloom-Filter)在Redis中是自行实现的,而不是使用第三方库。Redis模块提供了对布隆过滤器的支持,通过Redis的模块系统可以扩展功能,例如redis-bloom模块。" class="headerlink" title="布隆过滤器(Bloom Filter)在Redis中是自行实现的,而不是使用第三方库。Redis模块提供了对布隆过滤器的支持,通过Redis的模块系统可以扩展功能,例如redis-bloom模块。"></a>布隆过滤器(Bloom Filter)在Redis中是自行实现的,而不是使用第三方库。Redis模块提供了对布隆过滤器的支持,通过Redis的模块系统可以扩展功能,例如<code>redis-bloom</code>模块。</h2><h2 id="布隆过滤器的实现和误差率"><a href="#布隆过滤器的实现和误差率" class="headerlink" title="布隆过滤器的实现和误差率"></a>布隆过滤器的实现和误差率</h2><h2 id="布隆过滤器是一种空间效率极高的概率型数据结构,用于测试集合中是否存在一个元素。它能够显著减少存储空间,但允许一定的误判(false-positive),即可能报告一个不存在的元素为存在。"><a href="#布隆过滤器是一种空间效率极高的概率型数据结构,用于测试集合中是否存在一个元素。它能够显著减少存储空间,但允许一定的误判(false-positive),即可能报告一个不存在的元素为存在。" class="headerlink" title="布隆过滤器是一种空间效率极高的概率型数据结构,用于测试集合中是否存在一个元素。它能够显著减少存储空间,但允许一定的误判(false positive),即可能报告一个不存在的元素为存在。"></a>布隆过滤器是一种空间效率极高的概率型数据结构,用于测试集合中是否存在一个元素。它能够显著减少存储空间,但允许一定的误判(false positive),即可能报告一个不存在的元素为存在。</h2><h2 id="误差率的计算"><a href="#误差率的计算" class="headerlink" title="误差率的计算"></a>误差率的计算</h2><h2 id="布隆过滤器的误差率取决于以下几个因素:"><a href="#布隆过滤器的误差率取决于以下几个因素:" class="headerlink" title="布隆过滤器的误差率取决于以下几个因素:"></a>布隆过滤器的误差率取决于以下几个因素:</h2><ol><li><h2 id="哈希函数的数量(k):哈希函数的数量影响误差率,通常通过优化选择一个合适的数量。"><a href="#哈希函数的数量(k):哈希函数的数量影响误差率,通常通过优化选择一个合适的数量。" class="headerlink" title="哈希函数的数量(k):哈希函数的数量影响误差率,通常通过优化选择一个合适的数量。"></a><strong>哈希函数的数量(k)</strong>:哈希函数的数量影响误差率,通常通过优化选择一个合适的数量。</h2></li><li><h2 id="位数组的大小(m):位数组越大,误差率越低。"><a href="#位数组的大小(m):位数组越大,误差率越低。" class="headerlink" title="位数组的大小(m):位数组越大,误差率越低。"></a><strong>位数组的大小(m)</strong>:位数组越大,误差率越低。</h2></li><li><h2 id="插入元素的数量(n):插入的元素越多,误差率越高。"><a href="#插入元素的数量(n):插入的元素越多,误差率越高。" class="headerlink" title="插入元素的数量(n):插入的元素越多,误差率越高。"></a><strong>插入元素的数量(n)</strong>:插入的元素越多,误差率越高。</h2></li></ol><h2 id="误差率可以通过以下公式计算:"><a href="#误差率可以通过以下公式计算:" class="headerlink" title="误差率可以通过以下公式计算:"></a>误差率可以通过以下公式计算:</h2><h2 id="P-f-left-1-left-1-frac-1-m-right-kn-right-k"><a href="#P-f-left-1-left-1-frac-1-m-right-kn-right-k" class="headerlink" title="[ P(f) = \left( 1 - \left( 1 - \frac{1}{m} \right)^{kn} \right)^k ]"></a>[ P(f) = \left( 1 - \left( 1 - \frac{1}{m} \right)^{kn} \right)^k ]</h2><h2 id="简化后,在位数组和哈希函数数量较大时,误差率近似为:"><a href="#简化后,在位数组和哈希函数数量较大时,误差率近似为:" class="headerlink" title="简化后,在位数组和哈希函数数量较大时,误差率近似为:"></a>简化后,在位数组和哈希函数数量较大时,误差率近似为:</h2><h2 id="P-f-approx-left-1-e-frac-kn-m-right-k"><a href="#P-f-approx-left-1-e-frac-kn-m-right-k" class="headerlink" title="[ P(f) \approx \left( 1 - e^{-\frac{kn}{m}} \right)^k ]"></a>[ P(f) \approx \left( 1 - e^{-\frac{kn}{m}} \right)^k ]</h2><h2 id="Redis布隆过滤器模块中的误差率"><a href="#Redis布隆过滤器模块中的误差率" class="headerlink" title="Redis布隆过滤器模块中的误差率"></a>Redis布隆过滤器模块中的误差率</h2><h2 id="在redis-bloom模块中,误差率是可配置的。用户可以在创建布隆过滤器时指定预期的误差率和预计插入的元素数量,模块会自动计算和分配适当的位数组大小和哈希函数数量以达到指定的误差率。例如:"><a href="#在redis-bloom模块中,误差率是可配置的。用户可以在创建布隆过滤器时指定预期的误差率和预计插入的元素数量,模块会自动计算和分配适当的位数组大小和哈希函数数量以达到指定的误差率。例如:" class="headerlink" title="在redis-bloom模块中,误差率是可配置的。用户可以在创建布隆过滤器时指定预期的误差率和预计插入的元素数量,模块会自动计算和分配适当的位数组大小和哈希函数数量以达到指定的误差率。例如:"></a>在<code>redis-bloom</code>模块中,误差率是可配置的。用户可以在创建布隆过滤器时指定预期的误差率和预计插入的元素数量,模块会自动计算和分配适当的位数组大小和哈希函数数量以达到指定的误差率。例如:</h2><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">BF.RESERVE myBloomFilter 0.01 10000</span><br></pre></td></tr></tbody></table></figure><h2 id="这条命令创建一个名为myBloomFilter的布隆过滤器,目标是存储10000个元素,目标误差率为1-。"><a href="#这条命令创建一个名为myBloomFilter的布隆过滤器,目标是存储10000个元素,目标误差率为1-。" class="headerlink" title="这条命令创建一个名为myBloomFilter的布隆过滤器,目标是存储10000个元素,目标误差率为1%。"></a>这条命令创建一个名为<code>myBloomFilter</code>的布隆过滤器,目标是存储10000个元素,目标误差率为1%。</h2><h2 id="实现误差率的考量"><a href="#实现误差率的考量" class="headerlink" title="实现误差率的考量"></a>实现误差率的考量</h2><h2 id="在设计和实现布隆过滤器时,Redis开发者考虑了以下几点:"><a href="#在设计和实现布隆过滤器时,Redis开发者考虑了以下几点:" class="headerlink" title="在设计和实现布隆过滤器时,Redis开发者考虑了以下几点:"></a>在设计和实现布隆过滤器时,Redis开发者考虑了以下几点:</h2><ol><li><h2 id="内存效率:尽量在保证误差率的前提下使用最少的内存。"><a href="#内存效率:尽量在保证误差率的前提下使用最少的内存。" class="headerlink" title="内存效率:尽量在保证误差率的前提下使用最少的内存。"></a><strong>内存效率</strong>:尽量在保证误差率的前提下使用最少的内存。</h2></li><li><h2 id="灵活配置:允许用户根据实际需求配置误差率和预计插入的元素数量。"><a href="#灵活配置:允许用户根据实际需求配置误差率和预计插入的元素数量。" class="headerlink" title="灵活配置:允许用户根据实际需求配置误差率和预计插入的元素数量。"></a><strong>灵活配置</strong>:允许用户根据实际需求配置误差率和预计插入的元素数量。</h2></li><li><h2 id="性能优化:优化哈希函数和位数组操作以提供高性能的查询和插入。"><a href="#性能优化:优化哈希函数和位数组操作以提供高性能的查询和插入。" class="headerlink" title="性能优化:优化哈希函数和位数组操作以提供高性能的查询和插入。"></a><strong>性能优化</strong>:优化哈希函数和位数组操作以提供高性能的查询和插入。</h2></li></ol><h2 id="实现中的误差率"><a href="#实现中的误差率" class="headerlink" title="实现中的误差率"></a>实现中的误差率</h2><h2 id="自定义实现布隆过滤器时,开发者通常需要实际测试和验证误差率。通过大量实验和实际使用情况来确认布隆过滤器的性能和误差率是否符合预期。这也是Redis开发团队在实现布隆过滤器时所进行的步骤。"><a href="#自定义实现布隆过滤器时,开发者通常需要实际测试和验证误差率。通过大量实验和实际使用情况来确认布隆过滤器的性能和误差率是否符合预期。这也是Redis开发团队在实现布隆过滤器时所进行的步骤。" class="headerlink" title="自定义实现布隆过滤器时,开发者通常需要实际测试和验证误差率。通过大量实验和实际使用情况来确认布隆过滤器的性能和误差率是否符合预期。这也是Redis开发团队在实现布隆过滤器时所进行的步骤。"></a>自定义实现布隆过滤器时,开发者通常需要实际测试和验证误差率。通过大量实验和实际使用情况来确认布隆过滤器的性能和误差率是否符合预期。这也是Redis开发团队在实现布隆过滤器时所进行的步骤。</h2><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><h2 id="Redis中的布隆过滤器是自实现的,并且在设计时充分考虑了误差率问题。通过灵活的配置和优化的实现,Redis能够在实际应用中提供高效且误差可控的布隆过滤器功能。如果需要了解具体的误差率或调整参数,用户可以参考Redis官方文档和相关模块文档。"><a href="#Redis中的布隆过滤器是自实现的,并且在设计时充分考虑了误差率问题。通过灵活的配置和优化的实现,Redis能够在实际应用中提供高效且误差可控的布隆过滤器功能。如果需要了解具体的误差率或调整参数,用户可以参考Redis官方文档和相关模块文档。" class="headerlink" title="Redis中的布隆过滤器是自实现的,并且在设计时充分考虑了误差率问题。通过灵活的配置和优化的实现,Redis能够在实际应用中提供高效且误差可控的布隆过滤器功能。如果需要了解具体的误差率或调整参数,用户可以参考Redis官方文档和相关模块文档。"></a>Redis中的布隆过滤器是自实现的,并且在设计时充分考虑了误差率问题。通过灵活的配置和优化的实现,Redis能够在实际应用中提供高效且误差可控的布隆过滤器功能。如果需要了解具体的误差率或调整参数,用户可以参考Redis官方文档和相关模块文档。</h2><h1 id="6-考虑过商品超卖问题的解决方式吗?"><a href="#6-考虑过商品超卖问题的解决方式吗?" class="headerlink" title="6.考虑过商品超卖问题的解决方式吗?"></a>6.考虑过商品超卖问题的解决方式吗?</h1><p>商品超卖(overselling)是电商和在线零售系统中常见的问题,特别是在高并发场景下,如秒杀活动或抢购。为了有效解决商品超卖问题,可以考虑以下几种方法:</p><h3 id="1-使用分布式锁"><a href="#1-使用分布式锁" class="headerlink" title="1. 使用分布式锁"></a>1. 使用分布式锁</h3><p>分布式锁可以保证同一时刻只有一个进程能够操作某一资源,从而避免超卖。Redis是实现分布式锁的常用工具。</p><h4 id="基于Redis的分布式锁"><a href="#基于Redis的分布式锁" class="headerlink" title="基于Redis的分布式锁"></a>基于Redis的分布式锁</h4><p>使用Redis的<code>SETNX</code>(Set if Not Exists)命令来实现分布式锁。当用户请求购买商品时,先尝试获取锁,只有成功获取锁的用户才能继续购买流程。</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SETNX lock:product_id user_id</span><br></pre></td></tr></tbody></table></figure><p>如果获取成功(返回1),继续执行购买逻辑。购买完成后,释放锁:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DEL lock:product_id</span><br></pre></td></tr></tbody></table></figure><p>还可以结合<code>EXPIRE</code>设置锁的有效时间,防止死锁。</p><h3 id="2-原子操作"><a href="#2-原子操作" class="headerlink" title="2. 原子操作"></a>2. 原子操作</h3><p>使用数据库的原子操作可以确保库存扣减操作的原子性,避免超卖。</p><h4 id="基于Redis的原子操作"><a href="#基于Redis的原子操作" class="headerlink" title="基于Redis的原子操作"></a>基于Redis的原子操作</h4><p>Redis的<code>DECR</code>(Decrement)命令是原子操作,可以用来减少库存。当用户购买商品时,执行以下操作:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">DECR product:stock</span><br></pre></td></tr></tbody></table></figure><p>如果结果小于0,则表示库存不足,需要回滚操作:</p><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">INCR product:stock</span><br></pre></td></tr></tbody></table></figure><h3 id="3-乐观锁"><a href="#3-乐观锁" class="headerlink" title="3. 乐观锁"></a>3. 乐观锁</h3><p>乐观锁基于版本号或时间戳实现,适合高并发场景下的超卖问题。</p><h4 id="基于Redis的乐观锁"><a href="#基于Redis的乐观锁" class="headerlink" title="基于Redis的乐观锁"></a>基于Redis的乐观锁</h4><p>使用<code>WATCH</code>命令监控库存键,然后执行事务操作(<code>MULTI</code>和<code>EXEC</code>):</p><figure class="highlight shell"><table><tbody><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">WATCH product:stock</span><br><span class="line">MULTI</span><br><span class="line">DECR product:stock</span><br><span class="line">EXEC</span><br></pre></td></tr></tbody></table></figure><p>如果在<code>WATCH</code>后,库存键被其他客户端修改,<code>EXEC</code>操作会失败,需要重试。</p><h3 id="4-限流和队列"><a href="#4-限流和队列" class="headerlink" title="4. 限流和队列"></a>4. 限流和队列</h3><p>通过限流和队列机制控制请求的并发度,保证系统在高并发情况下的稳定性。</p><h4 id="基于消息队列"><a href="#基于消息队列" class="headerlink" title="基于消息队列"></a>基于消息队列</h4><p>可以使用消息队列(如RabbitMQ、Kafka等)将用户请求按照顺序排队处理。每次从队列中取出请求,处理完一个再处理下一个,从而避免超卖。</p><h3 id="5-数据库事务"><a href="#5-数据库事务" class="headerlink" title="5. 数据库事务"></a>5. 数据库事务</h3><p>在关系型数据库中,可以使用事务来确保库存更新的原子性。</p><h4 id="基于数据库事务的解决方案"><a href="#基于数据库事务的解决方案" class="headerlink" title="基于数据库事务的解决方案"></a>基于数据库事务的解决方案</h4><p>在MySQL等数据库中,可以使用事务来锁定行,确保操作的原子性和一致性。</p><figure class="highlight sql"><table><tbody><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="keyword">START</span> TRANSACTION;</span><br><span class="line"><span class="keyword">SELECT</span> stock <span class="keyword">FROM</span> products <span class="keyword">WHERE</span> id <span class="operator">=</span> product_id <span class="keyword">FOR</span> <span class="keyword">UPDATE</span>;</span><br><span class="line"><span class="comment">-- 如果库存足够,更新库存</span></span><br><span class="line"><span class="keyword">UPDATE</span> products <span class="keyword">SET</span> stock <span class="operator">=</span> stock <span class="operator">-</span> <span class="number">1</span> <span class="keyword">WHERE</span> id <span class="operator">=</span> product_id;</span><br><span class="line"><span class="keyword">COMMIT</span>;</span><br></pre></td></tr></tbody></table></figure><h3 id="6-双写一致性"><a href="#6-双写一致性" class="headerlink" title="6. 双写一致性"></a>6. 双写一致性</h3><p>在复杂场景下,可以使用双写一致性策略,确保缓存和数据库的数据一致性,避免由于缓存不一致导致的超卖。</p><h3 id="综述"><a href="#综述" class="headerlink" title="综述"></a>综述</h3><p>上述方法各有优缺点,根据具体业务场景和技术栈,选择合适的方案或组合使用,以确保商品在高并发场景下不会出现超卖问题。</p><ol><li><strong>分布式锁</strong>:适合需要严格顺序控制的场景,但可能有性能瓶颈。</li><li><strong>原子操作</strong>:高效且简单,但需要合理的库存设计。</li><li><strong>乐观锁</strong>:适合高并发下的库存扣减,但需要处理重试逻辑。</li><li><strong>限流和队列</strong>:能够有效控制并发量,确保系统稳定性。</li><li><strong>数据库事务</strong>:强一致性保证,但性能可能不如无锁方案。</li><li><strong>双写一致性</strong>:确保缓存和数据库一致,避免缓存不一致导致的问题。</li></ol><p>通过合理组合这些方案,可以有效解决商品超卖问题,确保系统的可靠性和用户体验。</p><h1 id="7-讲讲乐观锁?"><a href="#7-讲讲乐观锁?" class="headerlink" title="7.讲讲乐观锁?"></a>7.讲讲乐观锁?</h1><p>乐观锁是一种用于控制并发访问的机制,它假设多个事务在多数情况下不会互相干扰,因此不会在一开始就加锁,而是在提交更新时检查数据是否被其他事务修改过。如果发现数据被修改,则回滚当前事务并重试。这种策略特别适用于读操作多、写操作少的场景。</p><h3 id="乐观锁的实现原理"><a href="#乐观锁的实现原理" class="headerlink" title="乐观锁的实现原理"></a>乐观锁的实现原理</h3><p>乐观锁通常依赖于版本号或时间戳来实现。当对数据进行更新时,会检查当前版本号或时间戳是否与开始读取时的版本号或时间戳一致。如果一致,则执行更新;如果不一致,则说明数据已经被其他事务修改,当前操作需要重试。</p><h4 id="基于版本号的乐观锁"><a href="#基于版本号的乐观锁" class="headerlink" title="基于版本号的乐观锁"></a>基于版本号的乐观锁</h4><ol><li><strong>读取数据时</strong>,读取其版本号。</li><li><strong>更新数据时</strong>,检查当前版本号是否与读取时的一致。如果一致,则更新数据并将版本号加1;如果不一致,则说明数据已被修改,操作失败,需要重试。</li></ol><p>例如,在关系型数据库中可以这样实现:</p><figure class="highlight sql"><table><tbody><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">-- 读取数据及版本号</span></span><br><span class="line"><span class="keyword">SELECT</span> stock, version <span class="keyword">FROM</span> products <span class="keyword">WHERE</span> id <span class="operator">=</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">UPDATE</span> products <span class="keyword">SET</span> stock <span class="operator">=</span> stock <span class="operator">-</span> <span class="number">1</span>, version <span class="operator">=</span> version <span class="operator">+</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span> <span class="keyword">AND</span> version <span class="operator">=</span> 旧版本号;</span><br></pre></td></tr></tbody></table></figure><p>在Redis中,可以使用<code>WATCH</code>命令来实现类似的功能:</p><figure class="highlight shell"><table><tbody><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">WATCH product:stock</span><br><span class="line">-- 获取库存和版本号</span><br><span class="line">MULTI</span><br><span class="line">DECR product:stock</span><br><span class="line">-- 提交事务</span><br><span class="line">EXEC</span><br></pre></td></tr></tbody></table></figure><p>如果在<code>WATCH</code>命令之后,<code>product:stock</code>键被其他客户端修改,则<code>EXEC</code>命令执行时会失败,当前操作需要重试。</p><h3 id="乐观锁的优势"><a href="#乐观锁的优势" class="headerlink" title="乐观锁的优势"></a>乐观锁的优势</h3><ol><li><strong>高并发性能</strong>:因为不加锁,避免了锁争用和死锁问题,提高了系统的并发性能。</li><li><strong>适用读多写少的场景</strong>:在读操作多、写操作少的场景中,乐观锁的冲突概率较低,能发挥较高的性能优势。</li></ol><h3 id="乐观锁的局限性"><a href="#乐观锁的局限性" class="headerlink" title="乐观锁的局限性"></a>乐观锁的局限性</h3><ol><li><strong>重试机制</strong>:当并发写操作多时,乐观锁可能导致频繁的重试,影响系统性能。</li><li><strong>适用场景有限</strong>:乐观锁适用于读多写少的场景,如果写操作频繁,乐观锁的重试机制可能导致性能下降。</li><li><strong>实现复杂度</strong>:在一些复杂的业务逻辑中,实现乐观锁可能需要较多的额外工作,如管理版本号或时间戳。</li></ol><h3 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h3><ol><li><strong>高并发系统</strong>:如电商平台的库存管理、用户账户余额更新等,尤其是在读操作多、写操作少的情况下。</li><li><strong>分布式系统</strong>:如分布式数据库或缓存系统,需要在多个节点之间保持数据的一致性。</li></ol><h3 id="实际应用示例"><a href="#实际应用示例" class="headerlink" title="实际应用示例"></a>实际应用示例</h3><p>以下是一个实际应用乐观锁的示例,假设我们有一个库存管理系统,在高并发情况下进行库存扣减操作:</p><h4 id="使用MySQL实现乐观锁"><a href="#使用MySQL实现乐观锁" class="headerlink" title="使用MySQL实现乐观锁"></a>使用MySQL实现乐观锁</h4><ol><li><strong>读取库存和版本号</strong>:</li></ol><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> stock, version <span class="keyword">FROM</span> products <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br></pre></td></tr></tbody></table></figure><ol start="2"><li><strong>尝试更新库存和版本号</strong>:</li></ol><figure class="highlight sql"><table><tbody><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="keyword">UPDATE</span> products <span class="keyword">SET</span> stock <span class="operator">=</span> stock <span class="operator">-</span> <span class="number">1</span>, version <span class="operator">=</span> version <span class="operator">+</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span> <span class="keyword">AND</span> version <span class="operator">=</span> 旧版本号;</span><br></pre></td></tr></tbody></table></figure><ol start="3"><li><strong>检查更新结果</strong>:<ul><li>如果更新成功,则说明库存扣减成功。</li><li>如果更新失败,则说明库存已被修改,需要重试。</li></ul></li></ol><h4 id="使用Redis实现乐观锁"><a href="#使用Redis实现乐观锁" class="headerlink" title="使用Redis实现乐观锁"></a>使用Redis实现乐观锁</h4><ol><li><strong>监控库存键</strong>:</li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WATCH product:stock</span><br></pre></td></tr></tbody></table></figure><ol start="2"><li><strong>尝试扣减库存</strong>:</li></ol><figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MULTI</span><br><span class="line">DECR product:stock</span><br><span class="line">EXEC</span><br></pre></td></tr></tbody></table></figure><ol start="3"><li><strong>检查事务结果</strong>:<ul><li>如果事务成功,则说明库存扣减成功。</li><li>如果事务失败,则说明库存已被修改,需要重试。</li></ul></li></ol><p>通过上述方式,可以在高并发场景下有效使用乐观锁,避免超卖等问题,同时保持较高的系统性能和可扩展性。</p><h2 id="8-你乐观锁是sql实现还是逻辑实现?"><a href="#8-你乐观锁是sql实现还是逻辑实现?" class="headerlink" title="8.你乐观锁是sql实现还是逻辑实现?"></a>8.你乐观锁是sql实现还是逻辑实现?</h2><p>乐观锁的实现方式可以根据具体需求和场景进行选择,可以是SQL实现,也可以是逻辑实现。两者各有优缺点,下面详细说明:</p><h3 id="SQL实现乐观锁"><a href="#SQL实现乐观锁" class="headerlink" title="SQL实现乐观锁"></a>SQL实现乐观锁</h3><p>SQL实现乐观锁主要通过数据库的支持,利用版本号或时间戳来保证数据的一致性。</p><h4 id="实现方式"><a href="#实现方式" class="headerlink" title="实现方式"></a>实现方式</h4><ol><li><p><strong>表结构设计</strong>:在需要使用乐观锁的表中添加一个版本号字段。</p> <figure class="highlight sql"><table><tbody><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">CREATE</span> <span class="keyword">TABLE</span> products (</span><br><span class="line"> id <span class="type">INT</span> <span class="keyword">PRIMARY</span> KEY,</span><br><span class="line"> name <span class="type">VARCHAR</span>(<span class="number">255</span>),</span><br><span class="line"> stock <span class="type">INT</span>,</span><br><span class="line"> version <span class="type">INT</span></span><br><span class="line">);</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>读取数据</strong>:读取数据时,获取当前版本号。</p> <figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SELECT</span> stock, version <span class="keyword">FROM</span> products <span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span>;</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>更新数据</strong>:更新数据时,检查版本号是否匹配。如果匹配,则更新数据并增加版本号;否则,更新失败。</p> <figure class="highlight sql"><table><tbody><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="keyword">UPDATE</span> products <span class="keyword">SET</span> stock <span class="operator">=</span> stock <span class="operator">-</span> <span class="number">1</span>, version <span class="operator">=</span> version <span class="operator">+</span> <span class="number">1</span></span><br><span class="line"><span class="keyword">WHERE</span> id <span class="operator">=</span> <span class="number">1</span> <span class="keyword">AND</span> version <span class="operator">=</span> old_version;</span><br></pre></td></tr></tbody></table></figure></li></ol><h3 id="逻辑实现乐观锁"><a href="#逻辑实现乐观锁" class="headerlink" title="逻辑实现乐观锁"></a>逻辑实现乐观锁</h3><p>逻辑实现乐观锁是在应用层通过代码逻辑来实现乐观锁的机制,适合需要更灵活控制的场景。</p><h4 id="基于Redis的逻辑实现"><a href="#基于Redis的逻辑实现" class="headerlink" title="基于Redis的逻辑实现"></a>基于Redis的逻辑实现</h4><ol><li><p><strong>监控数据</strong>:使用Redis的<code>WATCH</code>命令监控需要修改的键。</p> <figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">WATCH product:stock</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>开始事务</strong>:在事务中执行需要的操作。</p> <figure class="highlight shell"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">MULTI</span><br><span class="line">DECR product:stock</span><br><span class="line">EXEC</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>检查结果</strong>:如果事务执行成功,说明数据未被其他客户端修改。如果失败,需要重试。</p></li></ol><h4 id="基于代码的逻辑实现"><a href="#基于代码的逻辑实现" class="headerlink" title="基于代码的逻辑实现"></a>基于代码的逻辑实现</h4><ol><li><p><strong>读取数据和版本号</strong>:在应用逻辑中读取数据和其版本号。</p> <figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Product</span> <span class="variable">product</span> <span class="operator">=</span> productRepository.findById(productId);</span><br><span class="line"><span class="type">int</span> <span class="variable">currentVersion</span> <span class="operator">=</span> product.getVersion();</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>执行更新</strong>:在更新时检查版本号。</p> <figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (productRepository.updateStock(productId, currentVersion) == <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 版本号不匹配,更新失败,需要重试</span></span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">OptimisticLockingFailureException</span>(<span class="string">"Data has been modified"</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ol><h3 id="优缺点比较"><a href="#优缺点比较" class="headerlink" title="优缺点比较"></a>优缺点比较</h3><h4 id="SQL实现"><a href="#SQL实现" class="headerlink" title="SQL实现"></a>SQL实现</h4><p><strong>优点</strong>:</p><ul><li>简单直接,依赖数据库的原生特性。</li><li>实现和维护成本低。</li></ul><p><strong>缺点</strong>:</p><ul><li>数据库负载较高时,性能可能受影响。</li><li>跨多个数据库或分布式环境下实现较为困难。</li></ul><h4 id="逻辑实现"><a href="#逻辑实现" class="headerlink" title="逻辑实现"></a>逻辑实现</h4><p><strong>优点</strong>:</p><ul><li>灵活性高,可以跨数据库和分布式系统使用。</li><li>可以结合不同存储系统实现一致性。</li></ul><p><strong>缺点</strong>:</p><ul><li>实现复杂,增加了应用层的逻辑。</li><li>需要额外处理重试和失败的情况。</li></ul><h3 id="结论-1"><a href="#结论-1" class="headerlink" title="结论"></a>结论</h3><ul><li><strong>SQL实现</strong>:适合单一数据库环境中较简单的应用场景,易于实现和维护。</li><li><strong>逻辑实现</strong>:适合复杂的、跨多个数据库或分布式系统的场景,提供更高的灵活性。</li></ul><p>具体选择哪种实现方式,取决于应用的架构、需求和复杂度。如果你的系统使用关系型数据库且对性能要求不高,可以选择SQL实现。如果你的系统是分布式架构,或者需要更灵活的控制,逻辑实现可能更适合。</p><h1 id="9-thread-local你为什么要用这个?"><a href="#9-thread-local你为什么要用这个?" class="headerlink" title="9.thread local你为什么要用这个?"></a>9.thread local你为什么要用这个?</h1><p><code>ThreadLocal</code>是Java中的一个工具类,它提供了线程本地变量。这些变量对每个使用该变量的线程都有独立的副本,线程之间互不干扰。这在某些特定场景下非常有用,可以用来简化线程安全问题的处理。</p><h3 id="为什么使用ThreadLocal?"><a href="#为什么使用ThreadLocal?" class="headerlink" title="为什么使用ThreadLocal?"></a>为什么使用<code>ThreadLocal</code>?</h3><ol><li><p><strong>线程安全</strong>:</p><ul><li>当多个线程需要独立地使用一些共享变量时,使用<code>ThreadLocal</code>可以避免并发问题。每个线程都有自己的变量副本,从而避免了线程间的竞争和同步问题。</li></ul></li><li><p><strong>简化代码</strong>:</p><ul><li>使用<code>ThreadLocal</code>可以避免显式的同步锁机制,简化代码的编写和维护。特别是在某些需要频繁访问共享变量的场景中,使用<code>ThreadLocal</code>可以使代码更加简洁和高效。</li></ul></li><li><p><strong>存储线程上下文信息</strong>:</p><ul><li><code>ThreadLocal</code>可以用于存储线程上下文信息,如数据库连接、用户会话信息等,使得这些信息在整个线程生命周期内都可以被方便地访问。</li></ul></li></ol><h3 id="典型使用场景"><a href="#典型使用场景" class="headerlink" title="典型使用场景"></a>典型使用场景</h3><ol><li><p><strong>数据库连接</strong>:</p><ul><li>在多线程环境中,每个线程可能需要一个独立的数据库连接。使用<code>ThreadLocal</code>可以确保每个线程有自己的数据库连接,从而避免竞争和同步问题。</li></ul> <figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConnectionManager</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal<Connection> connectionHolder = </span><br><span class="line"> ThreadLocal.withInitial(() -> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">return</span> DriverManager.getConnection(DB_URL);</span><br><span class="line"> } <span class="keyword">catch</span> (SQLException e) {</span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(e);</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Connection <span class="title function_">getConnection</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> connectionHolder.get();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>事务管理</strong>:</p><ul><li>在分布式事务处理中,<code>ThreadLocal</code>可以用来保存事务上下文,使得事务操作在同一线程内可以访问到相同的事务信息。</li></ul> <figure class="highlight java"><table><tbody><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="keyword">class</span> <span class="title class_">TransactionManager</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal<Transaction> transactionHolder = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span><>();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">beginTransaction</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Transaction</span>();</span><br><span class="line"> tx.begin();</span><br><span class="line"> transactionHolder.set(tx);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">commitTransaction</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> transactionHolder.get();</span><br><span class="line"> <span class="keyword">if</span> (tx != <span class="literal">null</span>) {</span><br><span class="line"> tx.commit();</span><br><span class="line"> transactionHolder.remove();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">rollbackTransaction</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">Transaction</span> <span class="variable">tx</span> <span class="operator">=</span> transactionHolder.get();</span><br><span class="line"> <span class="keyword">if</span> (tx != <span class="literal">null</span>) {</span><br><span class="line"> tx.rollback();</span><br><span class="line"> transactionHolder.remove();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Transaction <span class="title function_">getTransaction</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> transactionHolder.get();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>用户会话</strong>:</p><ul><li>在Web应用中,可以使用<code>ThreadLocal</code>来保存当前用户的会话信息,使得在整个请求处理过程中都能方便地访问用户信息。</li></ul> <figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserContext</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal<User> userHolder = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span><>();</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">setUser</span><span class="params">(User user)</span> {</span><br><span class="line"> userHolder.set(user);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> User <span class="title function_">getUser</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> userHolder.get();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">clear</span><span class="params">()</span> {</span><br><span class="line"> userHolder.remove();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ol><h3 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h3><ol><li><p><strong>内存泄漏</strong>:</p><ul><li>使用<code>ThreadLocal</code>时要特别注意内存泄漏问题。在应用服务器中,线程池中的线程可能会被重复使用。如果没有及时清理<code>ThreadLocal</code>变量,可能会导致内存泄漏。因此,在适当的时候要调用<code>remove()</code>方法来清理变量。</li></ul> <figure class="highlight java"><table><tbody><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="keyword">try</span> {</span><br><span class="line"> <span class="comment">// 使用 ThreadLocal 变量</span></span><br><span class="line">} <span class="keyword">finally</span> {</span><br><span class="line"> threadLocalVariable.remove();</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>复杂性</strong>:</p><ul><li>尽量简化<code>ThreadLocal</code>的使用,避免过度使用导致代码复杂和难以维护。<code>ThreadLocal</code>主要用于解决特定的线程安全问题,不应该作为解决所有并发问题的通用方案。</li></ul></li><li><p><strong>适用场景</strong>:</p><ul><li><code>ThreadLocal</code>适用于每个线程需要独立且一致地访问某些共享变量的场景。在大多数情况下,应优先考虑使用其他线程安全机制,如锁和并发容器。</li></ul></li></ol><p>通过合理使用<code>ThreadLocal</code>,可以有效解决特定场景下的线程安全问题,并简化代码的编写和维护。</p><h1 id="10-我们公司之前用这个出现过脏读问题,你变量是怎么清除?"><a href="#10-我们公司之前用这个出现过脏读问题,你变量是怎么清除?" class="headerlink" title="10.我们公司之前用这个出现过脏读问题,你变量是怎么清除?"></a>10.我们公司之前用这个出现过脏读问题,你变量是怎么清除?</h1><p>脏读(Dirty Read)是数据库并发控制中常见的问题之一,通常发生在未正确隔离事务的情况下。为了避免脏读问题,您需要确保事务隔离级别设置得当,并且在读取和写入数据时采取适当的措施。以下是一些方法和步骤,可以帮助您避免脏读问题:</p><ol><li><p><strong>设置事务隔离级别</strong>:</p><ul><li>选择适当的事务隔离级别可以显著减少脏读问题。最常见的隔离级别有四种:读未提交(Read Uncommitted)、读已提交(Read Committed)、可重复读(Repeatable Read)和序列化(Serializable)。</li><li><strong>读已提交(Read Committed)</strong>:这是防止脏读的最常见隔离级别。它确保一个事务只能读取另一个事务已提交的数据。</li><li><strong>可重复读(Repeatable Read)</strong>和<strong>序列化(Serializable)</strong>:这两个级别提供了更高的隔离度,但可能会影响性能。</li></ul><p>在SQL中,设置隔离级别的示例:</p><figure class="highlight sql"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SET</span> TRANSACTION ISOLATION LEVEL READ COMMITTED;</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>使用行级锁(Row-Level Locking)</strong>:</p><ul><li>确保在读取和写入数据时使用行级锁,而不是表级锁。行级锁可以减少并发访问时的冲突,从而避免脏读。</li><li>使用 <code>SELECT ... FOR UPDATE</code> 或者 <code>SELECT ... LOCK IN SHARE MODE</code> 来锁定读取的行。</li></ul></li><li><p><strong>乐观锁和悲观锁</strong>:</p><ul><li><strong>乐观锁</strong>:通常使用版本号或时间戳来控制并发。在读取数据时不加锁,但在更新时检查数据是否被修改过。</li><li><strong>悲观锁</strong>:在读取数据时直接加锁,确保其他事务不能修改数据,直到当前事务完成。</li></ul></li><li><p><strong>事务管理</strong>:</p><ul><li>确保事务在适当的时间提交或回滚。避免长时间未提交的事务,这会增加发生脏读的风险。</li><li>使用事务时,确保每个事务的作用范围清晰且尽量短。</li></ul></li><li><p><strong>数据库设置</strong>:</p><ul><li>检查和调整数据库的配置,确保其默认的隔离级别符合您的应用需求。例如,在MySQL中,可以通过配置文件或动态设置更改隔离级别。</li></ul></li><li><p><strong>编程实践</strong>:</p><ul><li>在代码中确保每个数据库操作都在适当的事务范围内。</li><li>使用一致的数据库访问模式,确保读写操作的顺序和隔离性。</li></ul></li></ol><p>以下是一个代码示例,展示如何在Java中使用JDBC设置事务隔离级别:</p><figure class="highlight java"><table><tbody><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="type">Connection</span> <span class="variable">conn</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> conn = DriverManager.getConnection(DB_URL, USER, PASS);</span><br><span class="line"> conn.setAutoCommit(<span class="literal">false</span>); <span class="comment">// 关闭自动提交</span></span><br><span class="line"> conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); <span class="comment">// 设置事务隔离级别</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 执行数据库操作</span></span><br><span class="line"> <span class="type">Statement</span> <span class="variable">stmt</span> <span class="operator">=</span> conn.createStatement();</span><br><span class="line"> <span class="type">ResultSet</span> <span class="variable">rs</span> <span class="operator">=</span> stmt.executeQuery(<span class="string">"SELECT * FROM my_table WHERE id = 1 FOR UPDATE"</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> (rs.next()) {</span><br><span class="line"> <span class="comment">// 更新操作</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> conn.commit(); <span class="comment">// 提交事务</span></span><br><span class="line">} <span class="keyword">catch</span> (SQLException e) {</span><br><span class="line"> <span class="keyword">if</span> (conn != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> conn.rollback(); <span class="comment">// 回滚事务</span></span><br><span class="line"> } <span class="keyword">catch</span> (SQLException ex) {</span><br><span class="line"> ex.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> e.printStackTrace();</span><br><span class="line">} <span class="keyword">finally</span> {</span><br><span class="line"> <span class="keyword">if</span> (conn != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> conn.close(); <span class="comment">// 关闭连接</span></span><br><span class="line"> } <span class="keyword">catch</span> (SQLException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>通过以上方法,可以有效避免脏读问题,提高系统的并发处理能力和数据一致性。</p><h1 id="11-讲讲threadlocal的底层原理。"><a href="#11-讲讲threadlocal的底层原理。" class="headerlink" title="11.讲讲threadlocal的底层原理。"></a>11.讲讲threadlocal的底层原理。</h1><p><code>ThreadLocal</code> 是 Java 提供的一种机制,用于为每个线程提供独立的变量副本,使每个线程都可以独立地修改自己的变量副本,而不会影响其他线程的变量副本。<code>ThreadLocal</code> 的主要作用是避免线程间共享数据,从而防止线程间的数据竞争问题。了解 <code>ThreadLocal</code> 的底层原理可以帮助我们更好地使用它并避免潜在的问题。</p><h3 id="ThreadLocal-的基本用法"><a href="#ThreadLocal-的基本用法" class="headerlink" title="ThreadLocal 的基本用法"></a><code>ThreadLocal</code> 的基本用法</h3><p>以下是一个简单的 <code>ThreadLocal</code> 使用示例:</p><figure class="highlight java"><table><tbody><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="keyword">class</span> <span class="title class_">ThreadLocalExample</span> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> <span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Runnable</span> <span class="variable">task</span> <span class="operator">=</span> () -> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">value</span> <span class="operator">=</span> threadLocal.get();</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" initial value: "</span> + value);</span><br><span class="line"> threadLocal.set(value + <span class="number">1</span>);</span><br><span class="line"> System.out.println(Thread.currentThread().getName() + <span class="string">" updated value: "</span> + threadLocal.get());</span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> <span class="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(task, <span class="string">"Thread-1"</span>);</span><br><span class="line"> <span class="type">Thread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(task, <span class="string">"Thread-2"</span>);</span><br><span class="line"></span><br><span class="line"> thread1.start();</span><br><span class="line"> thread2.start();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="ThreadLocal-的底层原理"><a href="#ThreadLocal-的底层原理" class="headerlink" title="ThreadLocal 的底层原理"></a><code>ThreadLocal</code> 的底层原理</h3><p><code>ThreadLocal</code> 的工作机制主要依赖于每个线程内部维护的一个 <code>ThreadLocalMap</code> 实例。每个线程有一个自己的 <code>ThreadLocalMap</code>,这个 <code>ThreadLocalMap</code> 通过 <code>ThreadLocal</code> 对象作为键来存储线程的私有变量。</p><h4 id="主要组件"><a href="#主要组件" class="headerlink" title="主要组件"></a>主要组件</h4><ol><li><p><strong>Thread 类</strong>:<br><code>Thread</code> 类中有一个 <code>ThreadLocal.ThreadLocalMap</code> 类型的字段 <code>threadLocals</code>,用于存储该线程的所有 <code>ThreadLocal</code> 变量。</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Thread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> {</span><br><span class="line"> ThreadLocal.<span class="type">ThreadLocalMap</span> <span class="variable">threadLocals</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="comment">// Other fields and methods...</span></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>ThreadLocal 类</strong>:<br><code>ThreadLocal</code> 类的每个实例实际上对应一个键值对,其中键是 <code>ThreadLocal</code> 自身,值是具体的变量值。</p><figure class="highlight java"><table><tbody><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">public</span> <span class="keyword">class</span> <span class="title class_">ThreadLocal</span><T> {</span><br><span class="line"> <span class="comment">// 内部静态类,用于存储键值对</span></span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">ThreadLocalMap</span> {</span><br><span class="line"> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Entry</span> <span class="keyword">extends</span> <span class="title class_">WeakReference</span><ThreadLocal<?>> {</span><br><span class="line"> Object value;</span><br><span class="line"> Entry(ThreadLocal<?> k, Object v) {</span><br><span class="line"> <span class="built_in">super</span>(k);</span><br><span class="line"> value = v;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// ThreadLocalMap 的具体实现</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> T <span class="title function_">get</span><span class="params">()</span> {</span><br><span class="line"> <span class="comment">// 获取当前线程</span></span><br><span class="line"> <span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line"> <span class="comment">// 获取当前线程的 ThreadLocalMap</span></span><br><span class="line"> <span class="type">ThreadLocalMap</span> <span class="variable">map</span> <span class="operator">=</span> getMap(t);</span><br><span class="line"> <span class="comment">// 在 map 中查找当前 ThreadLocal 对应的值</span></span><br><span class="line"> ThreadLocalMap.<span class="type">Entry</span> <span class="variable">e</span> <span class="operator">=</span> map.getEntry(<span class="built_in">this</span>);</span><br><span class="line"> <span class="comment">// 返回值或初始化值</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">protected</span> T <span class="title function_">initialValue</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> ThreadLocalMap <span class="title function_">getMap</span><span class="params">(Thread t)</span> {</span><br><span class="line"> <span class="keyword">return</span> t.threadLocals;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ol><h4 id="工作机制"><a href="#工作机制" class="headerlink" title="工作机制"></a>工作机制</h4><ol><li><p><strong>存取数据</strong>:<br>每次调用 <code>ThreadLocal</code> 的 <code>get</code> 或 <code>set</code> 方法时,<code>ThreadLocal</code> 会首先获取当前线程(通过 <code>Thread.currentThread()</code>)。然后,它会检查这个线程是否已经有一个 <code>ThreadLocalMap</code>。如果没有,它会创建一个新的 <code>ThreadLocalMap</code>。</p></li><li><p><strong>存储数据</strong>:<br><code>ThreadLocalMap</code> 是一个定制的哈希表结构,键是 <code>ThreadLocal</code> 实例,值是实际的数据。在存储数据时,<code>ThreadLocal</code> 使用自身作为键,将数据存储到当前线程的 <code>ThreadLocalMap</code> 中。</p></li><li><p><strong>垃圾回收</strong>:<br><code>ThreadLocalMap</code> 中的键(<code>ThreadLocal</code> 实例)是弱引用(<code>WeakReference</code>)。这意味着,如果一个 <code>ThreadLocal</code> 实例没有其他强引用,它可以被垃圾回收。这可以防止内存泄漏,但也需要用户在不需要时显式地调用 <code>remove</code> 方法来清除数据。</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">remove</span><span class="params">()</span> {</span><br><span class="line"> <span class="type">ThreadLocalMap</span> <span class="variable">m</span> <span class="operator">=</span> getMap(Thread.currentThread());</span><br><span class="line"> <span class="keyword">if</span> (m != <span class="literal">null</span>) {</span><br><span class="line"> m.remove(<span class="built_in">this</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure></li></ol><h3 id="内存泄漏问题"><a href="#内存泄漏问题" class="headerlink" title="内存泄漏问题"></a>内存泄漏问题</h3><p>虽然 <code>ThreadLocal</code> 设计时考虑了内存泄漏问题,但实际使用中如果不慎重仍可能导致内存泄漏。因为 <code>ThreadLocalMap</code> 的值部分是强引用,如果没有显式地删除 <code>ThreadLocal</code> 变量,当线程长时间存在时,其对应的 <code>ThreadLocal</code> 变量及其值将不会被回收。</p><h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p><code>ThreadLocal</code> 提供了一种方便的方式来实现线程本地存储,每个线程都可以拥有自己独立的变量副本而不影响其他线程。这种机制在实现简单的线程隔离时非常有用,但需要谨慎使用,特别是在长生命周期的线程(如线程池中的线程)中,应该在不需要时显式地清除 <code>ThreadLocal</code> 变量,以避免内存泄漏。</p><h1 id="12-浮点数精度丢失问题考虑过吗?"><a href="#12-浮点数精度丢失问题考虑过吗?" class="headerlink" title="12.浮点数精度丢失问题考虑过吗?"></a>12.浮点数精度丢失问题考虑过吗?</h1><p>浮点数精度丢失问题是计算机科学中的一个常见问题,尤其在处理金融计算、科学计算和图形渲染等领域时。浮点数在计算机中是以二进制形式表示的,而很多十进制的小数无法用有限的二进制位精确表示,从而导致精度丢失。</p><h3 id="浮点数表示"><a href="#浮点数表示" class="headerlink" title="浮点数表示"></a>浮点数表示</h3><p>在计算机中,浮点数通常使用 IEEE 754 标准表示,包括单精度(32位)和双精度(64位):</p><ul><li><strong>单精度浮点数(32位)</strong>:1位符号位,8位指数位,23位尾数位。</li><li><strong>双精度浮点数(64位)</strong>:1位符号位,11位指数位,52位尾数位。</li></ul><p>由于位数有限,某些数值(例如,0.1 或 1/3)无法精确表示,只能近似表示,从而导致精度丢失。</p><h3 id="常见的精度丢失问题"><a href="#常见的精度丢失问题" class="headerlink" title="常见的精度丢失问题"></a>常见的精度丢失问题</h3><ol><li><p><strong>简单的十进制小数表示问题</strong>:</p><ul><li>十进制数 0.1 在二进制中是一个无限循环小数,无法精确表示。因此,用浮点数表示 0.1 时,实际存储的是一个近似值。</li></ul></li><li><p><strong>累积误差</strong>:</p><ul><li>在多次运算中,精度误差可能累积,导致结果与期望值偏差较大。例如,循环加法操作中,每次小的误差可能在多次运算后累积成较大的误差。</li></ul></li><li><p><strong>舍入误差</strong>:</p><ul><li>当两个浮点数相加时,较小的数可能被舍入而导致丢失。例如,1.0 + 1e-17 可能依然等于 1.0。</li></ul></li><li><p><strong>比较误差</strong>:</p><ul><li>由于浮点数的精度问题,直接比较两个浮点数的相等性可能会出错。通常使用一个很小的阈值(如 epsilon)来判断两个浮点数是否接近。</li></ul></li></ol><h3 id="解决精度丢失的方法"><a href="#解决精度丢失的方法" class="headerlink" title="解决精度丢失的方法"></a>解决精度丢失的方法</h3><ol><li><p><strong>使用高精度库</strong>:</p><ul><li>在需要高精度计算的场合,可以使用大数或高精度浮点数库。例如,Java 中的 <code>BigDecimal</code>,Python 中的 <code>decimal</code> 模块,C++ 中的 <code>Boost.Multiprecision</code>。</li></ul> <figure class="highlight java"><table><tbody><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> java.math.BigDecimal;</span><br><span class="line"></span><br><span class="line"><span class="type">BigDecimal</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.1"</span>);</span><br><span class="line"><span class="type">BigDecimal</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.2"</span>);</span><br><span class="line"><span class="type">BigDecimal</span> <span class="variable">c</span> <span class="operator">=</span> a.add(b);</span><br><span class="line">System.out.println(c); <span class="comment">// 输出 0.3</span></span><br></pre></td></tr></tbody></table></figure></li><li><p><strong>避免累积误差</strong>:</p><ul><li>对于需要多次累加的操作,使用更高精度的数据类型或者通过重新排序运算顺序来减少误差累积。</li></ul></li><li><p><strong>使用适当的舍入模式</strong>:</p><ul><li>在进行四舍五入操作时,使用适当的舍入模式来减少误差。例如,<code>BigDecimal</code> 提供了多种舍入模式可供选择。</li></ul></li><li><p><strong>比较浮点数时使用阈值</strong>:</p><ul><li>比较浮点数时,使用一个很小的阈值来判断它们是否足够接近。</li></ul> <figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">areAlmostEqual</span><span class="params">(<span class="type">double</span> a, <span class="type">double</span> b, <span class="type">double</span> epsilon)</span> {</span><br><span class="line"> <span class="keyword">return</span> Math.abs(a - b) < epsilon;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="type">double</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1</span> * <span class="number">3</span>;</span><br><span class="line"><span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.3</span>;</span><br><span class="line">System.out.println(areAlmostEqual(a, b, <span class="number">1e-10</span>)); <span class="comment">// 输出 true</span></span><br></pre></td></tr></tbody></table></figure></li></ol><h3 id="示例代码"><a href="#示例代码" class="headerlink" title="示例代码"></a>示例代码</h3><p>以下是一个示例,展示了如何使用 <code>BigDecimal</code> 解决浮点数精度丢失问题:</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">PrecisionExample</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">double</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.2</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line"> System.out.println(<span class="string">"Using double: "</span> + c); <span class="comment">// 输出 0.30000000000000004</span></span><br><span class="line"></span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">bd1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.1"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">bd2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.2"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">bd3</span> <span class="operator">=</span> bd1.add(bd2);</span><br><span class="line"> System.out.println(<span class="string">"Using BigDecimal: "</span> + bd3); <span class="comment">// 输出 0.3</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="小结-1"><a href="#小结-1" class="headerlink" title="小结"></a>小结</h3><p>浮点数精度丢失是计算机科学中不可避免的问题,但可以通过使用高精度计算库、合理的运算方法和比较策略来减少和控制误差。了解浮点数的表示和运算机制是解决精度问题的基础。</p><h1 id="13-浮点数为什么会精度丢失,讲讲底层?"><a href="#13-浮点数为什么会精度丢失,讲讲底层?" class="headerlink" title="13.浮点数为什么会精度丢失,讲讲底层?"></a>13.浮点数为什么会精度丢失,讲讲底层?</h1><p>浮点数精度丢失的根本原因在于计算机中浮点数的表示方式和实际计算过程。计算机使用二进制(base-2)表示浮点数,而许多十进制(base-10)小数无法用有限的二进制位精确表示,从而导致精度丢失。</p><h3 id="浮点数的表示方式"><a href="#浮点数的表示方式" class="headerlink" title="浮点数的表示方式"></a>浮点数的表示方式</h3><p>浮点数通常使用 IEEE 754 标准表示,这个标准定义了浮点数的二进制表示方法。浮点数由三个部分组成:</p><ol><li><strong>符号位(Sign bit)</strong>:决定浮点数的正负,0 表示正数,1 表示负数。</li><li><strong>指数部分(Exponent)</strong>:表示浮点数的规模(即数值的范围)。</li><li><strong>尾数部分(Mantissa or Significand)</strong>:表示浮点数的精度(即数值的精确度)。</li></ol><h4 id="单精度浮点数(32位)"><a href="#单精度浮点数(32位)" class="headerlink" title="单精度浮点数(32位)"></a>单精度浮点数(32位)</h4><ul><li>1位符号位</li><li>8位指数位(用于存储指数,采用偏移量为 127 的偏置表示)</li><li>23位尾数位</li></ul><p>格式如下:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(-1)^sign * 1.mantissa * 2^(exponent - 127)</span><br></pre></td></tr></tbody></table></figure><h4 id="双精度浮点数(64位)"><a href="#双精度浮点数(64位)" class="headerlink" title="双精度浮点数(64位)"></a>双精度浮点数(64位)</h4><ul><li>1位符号位</li><li>11位指数位(偏移量为 1023)</li><li>52位尾数位</li></ul><p>格式如下:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(-1)^sign * 1.mantissa * 2^(exponent - 1023)</span><br></pre></td></tr></tbody></table></figure><h3 id="精度丢失的原因"><a href="#精度丢失的原因" class="headerlink" title="精度丢失的原因"></a>精度丢失的原因</h3><ol><li><p><strong>有限的尾数位</strong>:</p><ul><li>由于尾数位的数量有限,很多十进制小数在二进制中无法精确表示,只能近似表示。例如,十进制的 0.1 在二进制中是一个无限循环小数,其二进制表示为 0.000110011001100110011001100110011…(循环节为 0011)。因为位数有限,计算机会截断这个表示,从而导致近似值。</li></ul></li><li><p><strong>二进制和十进制的差异</strong>:</p><ul><li>很多十进制的小数在二进制中无法精确表示,因为二进制只能精确表示分母为 2 的幂的分数。例如,1/2、1/4、1/8 可以精确表示,而 1/3、1/5、0.1 则无法精确表示。</li></ul></li><li><p><strong>舍入误差</strong>:</p><ul><li>当两个浮点数进行运算(如加法、减法、乘法、除法)时,结果可能需要舍入到可表示的位数范围内。这种舍入会引入额外的误差,尤其在多次运算累积时,这种误差会显著增加。</li></ul></li></ol><h3 id="浮点数运算中的具体例子"><a href="#浮点数运算中的具体例子" class="headerlink" title="浮点数运算中的具体例子"></a>浮点数运算中的具体例子</h3><h4 id="示例:0-1-0-2"><a href="#示例:0-1-0-2" class="headerlink" title="示例:0.1 + 0.2"></a>示例:0.1 + 0.2</h4><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FloatPrecision</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">double</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.2</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">c</span> <span class="operator">=</span> a + b;</span><br><span class="line"> System.out.println(c); <span class="comment">// 输出 0.30000000000000004</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="解释"><a href="#解释" class="headerlink" title="解释"></a>解释</h4><ol><li><p><strong>0.1 和 0.2 的二进制表示</strong>:</p><ul><li>0.1 在二进制中近似表示为 0.000110011001100110011001100110011…</li><li>0.2 在二进制中近似表示为 0.001100110011001100110011001100110…</li></ul></li><li><p><strong>运算过程中的舍入</strong>:</p><ul><li>当 0.1 和 0.2 进行加法运算时,计算机会进行二进制加法。由于位数限制,结果会被截断或舍入,从而导致最终结果不完全准确。</li></ul></li><li><p><strong>结果的二进制表示</strong>:</p><ul><li>加法运算后的二进制结果为 0.010011001100110011001100110011001…,转换回十进制大约为 0.30000000000000004。</li></ul></li></ol><h3 id="如何减少精度丢失"><a href="#如何减少精度丢失" class="headerlink" title="如何减少精度丢失"></a>如何减少精度丢失</h3><ol><li><p><strong>使用高精度数据类型</strong>:</p><ul><li>在需要高精度计算的场合,可以使用高精度库,如 Java 的 <code>BigDecimal</code>,Python 的 <code>decimal</code> 模块等。</li></ul></li><li><p><strong>适当的舍入策略</strong>:</p><ul><li>选择合适的舍入策略来控制运算误差,例如四舍五入、向零舍入等。</li></ul></li><li><p><strong>分段计算</strong>:</p><ul><li>对于累积误差较大的计算,尽量分段计算,并在中间进行舍入和调整。</li></ul></li><li><p><strong>避免直接比较</strong>:</p><ul><li>在比较两个浮点数时,使用一个小的阈值(如 epsilon)来判断它们是否接近,而不是直接比较相等。</li></ul></li></ol><h3 id="示例:使用-BigDecimal"><a href="#示例:使用-BigDecimal" class="headerlink" title="示例:使用 BigDecimal"></a>示例:使用 <code>BigDecimal</code></h3><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BigDecimalExample</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.1"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.2"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">c</span> <span class="operator">=</span> a.add(b);</span><br><span class="line"> System.out.println(c); <span class="comment">// 输出 0.3</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>通过理解浮点数的底层表示和运算机制,我们可以更好地应对浮点数精度丢失问题,并在需要高精度计算时选择合适的解决方案。</p><h1 id="14-如何解决精度丢失问题?"><a href="#14-如何解决精度丢失问题?" class="headerlink" title="14.如何解决精度丢失问题?"></a>14.如何解决精度丢失问题?</h1><p>解决浮点数精度丢失问题的主要方法包括使用高精度数据类型、适当的舍入策略、分段计算以及避免直接比较等。以下是详细的解决方案和代码示例:</p><h3 id="1-使用高精度数据类型"><a href="#1-使用高精度数据类型" class="headerlink" title="1. 使用高精度数据类型"></a>1. 使用高精度数据类型</h3><p>在需要高精度计算的场合,可以使用高精度库,如 Java 的 <code>BigDecimal</code>、Python 的 <code>decimal</code> 模块等。这些库能够精确表示和计算十进制数。</p><h4 id="Java-示例"><a href="#Java-示例" class="headerlink" title="Java 示例"></a>Java 示例</h4><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">BigDecimalExample</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.1"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">b</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.2"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">c</span> <span class="operator">=</span> a.add(b);</span><br><span class="line"> System.out.println(c); <span class="comment">// 输出 0.3</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="Python-示例"><a href="#Python-示例" class="headerlink" title="Python 示例"></a>Python 示例</h4><figure class="highlight python"><table><tbody><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">from</span> decimal <span class="keyword">import</span> Decimal</span><br><span class="line"></span><br><span class="line">a = Decimal(<span class="string">'0.1'</span>)</span><br><span class="line">b = Decimal(<span class="string">'0.2'</span>)</span><br><span class="line">c = a + b</span><br><span class="line"><span class="built_in">print</span>(c) <span class="comment"># 输出 0.3</span></span><br></pre></td></tr></tbody></table></figure><h3 id="2-使用适当的舍入策略"><a href="#2-使用适当的舍入策略" class="headerlink" title="2. 使用适当的舍入策略"></a>2. 使用适当的舍入策略</h3><p>在进行浮点数运算时,可以使用适当的舍入策略来控制误差。常见的舍入模式有:四舍五入、向上舍入、向下舍入等。</p><h4 id="Java-示例-1"><a href="#Java-示例-1" class="headerlink" title="Java 示例"></a>Java 示例</h4><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.math.BigDecimal;</span><br><span class="line"><span class="keyword">import</span> java.math.RoundingMode;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RoundingExample</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">a</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BigDecimal</span>(<span class="string">"0.3333"</span>);</span><br><span class="line"> <span class="type">BigDecimal</span> <span class="variable">rounded</span> <span class="operator">=</span> a.setScale(<span class="number">2</span>, RoundingMode.HALF_UP);</span><br><span class="line"> System.out.println(rounded); <span class="comment">// 输出 0.33</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="3-分段计算"><a href="#3-分段计算" class="headerlink" title="3. 分段计算"></a>3. 分段计算</h3><p>对于累积误差较大的计算,可以将计算分段进行,并在中间进行舍入和调整,减少误差的积累。</p><h4 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h4><figure class="highlight java"><table><tbody><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SegmentedCalculation</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">double</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0.0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < <span class="number">1000000</span>; i++) {</span><br><span class="line"> sum += <span class="number">0.1</span>;</span><br><span class="line"> <span class="keyword">if</span> (i % <span class="number">1000</span> == <span class="number">0</span>) {</span><br><span class="line"> sum = Math.round(sum * <span class="number">1e10</span>) / <span class="number">1e10</span>; <span class="comment">// 中间舍入</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> System.out.println(sum); <span class="comment">// 输出接近预期结果</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="4-避免直接比较"><a href="#4-避免直接比较" class="headerlink" title="4. 避免直接比较"></a>4. 避免直接比较</h3><p>在比较两个浮点数时,使用一个小的阈值(如 epsilon)来判断它们是否接近,而不是直接比较相等。</p><h4 id="Java-示例-2"><a href="#Java-示例-2" class="headerlink" title="Java 示例"></a>Java 示例</h4><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">FloatComparison</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="type">boolean</span> <span class="title function_">areAlmostEqual</span><span class="params">(<span class="type">double</span> a, <span class="type">double</span> b, <span class="type">double</span> epsilon)</span> {</span><br><span class="line"> <span class="keyword">return</span> Math.abs(a - b) < epsilon;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">double</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">0.1</span> * <span class="number">3</span>;</span><br><span class="line"> <span class="type">double</span> <span class="variable">b</span> <span class="operator">=</span> <span class="number">0.3</span>;</span><br><span class="line"> System.out.println(areAlmostEqual(a, b, <span class="number">1e-10</span>)); <span class="comment">// 输出 true</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="Python-示例-1"><a href="#Python-示例-1" class="headerlink" title="Python 示例"></a>Python 示例</h4><figure class="highlight python"><table><tbody><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">def</span> <span class="title function_">are_almost_equal</span>(<span class="params">a, b, epsilon</span>):</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">abs</span>(a - b) < epsilon</span><br><span class="line"></span><br><span class="line">a = <span class="number">0.1</span> * <span class="number">3</span></span><br><span class="line">b = <span class="number">0.3</span></span><br><span class="line"><span class="built_in">print</span>(are_almost_equal(a, b, <span class="number">1e-10</span>)) <span class="comment"># 输出 True</span></span><br></pre></td></tr></tbody></table></figure><h3 id="5-使用专门的库和数据结构"><a href="#5-使用专门的库和数据结构" class="headerlink" title="5. 使用专门的库和数据结构"></a>5. 使用专门的库和数据结构</h3><p>在特定领域(如金融计算),使用专门设计的库和数据结构可以有效解决精度问题。例如,金融计算中常用的库 <code>Joda-Money</code> 或 <code>Money</code> 库,可以处理货币和金融数值的精度问题。</p><h4 id="Java-示例-3"><a href="#Java-示例-3" class="headerlink" title="Java 示例"></a>Java 示例</h4><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> org.joda.money.CurrencyUnit;</span><br><span class="line"><span class="keyword">import</span> org.joda.money.Money;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">JodaMoneyExample</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> {</span><br><span class="line"> <span class="type">Money</span> <span class="variable">a</span> <span class="operator">=</span> Money.of(CurrencyUnit.USD, <span class="number">0.1</span>);</span><br><span class="line"> <span class="type">Money</span> <span class="variable">b</span> <span class="operator">=</span> Money.of(CurrencyUnit.USD, <span class="number">0.2</span>);</span><br><span class="line"> <span class="type">Money</span> <span class="variable">c</span> <span class="operator">=</span> a.plus(b);</span><br><span class="line"> System.out.println(c); <span class="comment">// 输出 USD 0.30</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>通过这些方法,我们可以有效减少或避免浮点数精度丢失问题,在需要高精度计算的场合下保证计算结果的准确性。</p><h1 id="15-jwt验证流程?"><a href="#15-jwt验证流程?" class="headerlink" title="15.jwt验证流程?"></a>15.jwt验证流程?</h1><p>JSON Web Token (JWT) 是一种基于 JSON 的开放标准(RFC 7519)实现,用于在网络应用环境中传递声明。JWT 通常用于认证和授权。以下是 JWT 验证流程的详细说明:</p><h3 id="JWT-结构"><a href="#JWT-结构" class="headerlink" title="JWT 结构"></a>JWT 结构</h3><p>一个 JWT 由三个部分组成,每部分之间用点 (<code>.</code>) 分隔:</p><ol><li><strong>Header</strong>:头部</li><li><strong>Payload</strong>:负载</li><li><strong>Signature</strong>:签名</li></ol><p>示例 JWT:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</span><br></pre></td></tr></tbody></table></figure><h3 id="1-Header(头部)"><a href="#1-Header(头部)" class="headerlink" title="1. Header(头部)"></a>1. Header(头部)</h3><p>头部通常由两部分组成:类型(即 JWT)和签名算法(如 HMAC SHA256 或 RSA)。</p><p>示例:</p><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"alg"</span><span class="punctuation">:</span> <span class="string">"HS256"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"typ"</span><span class="punctuation">:</span> <span class="string">"JWT"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>这个 JSON 结构被编码为 Base64Url。</p><h3 id="2-Payload(负载)"><a href="#2-Payload(负载)" class="headerlink" title="2. Payload(负载)"></a>2. Payload(负载)</h3><p>负载部分包含声明(claims)。声明有三种类型:registered、public 和 private。</p><p>示例:</p><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"sub"</span><span class="punctuation">:</span> <span class="string">"1234567890"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"John Doe"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"iat"</span><span class="punctuation">:</span> <span class="number">1516239022</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>这个 JSON 结构也被编码为 Base64Url。</p><h3 id="3-Signature(签名)"><a href="#3-Signature(签名)" class="headerlink" title="3. Signature(签名)"></a>3. Signature(签名)</h3><p>为了创建签名,您需要对编码后的 header 和 payload 进行签名:</p><figure class="highlight plaintext"><table><tbody><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">HMACSHA256(</span><br><span class="line"> base64UrlEncode(header) + "." +</span><br><span class="line"> base64UrlEncode(payload),</span><br><span class="line"> secret</span><br><span class="line">)</span><br></pre></td></tr></tbody></table></figure><p>签名用于验证消息在传输过程中是否未被篡改。</p><h3 id="JWT-验证流程"><a href="#JWT-验证流程" class="headerlink" title="JWT 验证流程"></a>JWT 验证流程</h3><ol><li><p><strong>客户端请求认证</strong>:</p><ul><li>用户通过登录页面输入用户名和密码。</li><li>服务器验证用户的凭据。如果验证成功,服务器生成 JWT 并将其返回给客户端。</li></ul></li><li><p><strong>客户端存储 JWT</strong>:</p><ul><li>客户端(通常是浏览器)收到 JWT 后,将其存储在本地存储或 cookie 中。</li></ul></li><li><p><strong>客户端请求受保护资源</strong>:</p><ul><li>客户端在后续请求中将 JWT 添加到 HTTP 请求头中(通常是 <code>Authorization</code> 头部,格式为 <code>Bearer <token></code>)。</li></ul></li><li><p><strong>服务器验证 JWT</strong>:</p><ul><li>服务器接收到客户端请求后,提取 JWT 并进行验证。</li><li>验证步骤包括:<ol><li><strong>检查签名</strong>:<ul><li>使用服务器端的密钥(对称加密)或公钥(非对称加密)验证签名是否正确。</li></ul></li><li><strong>解码 JWT</strong>:<ul><li>解码 Base64Url 编码的 header 和 payload,检查 JWT 的结构是否正确。</li></ul></li><li><strong>验证声明</strong>:<ul><li>验证标准声明(如 <code>iss</code>、<code>exp</code>、<code>sub</code> 等),确保 token 未过期且符合预期。</li></ul></li><li><strong>检查权限</strong>:<ul><li>根据负载中的声明,检查用户是否有权限访问请求的资源。</li></ul></li></ol></li></ul></li><li><p><strong>响应客户端请求</strong>:</p><ul><li>如果 JWT 验证成功,服务器处理请求并返回受保护的资源。</li><li>如果 JWT 验证失败,服务器返回适当的错误信息(如 401 未授权)。</li></ul></li></ol><h3 id="示例代码-1"><a href="#示例代码-1" class="headerlink" title="示例代码"></a>示例代码</h3><p>以下是一个简单的 JWT 验证示例,使用 JavaScript 和 Node.js:</p><h4 id="创建和验证-JWT"><a href="#创建和验证-JWT" class="headerlink" title="创建和验证 JWT"></a>创建和验证 JWT</h4><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> jwt = <span class="built_in">require</span>(<span class="string">'jsonwebtoken'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 密钥</span></span><br><span class="line"><span class="keyword">const</span> secret = <span class="string">'your-256-bit-secret'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建 JWT</span></span><br><span class="line"><span class="keyword">const</span> token = jwt.<span class="title function_">sign</span>({ <span class="attr">sub</span>: <span class="string">'1234567890'</span>, <span class="attr">name</span>: <span class="string">'John Doe'</span>, <span class="attr">iat</span>: <span class="title class_">Math</span>.<span class="title function_">floor</span>(<span class="title class_">Date</span>.<span class="title function_">now</span>() / <span class="number">1000</span>) - <span class="number">30</span> }, secret);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Token:'</span>, token);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 验证 JWT</span></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> decoded = jwt.<span class="title function_">verify</span>(token, secret);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Decoded:'</span>, decoded);</span><br><span class="line">} <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">'Invalid token:'</span>, err.<span class="property">message</span>);</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h4 id="在-Express-js-中验证-JWT"><a href="#在-Express-js-中验证-JWT" class="headerlink" title="在 Express.js 中验证 JWT"></a>在 Express.js 中验证 JWT</h4><figure class="highlight javascript"><table><tbody><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">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">const</span> jwt = <span class="built_in">require</span>(<span class="string">'jsonwebtoken'</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> secret = <span class="string">'your-256-bit-secret'</span>;</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">json</span>());</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">post</span>(<span class="string">'/login'</span>, <span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> user = { <span class="attr">id</span>: <span class="number">1</span>, <span class="attr">username</span>: <span class="string">'user'</span> };</span><br><span class="line"> <span class="keyword">const</span> token = jwt.<span class="title function_">sign</span>({ <span class="attr">sub</span>: user.<span class="property">id</span>, <span class="attr">username</span>: user.<span class="property">username</span> }, secret, { <span class="attr">expiresIn</span>: <span class="string">'1h'</span> });</span><br><span class="line"> res.<span class="title function_">json</span>({ token });</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/protected'</span>, <span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> token = req.<span class="property">headers</span>.<span class="property">authorization</span>.<span class="title function_">split</span>(<span class="string">' '</span>)[<span class="number">1</span>];</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> decoded = jwt.<span class="title function_">verify</span>(token, secret);</span><br><span class="line"> res.<span class="title function_">json</span>({ <span class="attr">message</span>: <span class="string">'This is a protected route'</span>, <span class="attr">user</span>: decoded });</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> res.<span class="title function_">status</span>(<span class="number">401</span>).<span class="title function_">json</span>({ <span class="attr">message</span>: <span class="string">'Invalid token'</span> });</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Server running on port 3000'</span>);</span><br><span class="line">});</span><br></pre></td></tr></tbody></table></figure><p>通过以上步骤和示例代码,可以实现 JWT 的生成和验证,从而确保客户端和服务器之间的通信安全且可靠。</p><h1 id="16-jwt的参数是什么样的,有什么意义?"><a href="#16-jwt的参数是什么样的,有什么意义?" class="headerlink" title="16.jwt的参数是什么样的,有什么意义?"></a>16.jwt的参数是什么样的,有什么意义?</h1><p>JWT (JSON Web Token) 是一种紧凑且自包含的方式,用于在各方之间以 JSON 对象的形式安全地传输信息。JWT 通常由三部分组成:Header、Payload 和 Signature。它们分别具有不同的意义和作用。</p><h3 id="1-Header"><a href="#1-Header" class="headerlink" title="1. Header"></a>1. Header</h3><p>Header 通常包含两部分:令牌类型(即 JWT)和所使用的签名算法(如 HMAC SHA256 或 RSA 等)。例如:</p><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"alg"</span><span class="punctuation">:</span> <span class="string">"HS256"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"typ"</span><span class="punctuation">:</span> <span class="string">"JWT"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h3 id="2-Payload"><a href="#2-Payload" class="headerlink" title="2. Payload"></a>2. Payload</h3><p>Payload 是实际传输的数据。它包含声明(claims),即关于实体(通常是用户)和其他数据的陈述。声明有三种类型:</p><ul><li><p>**注册声明 (Registered Claims)**:预定义的声明,如 <code>iss</code> (签发者)、<code>exp</code> (过期时间)、<code>sub</code> (主题)、<code>aud</code> (受众) 等。尽管它们是可选的,但推荐在使用时遵循其规定的含义。</p></li><li><p>**公共声明 (Public Claims)**:可以自由定义的声明,但要避免与 JWT 规范中的注册声明冲突。通常使用 URI 或 URN 格式命名,以确保唯一性。</p></li><li><p>**私有声明 (Private Claims)**:自定义的声明,用于特定应用程序中的信息交换。</p></li></ul><p>例如:</p><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"sub"</span><span class="punctuation">:</span> <span class="string">"1234567890"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"John Doe"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"admin"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h3 id="3-Signature"><a href="#3-Signature" class="headerlink" title="3. Signature"></a>3. Signature</h3><p>签名是为了确保消息在传输过程中不被篡改。签名算法根据 Header 中指定的算法进行加密。通常,它将 Header 和 Payload 进行 Base64 编码,然后使用一个密钥(对于 HMAC 算法)或私钥(对于 RSA 或 ECDSA 算法)进行签名。其生成过程如下:</p><figure class="highlight plaintext"><table><tbody><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">HMACSHA256(</span><br><span class="line"> base64UrlEncode(header) + "." + base64UrlEncode(payload),</span><br><span class="line"> secret</span><br><span class="line">)</span><br></pre></td></tr></tbody></table></figure><p>生成的 JWT 形如:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">xxxxx.yyyyy.zzzzz</span><br></pre></td></tr></tbody></table></figure><p>其中,<code>xxxxx</code> 是 Header 的 Base64Url 编码,<code>yyyyy</code> 是 Payload 的 Base64Url 编码,<code>zzzzz</code> 是 Signature。</p><h3 id="JWT-的使用意义"><a href="#JWT-的使用意义" class="headerlink" title="JWT 的使用意义"></a>JWT 的使用意义</h3><ol><li><strong>安全传输信息</strong>:使用 JWT 可以确保信息在客户端和服务器之间的安全传输,防止信息被篡改。</li><li><strong>无状态的身份验证</strong>:服务器无需保存会话信息,只需验证 JWT,即可确认用户身份。</li><li>**跨域资源共享 (CORS)**:JWT 可以用于跨域资源请求的授权。</li></ol><p>JWT 广泛应用于身份验证、信息传输和分布式系统中,因其紧凑性和自包含性而备受青睐。</p><h1 id="17-密文密码能被存入负载中吗?为什么?"><a href="#17-密文密码能被存入负载中吗?为什么?" class="headerlink" title="17.密文密码能被存入负载中吗?为什么?"></a>17.密文密码能被存入负载中吗?为什么?</h1><p>在设计和使用 JWT (JSON Web Token) 时,将密文密码存入负载(Payload)中是不推荐的,原因如下:</p><h3 id="1-安全性风险"><a href="#1-安全性风险" class="headerlink" title="1. 安全性风险"></a>1. 安全性风险</h3><p>即使是加密的密码,也不应该存储在 JWT 的负载中。原因在于:</p><ul><li><strong>可解密风险</strong>:即使密码是加密的,如果攻击者获取到加密密钥或找到漏洞解密密文,他们就能得到原始密码。</li><li><strong>暴露敏感信息</strong>:JWT 通常在客户端和服务器之间传递。虽然负载被签名以防篡改,但内容并没有被加密。如果使用不安全的传输协议(如 HTTP 而非 HTTPS),攻击者可以截获 JWT 并访问其内容。</li></ul><h3 id="2-JWT-的设计目的"><a href="#2-JWT-的设计目的" class="headerlink" title="2. JWT 的设计目的"></a>2. JWT 的设计目的</h3><p>JWT 的设计目的是用于传递声明(如用户 ID、角色、权限等)而不是敏感信息。JWT 旨在提供一种紧凑、自包含的方式来验证用户身份和传递授权信息,而不是存储敏感数据。</p><h3 id="3-存储敏感信息的最佳实践"><a href="#3-存储敏感信息的最佳实践" class="headerlink" title="3. 存储敏感信息的最佳实践"></a>3. 存储敏感信息的最佳实践</h3><ul><li><strong>避免在 JWT 中存储敏感信息</strong>:应尽量避免在 JWT 的 Payload 中存储任何敏感信息,如密码、信用卡号等。</li><li><strong>使用 HTTPS</strong>:确保通过安全的传输层(HTTPS)来传递 JWT,防止中间人攻击。</li><li><strong>对敏感信息进行加密</strong>:如果确实需要传递敏感信息,应该对其进行加密,并在服务器端进行解密。</li><li><strong>短生命周期和刷新机制</strong>:确保 JWT 有短的有效期(例如 5-15 分钟),并使用刷新令牌来获取新的 JWT,减少被滥用的风险。</li></ul><h3 id="示例-1"><a href="#示例-1" class="headerlink" title="示例"></a>示例</h3><p>假设你需要在 JWT 中传递某些敏感信息,正确的做法是:</p><ol><li><strong>加密敏感信息</strong>:使用安全的加密算法对敏感信息加密。</li><li><strong>使用自定义声明</strong>:在 Payload 中使用自定义声明来传递加密后的信息。</li><li><strong>验证机制</strong>:在服务器端解密并验证这些信息。</li></ol><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"sub"</span><span class="punctuation">:</span> <span class="string">"1234567890"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"John Doe"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"admin"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"encrypted_data"</span><span class="punctuation">:</span> <span class="string">"ENCRYPTED_PASSWORD_HERE"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>然而,即使如此,仍应尽量避免将加密后的密码放入 JWT 中,而是考虑其他安全的身份验证机制,如 OAuth2 的访问令牌和刷新令牌机制。</p><p>总之,出于安全考虑,不应在 JWT 的负载中存储密文密码。JWT 应主要用于传递身份和授权信息,任何敏感信息应通过加密并使用安全的传输协议来保护。</p><h1 id="18-payload中的信息会被解密吗?"><a href="#18-payload中的信息会被解密吗?" class="headerlink" title="18.payload中的信息会被解密吗?"></a>18.payload中的信息会被解密吗?</h1><p>JWT (JSON Web Token) 的 Payload 部分默认情况下是以明文形式存储的,这意味着它不会被加密。因此,Payload 中的信息不会被解密,因为它从一开始就没有被加密。以下是一些关键点解释这一点:</p><h3 id="1-Payload-是明文的"><a href="#1-Payload-是明文的" class="headerlink" title="1. Payload 是明文的"></a>1. Payload 是明文的</h3><p>JWT 的设计使得它的 Payload 部分是以 Base64Url 编码的形式存储的,而不是加密的。Base64Url 编码仅仅是编码,不是加密,所以任何能够获取到 JWT 的人都可以轻松地解码并读取其内容。</p><h3 id="2-签名保护篡改但不保护隐私"><a href="#2-签名保护篡改但不保护隐私" class="headerlink" title="2. 签名保护篡改但不保护隐私"></a>2. 签名保护篡改但不保护隐私</h3><p>JWT 的签名部分是用于验证 Token 的完整性和真实性。它确保了 Payload 和 Header 没有被篡改,但它并不加密 Payload。因此,虽然签名能防止数据被篡改,但不能防止数据被读取。</p><h3 id="3-如果需要加密,使用-JWE-JSON-Web-Encryption"><a href="#3-如果需要加密,使用-JWE-JSON-Web-Encryption" class="headerlink" title="3. 如果需要加密,使用 JWE (JSON Web Encryption)"></a>3. 如果需要加密,使用 JWE (JSON Web Encryption)</h3><p>如果你需要传输的内容包含敏感信息,应该使用 JWE 而不是普通的 JWT。JWE 允许对 Payload 进行加密,从而保护其内容的隐私。JWE 的结构如下:</p><ul><li><strong>Protected Header</strong>:包含加密算法的信息。</li><li><strong>Encrypted Key</strong>:加密的密钥。</li><li><strong>Initialization Vector</strong>:初始化向量,用于加密。</li><li><strong>Ciphertext</strong>:加密后的 Payload。</li><li><strong>Authentication Tag</strong>:用于验证加密内容的真实性。</li></ul><h3 id="示例-2"><a href="#示例-2" class="headerlink" title="示例"></a>示例</h3><p>假设你有一个包含敏感信息的 JWT Payload,如下所示:</p><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"sub"</span><span class="punctuation">:</span> <span class="string">"1234567890"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"John Doe"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"admin"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>如果你只是使用普通的 JWT,任何人都可以解码并看到这些信息:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c</span><br></pre></td></tr></tbody></table></figure><h3 id="解码-Payload-示例"><a href="#解码-Payload-示例" class="headerlink" title="解码 Payload 示例"></a>解码 Payload 示例</h3><p>使用 Base64Url 解码工具,你可以解码上述 Token:</p><figure class="highlight json"><table><tbody><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="punctuation">{</span></span><br><span class="line"> <span class="attr">"sub"</span><span class="punctuation">:</span> <span class="string">"1234567890"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"John Doe"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"admin"</span><span class="punctuation">:</span> <span class="literal"><span class="keyword">true</span></span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><h3 id="使用-JWE-加密-Payload"><a href="#使用-JWE-加密-Payload" class="headerlink" title="使用 JWE 加密 Payload"></a>使用 JWE 加密 Payload</h3><p>为了加密 Payload,应该使用 JWE,以下是简化的 JWE 结构:</p><figure class="highlight json"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"> <span class="attr">"protected"</span><span class="punctuation">:</span> <span class="string">"eyJhbGciOiJSU0EtT0FFUCJ9"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"encrypted_key"</span><span class="punctuation">:</span> <span class="string">"Base64UrlEncodedEncryptedKey"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"iv"</span><span class="punctuation">:</span> <span class="string">"Base64UrlEncodedInitializationVector"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"ciphertext"</span><span class="punctuation">:</span> <span class="string">"Base64UrlEncodedCiphertext"</span><span class="punctuation">,</span></span><br><span class="line"> <span class="attr">"tag"</span><span class="punctuation">:</span> <span class="string">"Base64UrlEncodedAuthenticationTag"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></tbody></table></figure><p>通过使用 JWE,Payload 会被加密,只有授权的一方才能解密并访问其内容。</p><h3 id="结论-2"><a href="#结论-2" class="headerlink" title="结论"></a>结论</h3><p>JWT 的 Payload 部分默认情况下不会被加密,因此信息是可见的。如果需要保护 Payload 中的信息隐私,应使用 JWE 来加密 Payload。这样可以确保只有具备正确解密密钥的授权方才能读取其中的内容。</p><h1 id="19-反向代理和正向代理的区别?"><a href="#19-反向代理和正向代理的区别?" class="headerlink" title="19.反向代理和正向代理的区别?"></a>19.反向代理和正向代理的区别?</h1><p>反向代理(Reverse Proxy)和正向代理(Forward Proxy)是两种不同类型的代理服务器,尽管它们都充当客户端和服务器之间的中介,但它们的用途和工作方式有显著的不同。</p><h3 id="1-正向代理(Forward-Proxy)"><a href="#1-正向代理(Forward-Proxy)" class="headerlink" title="1. 正向代理(Forward Proxy)"></a>1. 正向代理(Forward Proxy)</h3><p><strong>定义</strong>:<br>正向代理是一种代理服务器,位于客户端和原始服务器之间。它代表客户端向服务器发送请求,并将服务器的响应返回给客户端。</p><p><strong>工作方式</strong>:</p><ul><li>客户端(如浏览器)向正向代理服务器发送请求。</li><li>正向代理服务器接收请求并代表客户端将请求转发给目标服务器。</li><li>目标服务器将响应发送给正向代理服务器。</li><li>正向代理服务器将响应返回给客户端。</li></ul><p><strong>用途</strong>:</p><ul><li><strong>访问控制</strong>:通过正向代理,可以控制和过滤客户端的访问权限。例如,限制访问某些网站。</li><li><strong>缓存</strong>:正向代理可以缓存请求的资源,从而加速后续相同请求的处理速度。</li><li><strong>匿名性</strong>:隐藏客户端的 IP 地址,从而保护用户隐私。</li></ul><p><strong>示例</strong>:<br>客户端配置正向代理服务器,以访问被防火墙阻挡的网站。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">客户端 -> 正向代理 -> 目标服务器</span><br></pre></td></tr></tbody></table></figure><h3 id="2-反向代理(Reverse-Proxy)"><a href="#2-反向代理(Reverse-Proxy)" class="headerlink" title="2. 反向代理(Reverse Proxy)"></a>2. 反向代理(Reverse Proxy)</h3><p><strong>定义</strong>:<br>反向代理是一种代理服务器,位于服务器端,代表服务器接收客户端的请求,并将请求转发给后端服务器。客户端无需知道实际的后端服务器信息。</p><p><strong>工作方式</strong>:</p><ul><li>客户端向反向代理服务器发送请求。</li><li>反向代理服务器接收请求并根据负载均衡或其他策略将请求转发给适当的后端服务器。</li><li>后端服务器将响应发送给反向代理服务器。</li><li>反向代理服务器将响应返回给客户端。</li></ul><p><strong>用途</strong>:</p><ul><li><strong>负载均衡</strong>:将请求分配到多个后端服务器上,从而平衡负载,提高系统性能和可靠性。</li><li><strong>安全性</strong>:通过隐藏后端服务器的 IP 地址和结构,增强安全性。可以防护 DDoS 攻击。</li><li><strong>缓存</strong>:缓存静态内容,减少后端服务器的压力。</li><li><strong>SSL 卸载</strong>:处理 SSL 加密和解密,减轻后端服务器的负担。</li></ul><p><strong>示例</strong>:<br>客户端向反向代理服务器发送请求,反向代理服务器将请求分发给不同的后端服务器处理。</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">客户端 -> 反向代理 -> 后端服务器群</span><br></pre></td></tr></tbody></table></figure><h3 id="对比总结"><a href="#对比总结" class="headerlink" title="对比总结"></a>对比总结</h3><ul><li><p><strong>方向</strong>:</p><ul><li>正向代理:代理服务器在客户端一侧,代理客户端请求。</li><li>反向代理:代理服务器在服务器一侧,代理服务器响应。</li></ul></li><li><p><strong>用途</strong>:</p><ul><li>正向代理:主要用于访问控制、匿名性和缓存。</li><li>反向代理:主要用于负载均衡、安全性、缓存和 SSL 卸载。</li></ul></li><li><p><strong>隐私</strong>:</p><ul><li>正向代理:隐藏客户端信息。</li><li>反向代理:隐藏服务器信息。</li></ul></li></ul><h3 id="示例图解"><a href="#示例图解" class="headerlink" title="示例图解"></a>示例图解</h3><p><strong>正向代理</strong>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[客户端] -> [正向代理] -> [目标服务器]</span><br></pre></td></tr></tbody></table></figure><p><strong>反向代理</strong>:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[客户端] -> [反向代理] -> [后端服务器1]</span><br><span class="line"> -> [后端服务器2]</span><br><span class="line"> -> [后端服务器3]</span><br></pre></td></tr></tbody></table></figure><p>通过了解正向代理和反向代理的区别,可以根据具体需求选择合适的代理类型来优化网络访问和提高系统安全性。</p><h1 id="20-为什么要反向代理?"><a href="#20-为什么要反向代理?" class="headerlink" title="20.为什么要反向代理?"></a>20.为什么要反向代理?</h1><p>反向代理(Reverse Proxy)在现代网络架构中扮演着至关重要的角色,主要原因如下:</p><h3 id="1-负载均衡"><a href="#1-负载均衡" class="headerlink" title="1. 负载均衡"></a>1. 负载均衡</h3><p>反向代理服务器可以将客户端请求分发到多台后端服务器上,从而均衡负载。这不仅提高了系统的性能和响应速度,还增加了系统的可扩展性和可靠性。常用的负载均衡算法包括轮询、最小连接数和基于权重的分配等。</p><h3 id="2-安全性"><a href="#2-安全性" class="headerlink" title="2. 安全性"></a>2. 安全性</h3><p>反向代理隐藏了后端服务器的实际 IP 地址和结构,增加了系统的安全性。它可以作为一个应用层防火墙,保护后端服务器免受直接攻击。反向代理还可以过滤恶意请求,阻止 DDoS 攻击,并实施 SSL 卸载,确保安全通信。</p><h3 id="3-缓存"><a href="#3-缓存" class="headerlink" title="3. 缓存"></a>3. 缓存</h3><p>反向代理可以缓存静态内容,如图像、视频、CSS 和 JavaScript 文件,从而减少后端服务器的负担,提高响应速度。通过缓存频繁访问的内容,可以显著降低服务器的处理压力和带宽消耗。</p><h3 id="4-SSL-卸载"><a href="#4-SSL-卸载" class="headerlink" title="4. SSL 卸载"></a>4. SSL 卸载</h3><p>SSL/TLS 加密和解密是计算密集型任务。反向代理可以处理 SSL/TLS 卸载,将加密和解密的负担从后端服务器移到反向代理服务器上,从而提高后端服务器的性能。</p><h3 id="5-动态网站加速"><a href="#5-动态网站加速" class="headerlink" title="5. 动态网站加速"></a>5. 动态网站加速</h3><p>反向代理可以通过缓存动态内容来加速动态网站的响应速度。某些反向代理服务器还支持内容压缩和优化,进一步提高页面加载速度。</p><h3 id="6-全球负载均衡"><a href="#6-全球负载均衡" class="headerlink" title="6. 全球负载均衡"></a>6. 全球负载均衡</h3><p>通过使用地理位置感知的反向代理,可以将客户端请求路由到距离最近的服务器,从而减少延迟,提升用户体验。这对于全球范围内有用户访问的网站尤为重要。</p><h3 id="7-简化架构管理"><a href="#7-简化架构管理" class="headerlink" title="7. 简化架构管理"></a>7. 简化架构管理</h3><p>反向代理可以集中管理多个后端服务器,简化了架构管理。它还可以进行协议转换,如将外部的 HTTP 请求转换为内部的 HTTPS 请求,或反之。</p><h3 id="8-高可用性和故障转移"><a href="#8-高可用性和故障转移" class="headerlink" title="8. 高可用性和故障转移"></a>8. 高可用性和故障转移</h3><p>反向代理可以检测后端服务器的健康状态,并将请求转发到可用的服务器上。如果某台后端服务器发生故障,反向代理可以自动将流量转移到其他可用的服务器上,确保服务的高可用性。</p><h3 id="示例应用"><a href="#示例应用" class="headerlink" title="示例应用"></a>示例应用</h3><ul><li>**内容分发网络 (CDN)**:CDN 服务商使用反向代理来缓存和分发内容到全球各地的节点,提高内容交付速度和可靠性。</li><li><strong>大型网站</strong>:如电商平台和社交媒体,通过反向代理实现负载均衡和缓存,以应对大量并发请求。</li><li><strong>企业内部网络</strong>:通过反向代理集中管理和保护内部应用系统,简化网络架构和安全管理。</li></ul><h3 id="结论-3"><a href="#结论-3" class="headerlink" title="结论"></a>结论</h3><p>反向代理在现代网络架构中是一个强大的工具,通过提供负载均衡、安全性、缓存、SSL 卸载、动态网站加速、全球负载均衡、简化架构管理和高可用性等功能,帮助提高系统的性能、可靠性和安全性。无论是小型网站还是大型企业应用,反向代理都是不可或缺的组件。</p><h1 id="21-有自己部署过反向代理和负载均衡吗?"><a href="#21-有自己部署过反向代理和负载均衡吗?" class="headerlink" title="21.有自己部署过反向代理和负载均衡吗?"></a>21.有自己部署过反向代理和负载均衡吗?</h1><p>是的,我可以分享如何部署反向代理和负载均衡的经验。以下是关于部署反向代理和负载均衡的一些常见工具和步骤的详细说明。</p><h3 id="1-使用-Nginx-部署反向代理和负载均衡"><a href="#1-使用-Nginx-部署反向代理和负载均衡" class="headerlink" title="1. 使用 Nginx 部署反向代理和负载均衡"></a>1. 使用 Nginx 部署反向代理和负载均衡</h3><p>Nginx 是一个高性能的 HTTP 服务器和反向代理服务器,也可以用作负载均衡器。以下是基本步骤:</p><h4 id="安装-Nginx"><a href="#安装-Nginx" class="headerlink" title="安装 Nginx"></a>安装 Nginx</h4><p>在大多数 Linux 发行版上,可以通过包管理器安装 Nginx。例如,在 Ubuntu 上:</p><figure class="highlight bash"><table><tbody><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">sudo apt update</span><br><span class="line">sudo apt install nginx</span><br></pre></td></tr></tbody></table></figure><h4 id="配置反向代理"><a href="#配置反向代理" class="headerlink" title="配置反向代理"></a>配置反向代理</h4><p>假设你有一个后端服务器运行在 <code>http://backend_server</code>,可以配置 Nginx 将所有请求代理到该服务器。</p><p>编辑 Nginx 配置文件 <code>/etc/nginx/sites-available/default</code>:</p><figure class="highlight nginx"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="section">server</span> {</span><br><span class="line"> <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">server_name</span> your_domain.com;</span><br><span class="line"></span><br><span class="line"> <span class="section">location</span> / {</span><br><span class="line"> <span class="attribute">proxy_pass</span> http://backend_server;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Forwarded-Proto <span class="variable">$scheme</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>保存文件并重启 Nginx:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl restart nginx</span><br></pre></td></tr></tbody></table></figure><h4 id="配置负载均衡"><a href="#配置负载均衡" class="headerlink" title="配置负载均衡"></a>配置负载均衡</h4><p>假设有多个后端服务器 <code>http://backend1</code> 和 <code>http://backend2</code>,可以配置 Nginx 进行负载均衡。</p><p>编辑 Nginx 配置文件 <code>/etc/nginx/sites-available/default</code>:</p><figure class="highlight nginx"><table><tbody><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="section">upstream</span> backend_servers {</span><br><span class="line"> <span class="attribute">server</span> backend1;</span><br><span class="line"> <span class="attribute">server</span> backend2;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="section">server</span> {</span><br><span class="line"> <span class="attribute">listen</span> <span class="number">80</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">server_name</span> your_domain.com;</span><br><span class="line"></span><br><span class="line"> <span class="section">location</span> / {</span><br><span class="line"> <span class="attribute">proxy_pass</span> http://backend_servers;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> Host <span class="variable">$host</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Real-IP <span class="variable">$remote_addr</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Forwarded-For <span class="variable">$proxy_add_x_forwarded_for</span>;</span><br><span class="line"> <span class="attribute">proxy_set_header</span> X-Forwarded-Proto <span class="variable">$scheme</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>保存文件并重启 Nginx:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl restart nginx</span><br></pre></td></tr></tbody></table></figure><h3 id="2-使用-HAProxy-部署负载均衡"><a href="#2-使用-HAProxy-部署负载均衡" class="headerlink" title="2. 使用 HAProxy 部署负载均衡"></a>2. 使用 HAProxy 部署负载均衡</h3><p>HAProxy 是一个开源的高可用性负载均衡和代理服务器,支持 TCP 和 HTTP 应用。</p><h4 id="安装-HAProxy"><a href="#安装-HAProxy" class="headerlink" title="安装 HAProxy"></a>安装 HAProxy</h4><p>在大多数 Linux 发行版上,可以通过包管理器安装 HAProxy。例如,在 Ubuntu 上:</p><figure class="highlight bash"><table><tbody><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">sudo apt update</span><br><span class="line">sudo apt install haproxy</span><br></pre></td></tr></tbody></table></figure><h4 id="配置负载均衡-1"><a href="#配置负载均衡-1" class="headerlink" title="配置负载均衡"></a>配置负载均衡</h4><p>编辑 HAProxy 配置文件 <code>/etc/haproxy/haproxy.cfg</code>:</p><figure class="highlight plaintext"><table><tbody><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">global</span><br><span class="line"> log /dev/log local0</span><br><span class="line"> log /dev/log local1 notice</span><br><span class="line"> chroot /var/lib/haproxy</span><br><span class="line"> stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners</span><br><span class="line"> stats timeout 30s</span><br><span class="line"> user haproxy</span><br><span class="line"> group haproxy</span><br><span class="line"> daemon</span><br><span class="line"></span><br><span class="line">defaults</span><br><span class="line"> log global</span><br><span class="line"> mode http</span><br><span class="line"> option httplog</span><br><span class="line"> option dontlognull</span><br><span class="line"> timeout connect 5000</span><br><span class="line"> timeout client 50000</span><br><span class="line"> timeout server 50000</span><br><span class="line"> errorfile 400 /etc/haproxy/errors/400.http</span><br><span class="line"> errorfile 403 /etc/haproxy/errors/403.http</span><br><span class="line"> errorfile 408 /etc/haproxy/errors/408.http</span><br><span class="line"> errorfile 500 /etc/haproxy/errors/500.http</span><br><span class="line"> errorfile 502 /etc/haproxy/errors/502.http</span><br><span class="line"> errorfile 503 /etc/haproxy/errors/503.http</span><br><span class="line"> errorfile 504 /etc/haproxy/errors/504.http</span><br><span class="line"></span><br><span class="line">frontend http_front</span><br><span class="line"> bind *:80</span><br><span class="line"> default_backend http_back</span><br><span class="line"></span><br><span class="line">backend http_back</span><br><span class="line"> balance roundrobin</span><br><span class="line"> server backend1 backend1:80 check</span><br><span class="line"> server backend2 backend2:80 check</span><br></pre></td></tr></tbody></table></figure><p>保存文件并重启 HAProxy:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">sudo systemctl restart haproxy</span><br></pre></td></tr></tbody></table></figure><h3 id="3-使用-AWS-Elastic-Load-Balancing-ELB"><a href="#3-使用-AWS-Elastic-Load-Balancing-ELB" class="headerlink" title="3. 使用 AWS Elastic Load Balancing (ELB)"></a>3. 使用 AWS Elastic Load Balancing (ELB)</h3><p>如果你使用 AWS 作为你的基础设施,可以使用 AWS 提供的 Elastic Load Balancing 服务,简化部署。</p><h4 id="创建一个负载均衡器"><a href="#创建一个负载均衡器" class="headerlink" title="创建一个负载均衡器"></a>创建一个负载均衡器</h4><ol><li>登录到 AWS 管理控制台。</li><li>导航到 EC2 服务。</li><li>在左侧导航栏中,点击“负载均衡器”。</li><li>点击“创建负载均衡器”,然后根据向导选择适合你的应用的负载均衡器类型(例如,应用负载均衡器)。</li><li>配置负载均衡器的详细信息,例如监听器、可用区和安全组。</li><li>配置目标组,添加你的后端实例到目标组。</li><li>完成配置并创建负载均衡器。</li></ol><p>通过这些步骤和工具,你可以成功部署反向代理和负载均衡,增强系统的性能、安全性和可扩展性。每个工具都有其独特的优势,可以根据你的具体需求选择最合适的解决方案。</p><h1 id="22-Web-socket的作用,怎么实现的?"><a href="#22-Web-socket的作用,怎么实现的?" class="headerlink" title="22.Web socket的作用,怎么实现的?"></a>22.Web socket的作用,怎么实现的?</h1><h3 id="WebSocket的作用"><a href="#WebSocket的作用" class="headerlink" title="WebSocket的作用"></a>WebSocket的作用</h3><p>WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。与传统的 HTTP 请求-响应模式不同,WebSocket 允许服务器主动向客户端发送数据,而无需客户端每次都发起请求。它适用于实时应用程序,如聊天应用、实时通知、在线游戏、协作工具和股票行情等。</p><h3 id="WebSocket的实现"><a href="#WebSocket的实现" class="headerlink" title="WebSocket的实现"></a>WebSocket的实现</h3><h4 id="1-客户端实现"><a href="#1-客户端实现" class="headerlink" title="1. 客户端实现"></a>1. 客户端实现</h4><p>在客户端,通常使用 JavaScript 和浏览器提供的 WebSocket API 来实现 WebSocket 连接。以下是一个简单的示例:</p><figure class="highlight javascript"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建一个新的 WebSocket 连接</span></span><br><span class="line"><span class="keyword">const</span> socket = <span class="keyword">new</span> <span class="title class_">WebSocket</span>(<span class="string">'ws://example.com/socket'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 连接成功时调用</span></span><br><span class="line">socket.<span class="property">onopen</span> = <span class="keyword">function</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'WebSocket is open now.'</span>);</span><br><span class="line"> <span class="comment">// 向服务器发送一条消息</span></span><br><span class="line"> socket.<span class="title function_">send</span>(<span class="string">'Hello Server!'</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">socket.<span class="property">onmessage</span> = <span class="keyword">function</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Message from server '</span>, event.<span class="property">data</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">socket.<span class="property">onclose</span> = <span class="keyword">function</span>(<span class="params">event</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'WebSocket is closed now.'</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">socket.<span class="property">onerror</span> = <span class="keyword">function</span>(<span class="params">error</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'WebSocket error: '</span>, error);</span><br><span class="line">};</span><br></pre></td></tr></tbody></table></figure><h4 id="2-服务器端实现"><a href="#2-服务器端实现" class="headerlink" title="2. 服务器端实现"></a>2. 服务器端实现</h4><p>服务器端实现 WebSocket 通常需要一个支持 WebSocket 协议的服务器框架。以下是使用 Node.js 和 <code>ws</code> 库实现的示例:</p><p>首先,安装 <code>ws</code> 库:</p><figure class="highlight bash"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install ws</span><br></pre></td></tr></tbody></table></figure><p>然后,创建一个 WebSocket 服务器:</p><figure class="highlight javascript"><table><tbody><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">const</span> <span class="title class_">WebSocket</span> = <span class="built_in">require</span>(<span class="string">'ws'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建 WebSocket 服务器,监听指定的端口</span></span><br><span class="line"><span class="keyword">const</span> wss = <span class="keyword">new</span> <span class="title class_">WebSocket</span>.<span class="title class_">Server</span>({ <span class="attr">port</span>: <span class="number">8080</span> });</span><br><span class="line"></span><br><span class="line">wss.<span class="title function_">on</span>(<span class="string">'connection'</span>, <span class="keyword">function</span> <span class="title function_">connection</span>(<span class="params">ws</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'A new client connected'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 接收到消息时调用</span></span><br><span class="line"> ws.<span class="title function_">on</span>(<span class="string">'message'</span>, <span class="keyword">function</span> <span class="title function_">incoming</span>(<span class="params">message</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'received: %s'</span>, message);</span><br><span class="line"> <span class="comment">// 向客户端发送消息</span></span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="string">`Hello, you sent -> <span class="subst">${message}</span>`</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"> ws.<span class="title function_">on</span>(<span class="string">'close'</span>, <span class="keyword">function</span> <span class="title function_">close</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Client disconnected'</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"> ws.<span class="title function_">on</span>(<span class="string">'error'</span>, <span class="keyword">function</span> <span class="title function_">error</span>(<span class="params">err</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Error: '</span>, err);</span><br><span class="line"> });</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'WebSocket server is listening on ws://localhost:8080'</span>);</span><br></pre></td></tr></tbody></table></figure><h3 id="WebSocket的工作流程"><a href="#WebSocket的工作流程" class="headerlink" title="WebSocket的工作流程"></a>WebSocket的工作流程</h3><ol><li><p><strong>握手阶段</strong>:</p><ul><li>客户端发起一个 HTTP 请求以升级到 WebSocket 协议。</li><li>服务器响应请求并确认协议升级。</li><li>一旦握手成功,连接将从 HTTP 协议升级到 WebSocket 协议。</li></ul></li><li><p><strong>数据传输阶段</strong>:</p><ul><li>客户端和服务器之间可以通过打开的 WebSocket 连接相互发送文本或二进制数据帧。</li><li>这种全双工通信允许实时、低延迟的数据交换。</li></ul></li><li><p><strong>连接关闭</strong>:</p><ul><li>任何一方(客户端或服务器)都可以关闭 WebSocket 连接。</li><li>连接关闭时,双方都会收到通知,进行必要的清理操作。</li></ul></li></ol><h3 id="WebSocket的优势"><a href="#WebSocket的优势" class="headerlink" title="WebSocket的优势"></a>WebSocket的优势</h3><ul><li><strong>低延迟</strong>:相比传统的 HTTP 请求-响应模式,WebSocket 减少了网络延迟,因为它避免了频繁的连接建立和断开。</li><li><strong>双向通信</strong>:允许服务器主动发送数据到客户端,而不仅仅是响应客户端请求。</li><li><strong>节省带宽</strong>:通过保持一个持久连接,减少了 HTTP 请求头的开销。</li></ul><h3 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h3><ul><li><strong>实时聊天应用</strong>:例如,Slack、WhatsApp Web 版。</li><li><strong>实时通知</strong>:如实时推送的社交媒体通知。</li><li><strong>在线协作工具</strong>:如 Google Docs 的实时编辑功能。</li><li><strong>实时游戏</strong>:如多人在线游戏的状态同步。</li><li><strong>金融数据</strong>:如股票行情的实时更新。</li></ul><p>通过了解和实现 WebSocket,可以构建高效、实时的应用程序,从而提升用户体验。</p><img src="/2024/05/18/%E8%8B%8D%E7%A9%B9%E5%A4%96%E5%8D%96%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%80%BB%E7%BB%93/MyBlogs/source/_posts/苍穹外卖面试问题总结/image-20240518132939689.png" alt="image-20240518132939689" style="zoom:200%;">]]></content>
</entry>
<entry>
<title>day022</title>
<link href="/2024/05/14/day28/"/>
<url>/2024/05/14/day28/</url>
<content type="html"><![CDATA[<h2 id="216-组合总和-III"><a href="#216-组合总和-III" class="headerlink" title="216. 组合总和 III"></a><a href="https://leetcode.cn/problems/combination-sum-iii/">216. 组合总和 III</a></h2><h3 id="思路:和077差不多,多加一个sum求和和目标值比较,以及回溯sum-i"><a href="#思路:和077差不多,多加一个sum求和和目标值比较,以及回溯sum-i" class="headerlink" title="思路:和077差不多,多加一个sum求和和目标值比较,以及回溯sum - i"></a>思路:和077差不多,多加一个sum求和和目标值比较,以及回溯sum - i</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.回溯;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class num216 {</span><br><span class="line"> List<List<Integer>> result = new ArrayList<>();</span><br><span class="line"> LinkedList<Integer> path = new LinkedList<>();</span><br><span class="line"> public List<List<Integer>> combinationSum3(int k, int n) {</span><br><span class="line"> backingTrack(k, n, 1, 0);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void backingTrack(int k, int n, int startNum, int sum){</span><br><span class="line"> // 减枝</span><br><span class="line"> if (sum > n) {</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> if (path.size() == k && sum == n){</span><br><span class="line"> result.add(new ArrayList<>(path));</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> for (int i = startNum; i <= 9 - (k - path.size()) + 1; i++) {//横向遍历</span><br><span class="line"> path.add(i);</span><br><span class="line"> sum += i;</span><br><span class="line"> backingTrack(k, n,i + 1, sum);//递归</span><br><span class="line"> path.removeLast();//回溯</span><br><span class="line"> sum -= i;//回溯</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="17-电话号码的字母组合"><a href="#17-电话号码的字母组合" class="headerlink" title="17. 电话号码的字母组合"></a><a href="https://leetcode.cn/problems/letter-combinations-of-a-phone-number/">17. 电话号码的字母组合</a></h2><h3 id="思路:创建对应数字与字符串的索引对应,然后对每一个字符串进行回溯"><a href="#思路:创建对应数字与字符串的索引对应,然后对每一个字符串进行回溯" class="headerlink" title="思路:创建对应数字与字符串的索引对应,然后对每一个字符串进行回溯"></a>思路:创建对应数字与字符串的索引对应,然后对每一个字符串进行回溯</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.回溯;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Arrays;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class num017 {</span><br><span class="line"> ArrayList<String> result = new ArrayList<>();</span><br><span class="line"> StringBuilder temp = new StringBuilder();</span><br><span class="line"> public List<String> letterCombinations(String digits) {</span><br><span class="line"> //剪枝</span><br><span class="line"> if (digits == "" || digits.length() == 0){</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> //初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""</span><br><span class="line"> String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};</span><br><span class="line"> backTracking(digits, numString, 0);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> public void backTracking(String digits, String[] numString, int num){</span><br><span class="line"> if (num == digits.length()){</span><br><span class="line"> result.add(temp.toString());</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> //获取对应的字符串</span><br><span class="line"> String str = numString[digits.charAt(num) - '0'];</span><br><span class="line"> for (int i = 0; i < str.length(); i++) {//横向遍历</span><br><span class="line"> temp.append(str.charAt(i));</span><br><span class="line"> backTracking(digits, numString, num + 1);//递归纵向遍历</span><br><span class="line"> temp.deleteCharAt(temp.length() - 1);//回溯</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 回溯算法 </category>
</categories>
<tags>
<tag> 回溯算法 </tag>
</tags>
</entry>
<entry>
<title>多线程</title>
<link href="/2024/05/13/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
<url>/2024/05/13/%E5%A4%9A%E7%BA%BF%E7%A8%8B/</url>
<content type="html"><![CDATA[<h2 id="按序打印:LK1114"><a href="#按序打印:LK1114" class="headerlink" title="按序打印:LK1114"></a>按序打印:LK1114</h2><h3 id="思路:使用线程等待的方式实现执行屏障,使用释放线程等待的方式实现屏障消除"><a href="#思路:使用线程等待的方式实现执行屏障,使用释放线程等待的方式实现屏障消除" class="headerlink" title="思路:使用线程等待的方式实现执行屏障,使用释放线程等待的方式实现屏障消除"></a>思路:使用线程等待的方式实现执行屏障,使用释放线程等待的方式实现屏障消除</h3><img src="/2024/05/13/%E5%A4%9A%E7%BA%BF%E7%A8%8B/image-20240513103906660.png" alt="image-20240513103906660" style="zoom:200%;"><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.多线程;</span><br><span class="line"></span><br><span class="line">class Foo {</span><br><span class="line"></span><br><span class="line"> private boolean firstFinished;</span><br><span class="line"> private boolean secondFinished;</span><br><span class="line"> private Object lock = new Object();</span><br><span class="line"></span><br><span class="line"> public Foo() {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void first(Runnable printFirst) throws InterruptedException {</span><br><span class="line"></span><br><span class="line"> synchronized (lock) {</span><br><span class="line"> // printFirst.run() outputs "first". Do not change or remove this line.</span><br><span class="line"> printFirst.run();</span><br><span class="line"> firstFinished = true;</span><br><span class="line"> lock.notifyAll();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void second(Runnable printSecond) throws InterruptedException {</span><br><span class="line"></span><br><span class="line"> synchronized (lock) {</span><br><span class="line"> while (!firstFinished) {</span><br><span class="line"> lock.wait();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // printSecond.run() outputs "second". Do not change or remove this line.</span><br><span class="line"> printSecond.run();</span><br><span class="line"> secondFinished = true;</span><br><span class="line"> lock.notifyAll();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void third(Runnable printThird) throws InterruptedException {</span><br><span class="line"></span><br><span class="line"> synchronized (lock) {</span><br><span class="line"> while (!secondFinished) {</span><br><span class="line"> lock.wait();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // printThird.run() outputs "third". Do not change or remove this line.</span><br><span class="line"> printThird.run();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="交替打印-FooBar"><a href="#交替打印-FooBar" class="headerlink" title="交替打印 FooBar"></a><a href="https://leetcode.cn/problems/print-foobar-alternately/">交替打印 FooBar</a></h2><h3 id="思路:设置异步锁,保证两个线程是交替进行"><a href="#思路:设置异步锁,保证两个线程是交替进行" class="headerlink" title="思路:设置异步锁,保证两个线程是交替进行"></a>思路:设置异步锁,保证两个线程是交替进行</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.多线程;</span><br><span class="line"></span><br><span class="line">import java.util.concurrent.*;</span><br><span class="line">import java.util.concurrent.locks.Condition;</span><br><span class="line">import java.util.concurrent.locks.Lock;</span><br><span class="line">import java.util.concurrent.locks.ReentrantLock;</span><br><span class="line"></span><br><span class="line">//synchronized</span><br><span class="line">class FooBar1 {</span><br><span class="line"></span><br><span class="line"> private int n;</span><br><span class="line"></span><br><span class="line"> private final Object lock;</span><br><span class="line"></span><br><span class="line"> private int flag;</span><br><span class="line"></span><br><span class="line"> public FooBar1(int n) {</span><br><span class="line"> this.n = n;</span><br><span class="line"> this.flag = 0;</span><br><span class="line"> this.lock = new Object();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void foo(Runnable printFoo) throws InterruptedException {</span><br><span class="line"> for (int i = 0; i < n; i++) {</span><br><span class="line"> synchronized (lock) {</span><br><span class="line"> while (flag == 1) {</span><br><span class="line"> lock.wait();</span><br><span class="line"> }</span><br><span class="line"> printFoo.run();</span><br><span class="line"> flag = 1;</span><br><span class="line"> lock.notifyAll();</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"> public void bar(Runnable printBar) throws InterruptedException {</span><br><span class="line"> for (int i = 0; i < n; i++) {</span><br><span class="line"> synchronized (lock) {</span><br><span class="line"> while (flag == 0) {</span><br><span class="line"> lock.wait();</span><br><span class="line"> }</span><br><span class="line"> printBar.run();</span><br><span class="line"> flag = 0;</span><br><span class="line"> lock.notifyAll();</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></tbody></table></figure><h2 id="1116-打印零与奇偶数"><a href="#1116-打印零与奇偶数" class="headerlink" title="1116. 打印零与奇偶数"></a><a href="https://leetcode.cn/problems/print-zero-even-odd/">1116. 打印零与奇偶数</a></h2><h3 id="思路:每一轮迭代分为四步,-先0-奇数-再0-偶数-,用两个开关即可以控制它们的顺序。"><a href="#思路:每一轮迭代分为四步,-先0-奇数-再0-偶数-,用两个开关即可以控制它们的顺序。" class="headerlink" title="思路:每一轮迭代分为四步,[先0] [奇数] [再0] [偶数],用两个开关即可以控制它们的顺序。"></a>思路:每一轮迭代分为四步,[先0] [奇数] [再0] [偶数],用两个开关即可以控制它们的顺序。</h3><h3 id="具体的先后顺序控制方法与题1115-交替打印-FooBar是一样的。"><a href="#具体的先后顺序控制方法与题1115-交替打印-FooBar是一样的。" class="headerlink" title="具体的先后顺序控制方法与题1115. 交替打印 FooBar是一样的。"></a>具体的先后顺序控制方法与题1115. 交替打印 FooBar是一样的。</h3><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.多线程;</span><br><span class="line"></span><br><span class="line">import java.util.function.IntConsumer;</span><br><span class="line"></span><br><span class="line">class ZeroEvenOdd {</span><br><span class="line"> private int n;</span><br><span class="line"> private volatile boolean needZero;</span><br><span class="line"> private volatile boolean needOdd;</span><br><span class="line"></span><br><span class="line"> public ZeroEvenOdd(int n) {</span><br><span class="line"> this.n = n;</span><br><span class="line"> needZero = true;</span><br><span class="line"> needOdd = true;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // printNumber.accept(x) outputs "x", where x is an integer.</span><br><span class="line"> public void zero(IntConsumer printNumber) throws InterruptedException {</span><br><span class="line"> for (int i = 0; i < n; ) {</span><br><span class="line"> if (needZero) {</span><br><span class="line"> printNumber.accept(0);</span><br><span class="line"> i++;</span><br><span class="line"> needZero = false;</span><br><span class="line"> } else {</span><br><span class="line"> Thread.yield();</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"> public void even(IntConsumer printNumber) throws InterruptedException {</span><br><span class="line"> for (int i = 2; i <= n; ) {</span><br><span class="line"> if (!needZero && !needOdd) {</span><br><span class="line"> printNumber.accept(i);</span><br><span class="line"> i += 2;</span><br><span class="line"> needZero = true;</span><br><span class="line"> needOdd = true;</span><br><span class="line"> } else {</span><br><span class="line"> Thread.yield();</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"> public void odd(IntConsumer printNumber) throws InterruptedException {</span><br><span class="line"> for (int i = 1; i <= n; ) {</span><br><span class="line"> if (!needZero && needOdd) {</span><br><span class="line"> printNumber.accept(i);</span><br><span class="line"> i += 2;</span><br><span class="line"> needZero = true;</span><br><span class="line"> needOdd = false;</span><br><span class="line"> } else {</span><br><span class="line"> Thread.yield();</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></tbody></table></figure>]]></content>
<categories>
<category> 多线程 </category>
</categories>
<tags>
<tag> 多线程锁的机制 </tag>
</tags>
</entry>
<entry>
<title>day021</title>
<link href="/2024/05/13/day27/"/>
<url>/2024/05/13/day27/</url>
<content type="html"><![CDATA[<h2 id="组合:LK077"><a href="#组合:LK077" class="headerlink" title="组合:LK077"></a>组合:LK077</h2><h3 id="思路:"><a href="#思路:" class="headerlink" title="思路:"></a>思路:</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.回溯算法;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class LK077 {</span><br><span class="line"></span><br><span class="line"> List<List<Integer>> result = new ArrayList<>();</span><br><span class="line"> LinkedList<Integer> path = new LinkedList<>();</span><br><span class="line"> public List<List<Integer>> combine(int n, int k) {</span><br><span class="line"> combineHelper(n, k, 1);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * 每次从集合中选取元素,可选择的范围随着选择的进行而收缩,调整可选择的范围,就是要靠startIndex</span><br><span class="line"> * @param startIndex 用来记录本层递归的中,集合从哪里开始遍历(集合就是[1,...,n] )。</span><br><span class="line"> */</span><br><span class="line"> private void combineHelper(int n, int k, int startIndex){</span><br><span class="line"> //终止条件</span><br><span class="line"> if (path.size() == k){</span><br><span class="line"> result.add(new ArrayList<>(path));</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> for (int i = startIndex; i <= n - (k - path.size()) + 1; i++){//广度遍历,横向</span><br><span class="line"> path.add(i);</span><br><span class="line"> combineHelper(n, k, i + 1);//深度遍历,纵向</span><br><span class="line"> path.removeLast();//回溯</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 回溯算法 </category>
</categories>
<tags>
<tag> 回溯算法 </tag>
</tags>
</entry>
<entry>
<title>day019</title>
<link href="/2024/05/12/day24/"/>
<url>/2024/05/12/day24/</url>
<content type="html"><![CDATA[<h2 id="二叉搜索树最近公共祖先:LK235"><a href="#二叉搜索树最近公共祖先:LK235" class="headerlink" title="二叉搜索树最近公共祖先:LK235"></a>二叉搜索树最近公共祖先:LK235</h2><h3 id="思路:利用后序遍历由下向上遍历"><a href="#思路:利用后序遍历由下向上遍历" class="headerlink" title="思路:利用后序遍历由下向上遍历"></a>思路:利用后序遍历由下向上遍历</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">public class LK235 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {</span><br><span class="line"> if (root.val > p.val && root.val > q.val) return lowestCommonAncestor(root.left, p, q);</span><br><span class="line"> if (root.val < p.val && root.val < q.val) return lowestCommonAncestor(root.right, p, q);</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉搜索树中的插入操作:LK701"><a href="#二叉搜索树中的插入操作:LK701" class="headerlink" title="二叉搜索树中的插入操作:LK701"></a>二叉搜索树中的插入操作:LK701</h2><h3 id="思路:根据二叉搜索树的左边节点小于根节点,有边节点大于根节点的性质,分别向两边递归查找"><a href="#思路:根据二叉搜索树的左边节点小于根节点,有边节点大于根节点的性质,分别向两边递归查找" class="headerlink" title="思路:根据二叉搜索树的左边节点小于根节点,有边节点大于根节点的性质,分别向两边递归查找"></a>思路:根据二叉搜索树的左边节点小于根节点,有边节点大于根节点的性质,分别向两边递归查找</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK701 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode insertIntoBST(TreeNode root, int val) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return new TreeNode(val);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (root.val < val){</span><br><span class="line"> root.right = insertIntoBST(root.right, val);</span><br><span class="line"> //return right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (root.val > val){</span><br><span class="line"> root.left = insertIntoBST(root.left, val);</span><br><span class="line"> //return left;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">class Solution1 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode insertIntoBST(TreeNode root, int val) {</span><br><span class="line"> if (root == null) return new TreeNode(val);</span><br><span class="line"> TreeNode newRoot = root;</span><br><span class="line"> TreeNode pre = root;</span><br><span class="line"> while (root != null) {</span><br><span class="line"> pre = root;</span><br><span class="line"> if (root.val > val) {</span><br><span class="line"> root = root.left;</span><br><span class="line"> } else if (root.val < val) {</span><br><span class="line"> root = root.right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (pre.val > val) {</span><br><span class="line"> pre.left = new TreeNode(val);</span><br><span class="line"> } else {</span><br><span class="line"> pre.right = new TreeNode(val);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return newRoot;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="删除二叉搜索树中的节点:LK450"><a href="#删除二叉搜索树中的节点:LK450" class="headerlink" title="删除二叉搜索树中的节点:LK450"></a>删除二叉搜索树中的节点:LK450</h2><h3 id="思路:确定递归终止条件,然后做好删除节点后它的子节点的分配"><a href="#思路:确定递归终止条件,然后做好删除节点后它的子节点的分配" class="headerlink" title="思路:确定递归终止条件,然后做好删除节点后它的子节点的分配"></a>思路:确定递归终止条件,然后做好删除节点后它的子节点的分配</h3><img src="https://code-thinking.cdn.bcebos.com/gifs/450.%E5%88%A0%E9%99%A4%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.gif" alt="450.删除二叉搜索树中的节点" style="zoom:200%;"><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK450 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode deleteNode(TreeNode root, int key) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> if (root.val == key){</span><br><span class="line"> if (root.left == null){</span><br><span class="line"> return root.right;</span><br><span class="line"> }else if (root.right == null){</span><br><span class="line"> return root.left;</span><br><span class="line"> }</span><br><span class="line"> //其中的else操作左右都不为空的手法最为重要</span><br><span class="line"> else {</span><br><span class="line"> TreeNode cur = root.right;</span><br><span class="line"> while (cur.left != null){</span><br><span class="line"> cur = cur.left;</span><br><span class="line"> }</span><br><span class="line"> cur.left = root.left;</span><br><span class="line"> return root.right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> if (root.val > key){</span><br><span class="line"> root.left = deleteNode(root.left, key);</span><br><span class="line"> }</span><br><span class="line"> if (root.val < key){</span><br><span class="line"> root.right = deleteNode(root.right, key);</span><br><span class="line"> }</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> // 解法1(最好理解的版本)</span><br><span class="line"> class Solution {</span><br><span class="line"> public TreeNode deleteNode(TreeNode root, int key) {</span><br><span class="line"> if (root == null) return root;</span><br><span class="line"> if (root.val == key) {</span><br><span class="line"> if (root.left == null) {</span><br><span class="line"> return root.right;</span><br><span class="line"> } else if (root.right == null) {</span><br><span class="line"> return root.left;</span><br><span class="line"> } else {</span><br><span class="line"> TreeNode cur = root.right;</span><br><span class="line"> while (cur.left != null) {</span><br><span class="line"> cur = cur.left;</span><br><span class="line"> }</span><br><span class="line"> cur.left = root.left;</span><br><span class="line"> root = root.right;</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (root.val > key) root.left = deleteNode(root.left, key);</span><br><span class="line"> if (root.val < key) root.right = deleteNode(root.right, key);</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>day020</title>
<link href="/2024/05/12/day25/"/>
<url>/2024/05/12/day25/</url>
<content type="html"><![CDATA[<h2 id="修剪二叉搜索树:LK669"><a href="#修剪二叉搜索树:LK669" class="headerlink" title="修剪二叉搜索树:LK669"></a>修剪二叉搜索树:LK669</h2><h3 id="思路:和删除二叉搜索树差不多,只是多了一个范围"><a href="#思路:和删除二叉搜索树差不多,只是多了一个范围" class="headerlink" title="思路:和删除二叉搜索树差不多,只是多了一个范围"></a>思路:和删除二叉搜索树差不多,只是多了一个范围</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK669 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode trimBST(TreeNode root, int low, int high) {</span><br><span class="line"></span><br><span class="line"> if (root == null){</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (root.val < low){</span><br><span class="line"> TreeNode right = trimBST(root.right, low, high);//修剪完再返回</span><br><span class="line"> return right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (root.val > high){</span><br><span class="line"> TreeNode left = trimBST(root.left, low, high);//修剪完再返回</span><br><span class="line"> return left;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> root.left = trimBST(root.left, low, high);</span><br><span class="line"> root.right = trimBST(root.right, low, high);</span><br><span class="line"></span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> class Solution {</span><br><span class="line"> //iteration</span><br><span class="line"> public TreeNode trimBST(TreeNode root, int low, int high) {</span><br><span class="line"> if(root == null)</span><br><span class="line"> return null;</span><br><span class="line"> while(root != null && (root.val < low || root.val > high)){</span><br><span class="line"> if(root.val < low)</span><br><span class="line"> root = root.right;</span><br><span class="line"> else</span><br><span class="line"> root = root.left;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> TreeNode curr = root;</span><br><span class="line"></span><br><span class="line"> //deal with root's left sub-tree, and deal with the value smaller than low.</span><br><span class="line"> while(curr != null){</span><br><span class="line"> while(curr.left != null && curr.left.val < low){</span><br><span class="line"> curr.left = curr.left.right;</span><br><span class="line"> }</span><br><span class="line"> curr = curr.left;</span><br><span class="line"> }</span><br><span class="line"> //go back to root;</span><br><span class="line"> curr = root;</span><br><span class="line"></span><br><span class="line"> //deal with root's righg sub-tree, and deal with the value bigger than high.</span><br><span class="line"> while(curr != null){</span><br><span class="line"> while(curr.right != null && curr.right.val > high){</span><br><span class="line"> curr.right = curr.right.left;</span><br><span class="line"> }</span><br><span class="line"> curr = curr.right;</span><br><span class="line"> }</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="将有序数组转换为二叉搜索树:LK108"><a href="#将有序数组转换为二叉搜索树:LK108" class="headerlink" title="将有序数组转换为二叉搜索树:LK108"></a>将有序数组转换为二叉搜索树:LK108</h2><h3 id="思路:寻找分割点,分割点作为当前节点,然后递归左区间和右区间。"><a href="#思路:寻找分割点,分割点作为当前节点,然后递归左区间和右区间。" class="headerlink" title="思路:寻找分割点,分割点作为当前节点,然后递归左区间和右区间。"></a>思路:<strong>寻找分割点,分割点作为当前节点,然后递归左区间和右区间</strong>。</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK108 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val, TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode sortedArrayToBST(int[] nums) {</span><br><span class="line"> if (nums.length == 0) {</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> //根节点初始化</span><br><span class="line"> TreeNode root = new TreeNode(-1);</span><br><span class="line"> Queue<TreeNode> nodeQueue = new LinkedList<>();</span><br><span class="line"> Queue<Integer> leftQueue = new LinkedList<>();</span><br><span class="line"> Queue<Integer> rightQueue = new LinkedList<>();</span><br><span class="line"></span><br><span class="line"> // 根节点入队列</span><br><span class="line"> nodeQueue.offer(root);</span><br><span class="line"> // 0为左区间下标初始位置</span><br><span class="line"> leftQueue.offer(0);</span><br><span class="line"> // nums.size() - 1为右区间下标初始位置</span><br><span class="line"> rightQueue.offer(nums.length - 1);</span><br><span class="line"></span><br><span class="line"> while (!nodeQueue.isEmpty()) {</span><br><span class="line"> TreeNode currNode = nodeQueue.poll();</span><br><span class="line"> int left = leftQueue.poll();</span><br><span class="line"> int right = rightQueue.poll();</span><br><span class="line"> int mid = left + ((right - left) >> 1);</span><br><span class="line"></span><br><span class="line"> // 将mid对应的元素给中间节点</span><br><span class="line"> currNode.val = nums[mid];</span><br><span class="line"></span><br><span class="line"> // 处理左区间</span><br><span class="line"> if (left <= mid - 1) {</span><br><span class="line"> currNode.left = new TreeNode(-1);</span><br><span class="line"> nodeQueue.offer(currNode.left);</span><br><span class="line"> leftQueue.offer(left);</span><br><span class="line"> rightQueue.offer(mid - 1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 处理右区间</span><br><span class="line"> if (right >= mid + 1) {</span><br><span class="line"> currNode.right = new TreeNode(-1);</span><br><span class="line"> nodeQueue.offer(currNode.right);</span><br><span class="line"> leftQueue.offer(mid + 1);</span><br><span class="line"> rightQueue.offer(right);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法代码:"><a href="#其他方法代码:" class="headerlink" title="其他方法代码:"></a>其他方法代码:</h3><figure class="highlight java"><table><tbody><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">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> TreeNode <span class="title function_">sortedArrayToBST</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> <span class="keyword">return</span> sortedArrayToBST(nums, <span class="number">0</span>, nums.length);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> TreeNode <span class="title function_">sortedArrayToBST</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> left, <span class="type">int</span> right)</span> {</span><br><span class="line"> <span class="keyword">if</span> (left >= right) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (right - left == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(nums[left]);</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> left + (right - left) / <span class="number">2</span>;</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">root</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(nums[mid]);</span><br><span class="line"> root.left = sortedArrayToBST(nums, left, mid);</span><br><span class="line"> root.right = sortedArrayToBST(nums, mid + <span class="number">1</span>, right);</span><br><span class="line"> <span class="keyword">return</span> root;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="把二叉搜索树转换为累加树:LK538"><a href="#把二叉搜索树转换为累加树:LK538" class="headerlink" title="把二叉搜索树转换为累加树:LK538"></a>把二叉搜索树转换为累加树:LK538</h2><h3 id="思路:按右中左顺序遍历累加即可"><a href="#思路:按右中左顺序遍历累加即可" class="headerlink" title="思路:按右中左顺序遍历累加即可"></a>思路:按右中左顺序遍历累加即可</h3><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class LK538 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> int sum;</span><br><span class="line"> public TreeNode convertBST(TreeNode root) {</span><br><span class="line"></span><br><span class="line"> sum = 0;</span><br><span class="line"> convertBST1(root);</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> // 按右中左顺序遍历,累加即可</span><br><span class="line"> public void convertBST1(TreeNode root) {</span><br><span class="line"> if (root == null) {</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> convertBST1(root.right);</span><br><span class="line"> sum += root.val;</span><br><span class="line"> root.val = sum;</span><br><span class="line"> convertBST1(root.left);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>day018</title>
<link href="/2024/05/09/day23/"/>
<url>/2024/05/09/day23/</url>
<content type="html"><![CDATA[<h2 id="二叉树搜索树的最小绝对差:LK530"><a href="#二叉树搜索树的最小绝对差:LK530" class="headerlink" title="二叉树搜索树的最小绝对差:LK530"></a>二叉树搜索树的最小绝对差:LK530</h2><h3 id="思路:运用递归或者迭代遍历记录前后两个节点的最小差值"><a href="#思路:运用递归或者迭代遍历记录前后两个节点的最小差值" class="headerlink" title="思路:运用递归或者迭代遍历记录前后两个节点的最小差值"></a>思路:运用递归或者迭代遍历记录前后两个节点的最小差值</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.*;</span><br><span class="line"></span><br><span class="line">public class LK530 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public int getMinimumDifference(TreeNode root) {</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> TreeNode pre = null;</span><br><span class="line"> int result = Integer.MAX_VALUE;</span><br><span class="line"></span><br><span class="line"> if(root != null)</span><br><span class="line"> stack.add(root);</span><br><span class="line"> while(!stack.isEmpty()){</span><br><span class="line"> TreeNode curr = stack.peek();</span><br><span class="line"> if(curr != null){</span><br><span class="line"> stack.pop();</span><br><span class="line"> if(curr.right != null)</span><br><span class="line"> stack.add(curr.right);</span><br><span class="line"> stack.add(curr);</span><br><span class="line"> stack.add(null);//为后面的条件计算服务pre</span><br><span class="line"> if(curr.left != null)</span><br><span class="line"> stack.add(curr.left);</span><br><span class="line"> }else{</span><br><span class="line"> stack.pop();</span><br><span class="line"> TreeNode temp = stack.pop();</span><br><span class="line"> if(pre != null)</span><br><span class="line"> result = Math.min(result, temp.val - pre.val);</span><br><span class="line"> pre = temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> TreeNode pre;// 记录上一个遍历的结点</span><br><span class="line"> int result = Integer.MAX_VALUE;</span><br><span class="line"> public int getMinimumDifference2(TreeNode root) {</span><br><span class="line"> if(root==null)return 0;</span><br><span class="line"> traversal(root);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> public void traversal(TreeNode root){</span><br><span class="line"> if(root==null)return;</span><br><span class="line"> //左</span><br><span class="line"> traversal(root.left);</span><br><span class="line"> //中</span><br><span class="line"> if(pre!=null){</span><br><span class="line"> result = Math.min(result,root.val-pre.val);</span><br><span class="line"> }</span><br><span class="line"> pre = root;</span><br><span class="line"> //右</span><br><span class="line"> traversal(root.right);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉搜索树中的众数:LK501"><a href="#二叉搜索树中的众数:LK501" class="headerlink" title="二叉搜索树中的众数:LK501"></a>二叉搜索树中的众数:LK501</h2><h3 id="思路:遍历二叉树记录其中的众数"><a href="#思路:遍历二叉树记录其中的众数" class="headerlink" title="思路:遍历二叉树记录其中的众数"></a>思路:遍历二叉树记录其中的众数</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line"></span><br><span class="line">public class LK501 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ArrayList<Integer> resList;</span><br><span class="line"> int maxCount;</span><br><span class="line"> int count;</span><br><span class="line"> TreeNode pre;</span><br><span class="line"> public int[] findMode(TreeNode root) {</span><br><span class="line"> resList = new ArrayList<>();</span><br><span class="line"> maxCount = 0;</span><br><span class="line"> count = 0;</span><br><span class="line"> pre = null;</span><br><span class="line"> findMode1(root);</span><br><span class="line"> int[] res = new int[resList.size()];</span><br><span class="line"> for (int i = 0; i < resList.size(); i++) {</span><br><span class="line"> res[i] = resList.get(i);</span><br><span class="line"> }</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void findMode1(TreeNode root) {</span><br><span class="line"> if (root == null) {</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> findMode1(root.left);</span><br><span class="line"></span><br><span class="line"> int rootValue = root.val;</span><br><span class="line"> // 计数</span><br><span class="line"> if (pre == null || rootValue != pre.val) {</span><br><span class="line"> count = 1;</span><br><span class="line"> } else {</span><br><span class="line"> count++;</span><br><span class="line"> }</span><br><span class="line"> // 更新结果以及maxCount</span><br><span class="line"> if (count > maxCount) {</span><br><span class="line"> resList.clear();</span><br><span class="line"> resList.add(rootValue);</span><br><span class="line"> maxCount = count;</span><br><span class="line"> } else if (count == maxCount) {</span><br><span class="line"> resList.add(rootValue);</span><br><span class="line"> }</span><br><span class="line"> pre = root;</span><br><span class="line"></span><br><span class="line"> findMode1(root.right);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉树的最近公共祖先:LK236"><a href="#二叉树的最近公共祖先:LK236" class="headerlink" title="二叉树的最近公共祖先:LK236"></a>二叉树的最近公共祖先:LK236</h2><h3 id="思路:用二叉树的后序遍历,再由回溯算法由下向上搜索"><a href="#思路:用二叉树的后序遍历,再由回溯算法由下向上搜索" class="headerlink" title="思路:用二叉树的后序遍历,再由回溯算法由下向上搜索"></a>思路:用二叉树的后序遍历,再由回溯算法由下向上搜索</h3><img src="/2024/05/09/day23/image-20240509190750075.png" alt="image-20240509190750075" style="zoom:200%;"><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK236 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> //后序遍历,利用它的回溯法</span><br><span class="line"> public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {</span><br><span class="line"> if (root == null || root == p || root == q){</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> TreeNode left = lowestCommonAncestor(root.left, p, q);</span><br><span class="line"> TreeNode right = lowestCommonAncestor(root.right, p, q);</span><br><span class="line"></span><br><span class="line"> if (left == null && right == null){</span><br><span class="line"> return null;</span><br><span class="line"> } else if (left == null && right != null) {</span><br><span class="line"> return right;</span><br><span class="line"> } else if (left != null && right == null) {</span><br><span class="line"> return left;</span><br><span class="line"> }else {</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
<tag> 回溯算法 </tag>
</tags>
</entry>
<entry>
<title>day017</title>
<link href="/2024/05/08/day22/"/>
<url>/2024/05/08/day22/</url>
<content type="html"><![CDATA[<h2 id="最大二叉树:LK654"><a href="#最大二叉树:LK654" class="headerlink" title="最大二叉树:LK654"></a>最大二叉树:LK654</h2><h3 id="思路:找到最大值然后划分数组递归划分创建节点就行"><a href="#思路:找到最大值然后划分数组递归划分创建节点就行" class="headerlink" title="思路:找到最大值然后划分数组递归划分创建节点就行"></a>思路:找到最大值然后划分数组递归划分创建节点就行</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.Arrays;</span><br><span class="line"></span><br><span class="line">public class LK654 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode constructMaximumBinaryTree(int[] nums) {</span><br><span class="line"> return buildHelper(nums, 0, nums.length);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode buildHelper(int[] nums, int leftIndex, int rightIndex){</span><br><span class="line"> if (rightIndex - leftIndex <1){</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> if (rightIndex - leftIndex == 1){</span><br><span class="line"> return new TreeNode(nums[leftIndex]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //找到最大元素分割数组</span><br><span class="line"> int maxIndex = leftIndex;// 最大值所在位置</span><br><span class="line"> int maxValue = nums[maxIndex];// 最大值</span><br><span class="line"> for (int i = leftIndex + 1; i < rightIndex; i++) {</span><br><span class="line"> if (nums[i] > maxValue){</span><br><span class="line"> maxValue = nums[i];</span><br><span class="line"> maxIndex = 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"> TreeNode root = new TreeNode(maxValue);</span><br><span class="line"></span><br><span class="line"> //向左递归构造左子树</span><br><span class="line"> root.left = buildHelper(nums, leftIndex, maxIndex);</span><br><span class="line"></span><br><span class="line"> //向右递归构造右子树</span><br><span class="line"> root.right = buildHelper(nums, maxIndex + 1, rightIndex);</span><br><span class="line"></span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="合并二叉树:LK617"><a href="#合并二叉树:LK617" class="headerlink" title="合并二叉树:LK617"></a>合并二叉树:LK617</h2><h3 id="思路:递归将两棵树的相同位置的节点值相加构建新节点"><a href="#思路:递归将两棵树的相同位置的节点值相加构建新节点" class="headerlink" title="思路:递归将两棵树的相同位置的节点值相加构建新节点"></a>思路:递归将两棵树的相同位置的节点值相加构建新节点</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK617 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {</span><br><span class="line"> return merge(root1, root2);</span><br><span class="line"> }</span><br><span class="line"> public TreeNode merge(TreeNode left, TreeNode right){</span><br><span class="line"> if (left == null && right == null){</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> if (left == null){</span><br><span class="line"> return right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (right == null){</span><br><span class="line"> return left;</span><br><span class="line"> }</span><br><span class="line"> TreeNode root = new TreeNode(left.val + right.val);</span><br><span class="line"> root.left = merge(left.left , right.left);</span><br><span class="line"> root.right = merge(left.right, right.right);</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉搜索树中的搜索:LK700"><a href="#二叉搜索树中的搜索:LK700" class="headerlink" title="二叉搜索树中的搜索:LK700"></a>二叉搜索树中的搜索:LK700</h2><h3 id="思路:使用遍历进行值的匹配就行,相等就返回节点"><a href="#思路:使用遍历进行值的匹配就行,相等就返回节点" class="headerlink" title="思路:使用遍历进行值的匹配就行,相等就返回节点"></a>思路:使用遍历进行值的匹配就行,相等就返回节点</h3><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class Lk700 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode searchBST(TreeNode root, int val) {</span><br><span class="line"> if (root == null || root.val == val) {</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> Queue<TreeNode> queue = new LinkedList<>();</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> while (!queue.isEmpty()) {</span><br><span class="line"> TreeNode node = queue.poll();</span><br><span class="line"> if (node.val == val) {</span><br><span class="line"> return node;</span><br><span class="line"> }</span><br><span class="line"> if (node.right != null) {</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> }</span><br><span class="line"> if (node.left != null) {</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="验证二叉搜索树:LK98"><a href="#验证二叉搜索树:LK98" class="headerlink" title="验证二叉搜索树:LK98"></a>验证二叉搜索树:LK98</h2><h3 id="思路:验证每个节点左边节点的值小于该节点,该节点的值小于右边节点的值"><a href="#思路:验证每个节点左边节点的值小于该节点,该节点的值小于右边节点的值" class="headerlink" title="思路:验证每个节点左边节点的值小于该节点,该节点的值小于右边节点的值"></a>思路:验证每个节点左边节点的值小于该节点,该节点的值小于右边节点的值</h3><h3 id="代码:-3"><a href="#代码:-3" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class LK098 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> // 递归</span><br><span class="line"> TreeNode max;</span><br><span class="line"> public boolean isValidBST(TreeNode root) {</span><br><span class="line"> if (root == null) {</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> // 左</span><br><span class="line"> boolean left = isValidBST(root.left);</span><br><span class="line"> if (!left) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> // 中if (max != null && root.val <= max.val) { return false; }: 如果当前节点的值小于等于已经遍历过的最大值 max 的值,</span><br><span class="line"> // 则不满足 BST 的条件,直接返回 false</span><br><span class="line"> //max = root;: 更新 max 为当前节点,因为当前节点符合 BST 的条件。</span><br><span class="line"> if (max != null && root.val <= max.val) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> max = root;//记录前一个节点</span><br><span class="line"> // 右</span><br><span class="line"> boolean right = isValidBST(root.right);</span><br><span class="line"> return right;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">//使用統一迭代法</span><br><span class="line">class Solution {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public boolean isValidBST(TreeNode root) {</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> TreeNode pre = null;</span><br><span class="line"> if(root != null)</span><br><span class="line"> stack.add(root);</span><br><span class="line"> while(!stack.isEmpty()){</span><br><span class="line"> TreeNode curr = stack.peek();</span><br><span class="line"> if(curr != null){</span><br><span class="line"> stack.pop();</span><br><span class="line"> if(curr.right != null)</span><br><span class="line"> stack.add(curr.right);</span><br><span class="line"> stack.add(curr);</span><br><span class="line"> stack.add(null);</span><br><span class="line"> if(curr.left != null)</span><br><span class="line"> stack.add(curr.left);</span><br><span class="line"> }else{</span><br><span class="line"> stack.pop();</span><br><span class="line"> TreeNode temp = stack.pop();</span><br><span class="line"> if(pre != null && pre.val >= temp.val)</span><br><span class="line"> return false;</span><br><span class="line"> pre = temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法代码:"><a href="#其他方法代码:" class="headerlink" title="其他方法代码:"></a>其他方法代码:</h3><figure class="highlight java"><table><tbody><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">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isValidBST</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> Stack<TreeNode> stack = <span class="keyword">new</span> <span class="title class_">Stack</span><>();</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">pre</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">if</span>(root != <span class="literal">null</span>)</span><br><span class="line"> stack.add(root); </span><br><span class="line"> <span class="keyword">while</span>(!stack.isEmpty()){</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">curr</span> <span class="operator">=</span> stack.peek();</span><br><span class="line"> <span class="keyword">if</span>(curr != <span class="literal">null</span>){</span><br><span class="line"> stack.pop();</span><br><span class="line"> <span class="keyword">if</span>(curr.right != <span class="literal">null</span>)</span><br><span class="line"> stack.add(curr.right);</span><br><span class="line"> stack.add(curr);</span><br><span class="line"> stack.add(<span class="literal">null</span>);</span><br><span class="line"> <span class="keyword">if</span>(curr.left != <span class="literal">null</span>)</span><br><span class="line"> stack.add(curr.left);</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> stack.pop();</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">temp</span> <span class="operator">=</span> stack.pop();</span><br><span class="line"> <span class="keyword">if</span>(pre != <span class="literal">null</span> && pre.val >= temp.val)</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> pre = temp;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>day016</title>
<link href="/2024/05/07/day21/"/>
<url>/2024/05/07/day21/</url>
<content type="html"><![CDATA[<h2 id="找树左下角的值:LK513"><a href="#找树左下角的值:LK513" class="headerlink" title="找树左下角的值:LK513"></a>找树左下角的值:LK513</h2><h3 id="思路:记录每一个做子叶节点的值,最后一个就是左下角的值"><a href="#思路:记录每一个做子叶节点的值,最后一个就是左下角的值" class="headerlink" title="思路:记录每一个做子叶节点的值,最后一个就是左下角的值"></a>思路:记录每一个做子叶节点的值,最后一个就是左下角的值</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK513 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public int findBottomLeftValue(TreeNode root) {</span><br><span class="line"> Queue<TreeNode> queue = new LinkedList<>();</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> int res = 0;</span><br><span class="line"> while (!queue.isEmpty()) {</span><br><span class="line"> int size = queue.size();</span><br><span class="line"> for (int i = 0; i < size; i++) {</span><br><span class="line"> TreeNode poll = queue.poll();</span><br><span class="line"> if (i == 0) {</span><br><span class="line"> res = poll.val;</span><br><span class="line"> }</span><br><span class="line"> if (poll.left != null) {</span><br><span class="line"> queue.offer(poll.left);</span><br><span class="line"> }</span><br><span class="line"> if (poll.right != null) {</span><br><span class="line"> queue.offer(poll.right);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="路径总和:LK112"><a href="#路径总和:LK112" class="headerlink" title="路径总和:LK112"></a>路径总和:LK112</h2><h3 id="思路:递归遍历每一条路径,并计算路径上的节点的值总和,等于sum-就返回-true"><a href="#思路:递归遍历每一条路径,并计算路径上的节点的值总和,等于sum-就返回-true" class="headerlink" title="思路:递归遍历每一条路径,并计算路径上的节点的值总和,等于sum 就返回 true"></a>思路:递归遍历每一条路径,并计算路径上的节点的值总和,等于sum 就返回 true</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK112 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public boolean hasPathSum(TreeNode root, int targetSum) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> targetSum -= root.val;</span><br><span class="line"> if (root.left ==null && root.right == null){</span><br><span class="line"> return targetSum == 0;</span><br><span class="line"> }</span><br><span class="line"> if (root.left != null){</span><br><span class="line"> boolean left = hasPathSum(root.left, targetSum);</span><br><span class="line"> if (left == true){</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (root.right != null){</span><br><span class="line"> boolean right = hasPathSum(root.right, targetSum);</span><br><span class="line"> if (right == true){</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="从中序与后序遍历序列构造二叉树:LK106"><a href="#从中序与后序遍历序列构造二叉树:LK106" class="headerlink" title="从中序与后序遍历序列构造二叉树:LK106"></a>从中序与后序遍历序列构造二叉树:LK106</h1><h3 id="思路:后序遍历确定根节点"><a href="#思路:后序遍历确定根节点" class="headerlink" title="思路:后序遍历确定根节点"></a>思路:后序遍历确定根节点</h3><h3 id="根据根节点划分中序数组的左右子树"><a href="#根据根节点划分中序数组的左右子树" class="headerlink" title="根据根节点划分中序数组的左右子树"></a>根据根节点划分中序数组的左右子树</h3><img src="/2024/05/07/day21/image-20240507162748144.png" alt="image-20240507162748144" style="zoom: 200%;"><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class LK106 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode buildTree(int[] inorder, int[] postorder) {</span><br><span class="line"> if (inorder.length == 0 || postorder.length == 0){</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> return buildHelper(inorder, 0, inorder.length, postorder, 0, postorder.length);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode buildHelper(int[] inorder, int inorderStart, int inorderEnd,</span><br><span class="line"> int[] postorder, int postorderStart, int postorderEnd){</span><br><span class="line"> if (postorderStart == postorderEnd){return null;}</span><br><span class="line"> //找到后序数组的最后一个数确定根元素</span><br><span class="line"> int rootVal = postorder[postorderEnd - 1];</span><br><span class="line"> //构造根节点</span><br><span class="line"> TreeNode root = new TreeNode(rootVal);</span><br><span class="line"> //根据根节点划分中序数组的左右子数组</span><br><span class="line"> int middleIndex;</span><br><span class="line"> for (middleIndex = inorderStart; middleIndex < inorderEnd; middleIndex ++){</span><br><span class="line"> if (inorder[middleIndex] == rootVal){break;}</span><br><span class="line"> }</span><br><span class="line"> //中序数组划分</span><br><span class="line"> int leftInorderStart = inorderStart;//</span><br><span class="line"> int leftInorderEnd = middleIndex;//</span><br><span class="line"></span><br><span class="line"> int rightInorderStart = middleIndex + 1;//</span><br><span class="line"> int rightInorderEnd = inorderEnd;</span><br><span class="line"> //后序数组划分</span><br><span class="line"> int leftPostorderStart = postorderStart;</span><br><span class="line"> int leftPostorderEnd = postorderStart + (middleIndex - inorderStart);//important</span><br><span class="line"> int rightPostorderStart = leftPostorderEnd;</span><br><span class="line"> int rightPostorderEnd = postorderEnd - 1;//除去根节点</span><br><span class="line"></span><br><span class="line"> root.left = buildHelper(inorder, leftInorderStart, leftInorderEnd, postorder, leftPostorderStart, leftPostorderEnd);</span><br><span class="line"> root.right = buildHelper(inorder, rightInorderStart, rightInorderEnd, postorder, rightPostorderStart, rightPostorderEnd);</span><br><span class="line"></span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>卖股票的最佳时机</title>
<link href="/2024/05/06/%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA/"/>
<url>/2024/05/06/%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA/</url>
<content type="html"><![CDATA[<h2 id="卖股票的最佳时机1:LK121"><a href="#卖股票的最佳时机1:LK121" class="headerlink" title="卖股票的最佳时机1:LK121"></a>卖股票的最佳时机1:LK121</h2><h3 id="思路:"><a href="#思路:" class="headerlink" title="思路:"></a>思路:</h3><h3 id="dp-i-0-代表第i天持有股票的最大收益;"><a href="#dp-i-0-代表第i天持有股票的最大收益;" class="headerlink" title="dp[i][0]代表第i天持有股票的最大收益;"></a>dp[i][0]代表第i天持有股票的最大收益;</h3><h3 id="dp-i-1-代表第i天不持有股票的最大收益"><a href="#dp-i-1-代表第i天不持有股票的最大收益" class="headerlink" title="dp[i][1]代表第i天不持有股票的最大收益"></a>dp[i][1]代表第i天不持有股票的最大收益</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK121 {</span><br><span class="line"> public int maxProfit(int[] prices) {</span><br><span class="line"> if (prices == null || prices.length == 0) return 0;</span><br><span class="line"> int length = prices.length;</span><br><span class="line"> </span><br><span class="line"> int[][] dp = new int[length][2];</span><br><span class="line"> dp[0][0] = -prices[0];</span><br><span class="line"> dp[0][1] = 0;</span><br><span class="line"> for (int i = 1; i < length; i++) {</span><br><span class="line"> dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);</span><br><span class="line"> dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);</span><br><span class="line"> }</span><br><span class="line"> return dp[length - 1][1];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="卖股票的最佳时机2:LK122"><a href="#卖股票的最佳时机2:LK122" class="headerlink" title="卖股票的最佳时机2:LK122"></a>卖股票的最佳时机2:LK122</h2><h3 id="思路:-1"><a href="#思路:-1" class="headerlink" title="思路:"></a>思路:</h3><h3 id="dp-i-0-代表第i天持有股票的最大收益;-1"><a href="#dp-i-0-代表第i天持有股票的最大收益;-1" class="headerlink" title="dp[i][0]代表第i天持有股票的最大收益;"></a>dp[i][0]代表第i天持有股票的最大收益;</h3><h3 id="dp-i-1-代表第i天不持有股票的最大收益-1"><a href="#dp-i-1-代表第i天不持有股票的最大收益-1" class="headerlink" title="dp[i][1]代表第i天不持有股票的最大收益"></a>dp[i][1]代表第i天不持有股票的最大收益</h3><h3 id="如果第i天持有股票即dp-i-0-,-那么可以由两个状态推出来"><a href="#如果第i天持有股票即dp-i-0-,-那么可以由两个状态推出来" class="headerlink" title="如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来"></a>如果第i天持有股票即dp[i][0], 那么可以由两个状态推出来</h3><ul><li>第i-1天就持有股票,那么就保持现状,所得现金就是昨天持有股票的所得现金 即:dp[i - 1][0]</li><li>第i天买入股票,所得现金就是昨天不持有股票的所得现金减去 今天的股票价格 即:dp[i - 1][1] - prices[i]</li></ul><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK122 {</span><br><span class="line"> public int maxProfit(int[] prices) {</span><br><span class="line"> int n = prices.length;</span><br><span class="line"> int[][] dp = new int[n][2];</span><br><span class="line"> dp[0][0] = 0;</span><br><span class="line"> dp[0][1] = -prices[0];</span><br><span class="line"> for (int i = 1; i < n; ++i) {</span><br><span class="line"> dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);</span><br><span class="line"> dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);</span><br><span class="line"> }</span><br><span class="line"> return dp[n - 1][0];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="卖股票的最佳时机3:LK123"><a href="#卖股票的最佳时机3:LK123" class="headerlink" title="卖股票的最佳时机3:LK123"></a>卖股票的最佳时机3:LK123</h2><h3 id="思路:-2"><a href="#思路:-2" class="headerlink" title="思路:"></a>思路:</h3><ol><li>确定dp数组以及下标的含义</li></ol><p>一天一共就有五个状态,</p><ol><li>没有操作 (其实我们也可以不设置这个状态)</li><li>第一次持有股票</li><li>第一次不持有股票</li><li>第二次持有股票</li><li>第二次不持有股票</li></ol><p>dp[i][j]中 i表示第i天,j为 [0 - 4] 五个状态,dp[i][j]表示第i天状态j所剩最大现金。</p><img src="/2024/05/06/%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA/image-20240506222809881.png" alt="image-20240506222809881" style="zoom: 200%;"><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK123 {</span><br><span class="line"> public int maxProfit(int[] prices) {</span><br><span class="line"> int len = prices.length;</span><br><span class="line"> // 边界判断, 题目中 length >= 1, 所以可省去</span><br><span class="line"> if (prices.length == 0) return 0;</span><br><span class="line"></span><br><span class="line"> /*</span><br><span class="line"> * 定义 5 种状态:</span><br><span class="line"> * 0: 没有操作, 1: 第一次买入, 2: 第一次卖出, 3: 第二次买入, 4: 第二次卖出</span><br><span class="line"> */</span><br><span class="line"> int[][] dp = new int[len][5];</span><br><span class="line"> dp[0][0] = 0;</span><br><span class="line"> dp[0][1] = -prices[0];</span><br><span class="line"> dp[0][2] = 0;</span><br><span class="line"> // 初始化第二次买入的状态是确保 最后结果是最多两次买卖的最大利润</span><br><span class="line"> dp[0][3] = -prices[0];</span><br><span class="line">dp[0][4] = 0;</span><br><span class="line"> for (int i = 1; i < len; i++) {</span><br><span class="line"> dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);</span><br><span class="line"> dp[i][2] = Math.max(dp[i - 1][2], dp[i - 1][1] + prices[i]);</span><br><span class="line"> dp[i][3] = Math.max(dp[i - 1][3], dp[i - 1][2] - prices[i]);</span><br><span class="line"> dp[i][4] = Math.max(dp[i - 1][4], dp[i - 1][3] + prices[i]);</span><br><span class="line"> }</span><br><span class="line"> return dp[len - 1][4];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="卖股票的最佳时机4:LK188"><a href="#卖股票的最佳时机4:LK188" class="headerlink" title="卖股票的最佳时机4:LK188"></a>卖股票的最佳时机4:LK188</h2><h3 id="思路:和3差不多只是状态多了需要用循环来进行初始化"><a href="#思路:和3差不多只是状态多了需要用循环来进行初始化" class="headerlink" title="思路:和3差不多只是状态多了需要用循环来进行初始化"></a>思路:和3差不多只是状态多了需要用循环来进行初始化</h3><h3 id=""><a href="#" class="headerlink" title=""></a></h3><ol><li>确定dp数组以及下标的含义</li></ol><p>一天一共就有 2 * k + 1个状态,</p><ol><li><p>没有操作 (其实我们也可以不设置这个状态)</p></li><li><p>第一次持有股票</p></li><li><p>第一次不持有股票</p></li><li><p>第二次持有股票</p></li><li><p>第二次不持有股票</p><p>. </p><p>.</p><p>.</p><p>.</p><p>.</p><p>.</p></li></ol><h3 id="其中奇数为持有股票状态"><a href="#其中奇数为持有股票状态" class="headerlink" title="其中奇数为持有股票状态"></a>其中奇数为持有股票状态</h3><h3 id="偶数为卖出股票状态"><a href="#偶数为卖出股票状态" class="headerlink" title="偶数为卖出股票状态"></a>偶数为卖出股票状态</h3><h3 id="代码:-3"><a href="#代码:-3" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK188 {</span><br><span class="line"> public int maxProfit(int k, int[] prices) {</span><br><span class="line"> if (prices.length == 0) return 0;</span><br><span class="line"></span><br><span class="line"> // [天数][股票状态]</span><br><span class="line"> // 股票状态: 奇数表示第 k 次交易持有/买入, 偶数表示第 k 次交易不持有/卖出, 0 表示没有操作</span><br><span class="line"> int len = prices.length;</span><br><span class="line"> int[][] dp = new int[len][k*2 + 1];</span><br><span class="line"> dp[0][0] = 0; </span><br><span class="line"> // dp数组的初始化, 与版本一同理</span><br><span class="line"> for (int i = 1; i < k*2; i += 2) {</span><br><span class="line"> dp[0][i] = -prices[0];</span><br><span class="line"> dp[0][i + 1] = 0;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (int i = 1; i < len; i++) {</span><br><span class="line"> for (int j = 0; j < k*2 - 1; j += 2) {</span><br><span class="line"> dp[i][j + 1] = Math.max(dp[i - 1][j + 1], dp[i - 1][j] - prices[i]);</span><br><span class="line"> dp[i][j + 2] = Math.max(dp[i - 1][j + 2], dp[i - 1][j + 1] + prices[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return dp[len - 1][k*2];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="最佳买卖股票时机含冷冻期-LK309"><a href="#最佳买卖股票时机含冷冻期-LK309" class="headerlink" title="最佳买卖股票时机含冷冻期:LK309"></a>最佳买卖股票时机含冷冻期:LK309</h1><h3 id="思路:-3"><a href="#思路:-3" class="headerlink" title="思路:"></a>思路:</h3><img src="/2024/05/06/%E5%8D%96%E8%82%A1%E7%A5%A8%E7%9A%84%E6%9C%80%E4%BD%B3%E6%97%B6%E6%9C%BA/image-20240507103402392.png" alt="image-20240507103402392" style="zoom:150%;"><h3 id="代码:-4"><a href="#代码:-4" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK309 {</span><br><span class="line"> public int maxProfit(int[] prices) {</span><br><span class="line"> int[][] dp = new int[prices.length + 1][2];</span><br><span class="line"> dp[1][0] = -prices[0];</span><br><span class="line"></span><br><span class="line"> for (int i = 2; i <= prices.length; i++) {</span><br><span class="line"> /*</span><br><span class="line"> dp[i][0] 第i天持有股票收益;</span><br><span class="line"> dp[i][1] 第i天不持有股票收益;</span><br><span class="line"> 情况一:第i天是冷静期,不能以dp[i-1][1]购买股票,所以以dp[i - 2][1]买股票,没问题</span><br><span class="line"> 情况二:第i天不是冷静期,理论上应该以dp[i-1][1]购买股票,但是第i天不是冷静期说明,第i-1天没有卖出股票,</span><br><span class="line"> 则dp[i-1][1]=dp[i-2][1],所以可以用dp[i-2][1]买股票,没问题</span><br><span class="line"> */</span><br><span class="line"> dp[i][0] = Math.max(dp[i - 1][0], dp[i - 2][1] - prices[i - 1]);</span><br><span class="line"> dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i - 1]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return dp[prices.length][1];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 动态规划 </category>
</categories>
<tags>
<tag> 动态规划 </tag>
</tags>
</entry>
<entry>
<title>day014</title>
<link href="/2024/05/05/day19/"/>
<url>/2024/05/05/day19/</url>
<content type="html"><![CDATA[<h2 id="二叉树的最大深度:LK104"><a href="#二叉树的最大深度:LK104" class="headerlink" title="二叉树的最大深度:LK104"></a>二叉树的最大深度:LK104</h2><h3 id="思路:用层序遍历在每层记录层数就行"><a href="#思路:用层序遍历在每层记录层数就行" class="headerlink" title="思路:用层序遍历在每层记录层数就行"></a>思路:用层序遍历在每层记录层数就行</h3><img src="/2024/05/05/day19/image-20240505153756937.png" alt="image-20240505153756937" style="zoom:200%;"><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK104 {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public int maxDepth(TreeNode root) {</span><br><span class="line"> if (root == null) {</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br><span class="line"> Queue<TreeNode> queue = new LinkedList<>();</span><br><span class="line"> //队列先进先出</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> int maxDepth = 0;</span><br><span class="line"> while (!queue.isEmpty()) {</span><br><span class="line"> int currentLevelSize = queue.size();</span><br><span class="line"> while (currentLevelSize > 0){</span><br><span class="line"> TreeNode node = queue.poll();</span><br><span class="line"> if (node.left != null) {</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> }</span><br><span class="line"> if (node.right != null) {</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> }</span><br><span class="line"> currentLevelSize --;</span><br><span class="line"> }</span><br><span class="line"> maxDepth++;</span><br><span class="line"> }</span><br><span class="line"> return maxDepth;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉树的最小深度:LK111"><a href="#二叉树的最小深度:LK111" class="headerlink" title="二叉树的最小深度:LK111"></a>二叉树的最小深度:LK111</h2><h3 id="思路:运用层序遍历,当遇到节点的左右孩子节点为-null-的时候记录最小深度"><a href="#思路:运用层序遍历,当遇到节点的左右孩子节点为-null-的时候记录最小深度" class="headerlink" title="思路:运用层序遍历,当遇到节点的左右孩子节点为 null 的时候记录最小深度"></a>思路:运用层序遍历,当遇到节点的左右孩子节点为 null 的时候记录最小深度</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK111 {</span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public int minDepth(TreeNode root) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br><span class="line"> int depth = 0;</span><br><span class="line"> LinkedList<TreeNode> queue = new LinkedList<TreeNode>();</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> while (! queue.isEmpty()){</span><br><span class="line"> int size = queue.size();</span><br><span class="line"> depth ++;</span><br><span class="line"> for (int i = 0; i < size; i++) {</span><br><span class="line"> TreeNode cur = queue.poll();</span><br><span class="line"> if (cur.left == null && cur.right == null){</span><br><span class="line"> return depth;</span><br><span class="line"> }</span><br><span class="line"> if (cur.right != null){</span><br><span class="line"> queue.offer(cur.right);</span><br><span class="line"> }</span><br><span class="line"> if (cur.left != null){</span><br><span class="line"> queue.offer(cur.left);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return depth;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="完全二叉树的节点个数:LK222"><a href="#完全二叉树的节点个数:LK222" class="headerlink" title="完全二叉树的节点个数:LK222"></a>完全二叉树的节点个数:LK222</h2><h3 id="思路:计算左边和右边的节点数最后加上根节点"><a href="#思路:计算左边和右边的节点数最后加上根节点" class="headerlink" title="思路:计算左边和右边的节点数最后加上根节点"></a>思路:计算左边和右边的节点数最后加上根节点</h3><img src="/2024/05/05/day19/image-20240505154740562.png" alt="image-20240505154740562" style="zoom:200%;"><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK222 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public int countNodes(TreeNode root) {</span><br><span class="line"> if(root == null) {</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br><span class="line"> return countNodes(root.left) + countNodes(root.right) + 1;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>day015</title>
<link href="/2024/05/05/day20/"/>
<url>/2024/05/05/day20/</url>
<content type="html"><![CDATA[<h2 id="平衡二叉树:LK110"><a href="#平衡二叉树:LK110" class="headerlink" title="平衡二叉树:LK110"></a>平衡二叉树:LK110</h2><h3 id="思路:用层序遍历,求取根节点的左右子树的层数差-小于等于1!"><a href="#思路:用层序遍历,求取根节点的左右子树的层数差-小于等于1!" class="headerlink" title="思路:用层序遍历,求取根节点的左右子树的层数差 小于等于1!"></a>思路:用层序遍历,求取根节点的左右子树的层数差 小于等于1!</h3><img src="/2024/05/05/day20/image-20240506170208247.png" alt="image-20240506170208247" style="zoom:200%;"><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK110 {</span><br><span class="line"> class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public boolean isBalanced(TreeNode root) {</span><br><span class="line"> int gap = max(root) - min(root);</span><br><span class="line"> return gap == 0 || gap == 1;</span><br><span class="line"> }</span><br><span class="line"> public int min(TreeNode root) {</span><br><span class="line"> Queue<TreeNode> queue = new LinkedList<>();</span><br><span class="line"> if (root == null){</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br><span class="line"> // int maxDepth = 0;</span><br><span class="line"> int minDepth = 1;</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> while ( ! queue.isEmpty()){</span><br><span class="line"> int size = queue.size();</span><br><span class="line"> for (int i = 0; i < size; i++) {</span><br><span class="line"> //maxDepth ++;</span><br><span class="line"> TreeNode node = queue.poll();</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> }</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> }</span><br><span class="line"> if (node.left == null && node.right == null){</span><br><span class="line"> return minDepth;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return minDepth;</span><br><span class="line"> }</span><br><span class="line"> public int max(TreeNode root) {</span><br><span class="line"> Queue<TreeNode> queue = new LinkedList<>();</span><br><span class="line"> if (root == null){</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br><span class="line"> int maxDepth = 1;</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> while ( ! queue.isEmpty()){</span><br><span class="line"> int size = queue.size();</span><br><span class="line"> for (int i = 0; i < size; i++) {</span><br><span class="line"> maxDepth ++;</span><br><span class="line"> TreeNode node = queue.poll();</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> }</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return maxDepth;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法代码:"><a href="#其他方法代码:" class="headerlink" title="其他方法代码:"></a>其他方法代码:</h3><figure class="highlight java"><table><tbody><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">class</span> <span class="title class_">Solution</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="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isBalanced</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="keyword">return</span> getHeight(root) != -<span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="type">int</span> <span class="title function_">getHeight</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="keyword">if</span> (root == <span class="literal">null</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 class="type">int</span> <span class="variable">leftHeight</span> <span class="operator">=</span> getHeight(root.left);</span><br><span class="line"> <span class="keyword">if</span> (leftHeight == -<span class="number">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 class="type">int</span> <span class="variable">rightHeight</span> <span class="operator">=</span> getHeight(root.right);</span><br><span class="line"> <span class="keyword">if</span> (rightHeight == -<span class="number">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 class="comment">// 左右子树高度差大于1,return -1表示已经不是平衡树了</span></span><br><span class="line"> <span class="keyword">if</span> (Math.abs(leftHeight - rightHeight) > <span class="number">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 class="keyword">return</span> Math.max(leftHeight, rightHeight) + <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉树的所有路径:LK257"><a href="#二叉树的所有路径:LK257" class="headerlink" title="二叉树的所有路径:LK257"></a>二叉树的所有路径:LK257</h2><h3 id="思路:运用回溯算法暴力求解"><a href="#思路:运用回溯算法暴力求解" class="headerlink" title="思路:运用回溯算法暴力求解"></a>思路:运用回溯算法暴力求解</h3><img src="/2024/05/05/day20/image-20240506170937396.png" alt="image-20240506170937396" style="zoom:200%;"><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class LK257 { static class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int i) {</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">public static void main(String[] args) {</span><br><span class="line"> LK257 lk257 = new LK257();</span><br><span class="line"> TreeNode root = new TreeNode(1);</span><br><span class="line"> TreeNode node2 = new TreeNode(2);</span><br><span class="line"> TreeNode node3 = new TreeNode(3);</span><br><span class="line"> TreeNode node4 = new TreeNode(4);</span><br><span class="line"> TreeNode node5 = new TreeNode(5);</span><br><span class="line"></span><br><span class="line"> // 构建二叉树结构</span><br><span class="line"> root.left = node2;</span><br><span class="line"> root.right = node3;</span><br><span class="line"> node2.left = node4;</span><br><span class="line"> node2.right = node5;</span><br><span class="line"> lk257.binaryTreePaths(root);</span><br><span class="line">}</span><br><span class="line">public List<String> binaryTreePaths(TreeNode root) {</span><br><span class="line"> List<String> res = new ArrayList<>();// 存最终的结果</span><br><span class="line"> if (root == null) {</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line"> List<Integer> paths = new ArrayList<>();// 作为结果中的路径</span><br><span class="line"> traversal(root, paths, res);</span><br><span class="line"> return res;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"> private void traversal(TreeNode root, List<Integer> paths, List<String> res) {</span><br><span class="line"> paths.add(root.val);// 前序遍历,中</span><br><span class="line"> // 遇到叶子结点</span><br><span class="line"> if (root.left == null && root.right == null) {</span><br><span class="line"> // 输出</span><br><span class="line"> StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串,速度更快</span><br><span class="line"> for (int i = 0; i < paths.size() - 1; i++) {</span><br><span class="line"> sb.append(paths.get(i)).append("->");</span><br><span class="line"> System.out.println(res);</span><br><span class="line"> }</span><br><span class="line"> sb.append(paths.get(paths.size() - 1));// 记录最后一个节点</span><br><span class="line"> res.add(sb.toString());// 收集一个路径</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> // 递归和回溯是同时进行,所以要放在同一个花括号里</span><br><span class="line"> if (root.left != null) { // 左</span><br><span class="line"> traversal(root.left, paths, res);</span><br><span class="line"> paths.remove(paths.size() - 1);// 回溯</span><br><span class="line"> }</span><br><span class="line"> if (root.right != null) { // 右</span><br><span class="line"> traversal(root.right, paths, res);</span><br><span class="line"> paths.remove(paths.size() - 1);// 回溯</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法的代码:"><a href="#其他方法的代码:" class="headerlink" title="其他方法的代码:"></a>其他方法的代码:</h3><figure class="highlight java"><table><tbody><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="comment">// 解法二</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</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="keyword">public</span> List<String> <span class="title function_">binaryTreePaths</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> List<String> result = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="keyword">if</span> (root == <span class="literal">null</span>)</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> Stack<Object> stack = <span class="keyword">new</span> <span class="title class_">Stack</span><>();</span><br><span class="line"> <span class="comment">// 节点和路径同时入栈</span></span><br><span class="line"> stack.push(root);</span><br><span class="line"> stack.push(root.val + <span class="string">""</span>);</span><br><span class="line"> <span class="keyword">while</span> (!stack.isEmpty()) {</span><br><span class="line"> <span class="comment">// 节点和路径同时出栈</span></span><br><span class="line"> <span class="type">String</span> <span class="variable">path</span> <span class="operator">=</span> (String) stack.pop();</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> (TreeNode) stack.pop();</span><br><span class="line"> <span class="comment">// 若找到叶子节点</span></span><br><span class="line"> <span class="keyword">if</span> (node.left == <span class="literal">null</span> && node.right == <span class="literal">null</span>) {</span><br><span class="line"> result.add(path);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//右子节点不为空</span></span><br><span class="line"> <span class="keyword">if</span> (node.right != <span class="literal">null</span>) {</span><br><span class="line"> stack.push(node.right);</span><br><span class="line"> stack.push(path + <span class="string">"->"</span> + node.right.val);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//左子节点不为空</span></span><br><span class="line"> <span class="keyword">if</span> (node.left != <span class="literal">null</span>) {</span><br><span class="line"> stack.push(node.left);</span><br><span class="line"> stack.push(path + <span class="string">"->"</span> + node.left.val);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="左叶子之和:LK404"><a href="#左叶子之和:LK404" class="headerlink" title="左叶子之和:LK404"></a>左叶子之和:LK404</h2><h3 id="思路:运用层序遍历收集每一层符合左叶子条件的节点的值"><a href="#思路:运用层序遍历收集每一层符合左叶子条件的节点的值" class="headerlink" title="思路:运用层序遍历收集每一层符合左叶子条件的节点的值"></a>思路:运用层序遍历收集每一层符合左叶子条件的节点的值</h3><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.LinkedList;</span><br><span class="line">import java.util.Queue;</span><br><span class="line"></span><br><span class="line">public class LK404 {</span><br><span class="line"> static class TreeNode {</span><br><span class="line"> TreeNode left;</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode(TreeNode left, int val,TreeNode right) {</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> LK404 lk404 = new LK404();</span><br><span class="line"> TreeNode d = new TreeNode(null,12,null);</span><br><span class="line"> TreeNode b = new TreeNode(d,10,null);</span><br><span class="line"> TreeNode e = new TreeNode(null,10,null);</span><br><span class="line"> TreeNode c = new TreeNode(e,15,null);</span><br><span class="line"> TreeNode a = new TreeNode(b ,11,c);</span><br><span class="line"> int sum = lk404.sumOfLeftLeaves2(a);</span><br><span class="line"> System.out.println(sum);</span><br><span class="line"> }</span><br><span class="line"> public int sumOfLeftLeaves2(TreeNode root) {</span><br><span class="line"> if (root == null) return 0;</span><br><span class="line"> int leftValue = sumOfLeftLeaves(root.left); // 左</span><br><span class="line"> int rightValue = sumOfLeftLeaves(root.right); // 右</span><br><span class="line"></span><br><span class="line"> int midValue = 0;</span><br><span class="line"> if (root.left != null && root.left.left == null && root.left.right == null) {</span><br><span class="line"> midValue = root.left.val;</span><br><span class="line"> }</span><br><span class="line"> int sum = midValue + leftValue + rightValue; // 中</span><br><span class="line"> return sum;</span><br><span class="line"> }</span><br><span class="line"> public int sumOfLeftLeaves(TreeNode root) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return 0;</span><br><span class="line"> }</span><br><span class="line"> int result = sumOfLeftLeave(root);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> public int sumOfLeftLeave(TreeNode root){</span><br><span class="line"> Queue<TreeNode> queue = new LinkedList<>();</span><br><span class="line"> // ArrayList<Integer> result = new ArrayList<>();</span><br><span class="line"> int sum = 0;</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> while (! queue.isEmpty()){</span><br><span class="line"> int size = queue.size();</span><br><span class="line"> for (int i = 0; i < size; i++) {</span><br><span class="line"> TreeNode node = queue.poll();</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> //result.add(node.left.val);</span><br><span class="line"> if (node.left.left == null && node.left.right == null){</span><br><span class="line"> sum += node.left.val;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return sum;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
<tag> 回溯算法 </tag>
</tags>
</entry>
<entry>
<title>day012</title>
<link href="/2024/05/04/day15/"/>
<url>/2024/05/04/day15/</url>
<content type="html"><![CDATA[<h1 id="二叉树的统一迭代法:"><a href="#二叉树的统一迭代法:" class="headerlink" title="二叉树的统一迭代法:"></a>二叉树的统一迭代法:</h1><h2 id="思路:利用栈的先进后出规则,并添加-null-值进行遍历"><a href="#思路:利用栈的先进后出规则,并添加-null-值进行遍历" class="headerlink" title="思路:利用栈的先进后出规则,并添加 null 值进行遍历"></a>思路:利用栈的先进后出规则,并添加 null 值进行遍历</h2><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Collections;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class Tree1 {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</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"> public List<Integer> preorderTraversal(TreeNode root){</span><br><span class="line"> ArrayList<Integer> result = new ArrayList<>();</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> if (root != null){</span><br><span class="line"> stack.push(root);</span><br><span class="line"> }</span><br><span class="line"> while (!stack.isEmpty()){</span><br><span class="line"> TreeNode node = stack.peek();</span><br><span class="line"> //节点的入栈顺序和遍历的完全相反</span><br><span class="line"> if (node != null){</span><br><span class="line"> stack.pop();</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> stack.push(node.right);</span><br><span class="line"> }</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> stack.push(node.left);</span><br><span class="line"> }</span><br><span class="line"> stack.push(node);</span><br><span class="line"></span><br><span class="line"> stack.push(null);// 中节点访问过,但是还没有处理,加入空节点做为标记。</span><br><span class="line"> }else {</span><br><span class="line"> stack.pop();//将空节点弹出栈</span><br><span class="line"> node = stack.peek();//重新取出栈中元素</span><br><span class="line"> stack.pop();</span><br><span class="line"> result.add(node.val);//收集结果</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> // 中序遍历顺序: 左-中-右 入栈顺序: 右-中-左</span><br><span class="line"> public List<Integer> inorderTraversal(TreeNode root) {</span><br><span class="line"> List<Integer> result = new ArrayList<>();</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> if (root != null){</span><br><span class="line"> stack.push(root);</span><br><span class="line"> }</span><br><span class="line"> while (!stack.isEmpty()){</span><br><span class="line"> TreeNode node = stack.peek();</span><br><span class="line"> if (node != null){</span><br><span class="line"> stack.pop();</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> stack.push(node.right);</span><br><span class="line"> }</span><br><span class="line"> stack.push(node);</span><br><span class="line"> stack.push(null);</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> stack.push(node.left);</span><br><span class="line"> }</span><br><span class="line"> }else {</span><br><span class="line"> stack.pop();</span><br><span class="line"> node = stack.peek();</span><br><span class="line"> stack.pop();</span><br><span class="line"> result.add(node.val);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> // 后序遍历顺序 左-右-中 入栈顺序:中-右-左</span><br><span class="line"> public List<Integer> postorderTraversal(TreeNode root) {</span><br><span class="line"> List<Integer> result = new ArrayList<>();</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> if (root != null){</span><br><span class="line"> stack.push(root);</span><br><span class="line"> }</span><br><span class="line"> while (!stack.isEmpty()){</span><br><span class="line"> TreeNode node = stack.peek();</span><br><span class="line"> if (node != null){</span><br><span class="line"> stack.pop();</span><br><span class="line"> stack.push(node);</span><br><span class="line"> stack.push(null);</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> stack.push(node.right);</span><br><span class="line"> }</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> stack.push(node.left);</span><br><span class="line"> }</span><br><span class="line"> }else {</span><br><span class="line"> stack.pop();</span><br><span class="line"> node = stack.peek();</span><br><span class="line"> stack.pop();</span><br><span class="line"> result.add(node.val);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h1 id="二叉树的层序遍历:"><a href="#二叉树的层序遍历:" class="headerlink" title="二叉树的层序遍历:"></a>二叉树的层序遍历:</h1><h2 id="思路:类似前序遍历,但要每层进行结果收集"><a href="#思路:类似前序遍历,但要每层进行结果收集" class="headerlink" title="思路:类似前序遍历,但要每层进行结果收集"></a>思路:类似前序遍历,但要每层进行结果收集</h2><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.*;</span><br><span class="line"></span><br><span class="line">public class TreeLevelOrder {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public List<List<Integer>> resList = new ArrayList<List<Integer>>();</span><br><span class="line"></span><br><span class="line"> public List<List<Integer>> levelOrder(TreeNode root) {</span><br><span class="line"> //checkFun01(root,0);</span><br><span class="line"> checkFun02(root);</span><br><span class="line"></span><br><span class="line"> return resList;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //DFS--递归方式</span><br><span class="line"> public void checkFun01(TreeNode node, Integer deep) {</span><br><span class="line"> if (node == null) return;</span><br><span class="line"> deep++;</span><br><span class="line"></span><br><span class="line"> if (resList.size() < deep) {</span><br><span class="line"> //当层级增加时,list的Item也增加,利用list的索引值进行层级界定</span><br><span class="line"> List<Integer> item = new ArrayList<Integer>();</span><br><span class="line"> resList.add(item);</span><br><span class="line"> }</span><br><span class="line"> resList.get(deep - 1).add(node.val);</span><br><span class="line"></span><br><span class="line"> checkFun01(node.left, deep);</span><br><span class="line"> checkFun01(node.right, deep);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //BFS--迭代方式--借助队列</span><br><span class="line"> public void checkFun02(TreeNode node) {</span><br><span class="line"> if (node == null) return;</span><br><span class="line"> Queue<TreeNode> que = new LinkedList<TreeNode>();</span><br><span class="line"> que.offer(node);</span><br><span class="line"></span><br><span class="line"> while (!que.isEmpty()) {</span><br><span class="line"> List<Integer> itemList = new ArrayList<Integer>();</span><br><span class="line"> int len = que.size();</span><br><span class="line"></span><br><span class="line"> while (len > 0) {</span><br><span class="line"> TreeNode tmpNode = que.poll();</span><br><span class="line"> itemList.add(tmpNode.val);</span><br><span class="line"></span><br><span class="line"> if (tmpNode.left != null) que.offer(tmpNode.left);</span><br><span class="line"> if (tmpNode.right != null) que.offer(tmpNode.right);</span><br><span class="line"> len--;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> resList.add(itemList);</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></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树的遍历 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>day013</title>
<link href="/2024/05/04/day18/"/>
<url>/2024/05/04/day18/</url>
<content type="html"><![CDATA[<h2 id="翻转二叉树:LK226"><a href="#翻转二叉树:LK226" class="headerlink" title="翻转二叉树:LK226"></a>翻转二叉树:LK226</h2><h3 id="思路:交换左右孩子的值,利用递归函数一直向下递归就行!💪"><a href="#思路:交换左右孩子的值,利用递归函数一直向下递归就行!💪" class="headerlink" title="思路:交换左右孩子的值,利用递归函数一直向下递归就行!💪"></a>思路:交换左右孩子的值,利用递归函数一直向下递归就行!💪</h3><img src="https://code-thinking.cdn.bcebos.com/gifs/%E7%BF%BB%E8%BD%AC%E4%BA%8C%E5%8F%89%E6%A0%91.gif" alt="翻转二叉树" style="zoom: 200%;"><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class num226 {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public TreeNode invertTree(TreeNode root) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> swap(root);</span><br><span class="line"> return root;</span><br><span class="line"> }</span><br><span class="line"> public void swap(TreeNode root){</span><br><span class="line"> if (root == null){</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> TreeNode left = root.left;</span><br><span class="line"> TreeNode right = root.right;</span><br><span class="line"> root.left = right;</span><br><span class="line"> root.right = left;</span><br><span class="line"> swap(left);</span><br><span class="line"> swap(right);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法的代码:"><a href="#其他方法的代码:" class="headerlink" title="其他方法的代码:"></a>其他方法的代码:</h3><figure class="highlight java"><table><tbody><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></pre></td><td class="code"><pre><span class="line"><span class="comment">//DFS递归</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</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="keyword">public</span> TreeNode <span class="title function_">invertTree</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="keyword">if</span> (root == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> invertTree(root.left);</span><br><span class="line"> invertTree(root.right);</span><br><span class="line"> swapChildren(root);</span><br><span class="line"> <span class="keyword">return</span> root;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">swapChildren</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">tmp</span> <span class="operator">=</span> root.left;</span><br><span class="line"> root.left = root.right;</span><br><span class="line"> root.right = tmp;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">//BFS</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> TreeNode <span class="title function_">invertTree</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="keyword">if</span> (root == <span class="literal">null</span>) {<span class="keyword">return</span> <span class="literal">null</span>;}</span><br><span class="line"> ArrayDeque<TreeNode> deque = <span class="keyword">new</span> <span class="title class_">ArrayDeque</span><>();</span><br><span class="line"> deque.offer(root);</span><br><span class="line"> <span class="keyword">while</span> (!deque.isEmpty()) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> deque.size();</span><br><span class="line"> <span class="keyword">while</span> (size-- > <span class="number">0</span>) {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> deque.poll();</span><br><span class="line"> swap(node);</span><br><span class="line"> <span class="keyword">if</span> (node.left != <span class="literal">null</span>) deque.offer(node.left);</span><br><span class="line"> <span class="keyword">if</span> (node.right != <span class="literal">null</span>) deque.offer(node.right);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> root;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">swap</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">temp</span> <span class="operator">=</span> root.left;</span><br><span class="line"> root.left = root.right;</span><br><span class="line"> root.right = temp;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="对称二叉树:LK101"><a href="#对称二叉树:LK101" class="headerlink" title="对称二叉树:LK101"></a>对称二叉树:LK101</h2><h3 id="思路:比较根节点的左右子树,比较左右子树的节点值是否相等"><a href="#思路:比较根节点的左右子树,比较左右子树的节点值是否相等" class="headerlink" title="思路:比较根节点的左右子树,比较左右子树的节点值是否相等"></a>思路:比较根节点的左右子树,比较左右子树的节点值是否相等</h3><p><img src="https://code-thinking-1253855093.file.myqcloud.com/pics/20210203144624414.png" alt="101. 对称二叉树1"></p><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">public class num101 {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> public boolean isSymmetric(TreeNode root) {</span><br><span class="line"> return compare(root.left, root.right);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private boolean compare(TreeNode left, TreeNode right) {</span><br><span class="line"></span><br><span class="line"> if (left == null && right != null) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> if (left != null && right == null) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (left == null && right == null) {</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> if (left.val != right.val) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> // 比较外侧</span><br><span class="line"> boolean compareOutside = compare(left.left, right.right);</span><br><span class="line"> // 比较内侧</span><br><span class="line"> boolean compareInside = compare(left.right, right.left);</span><br><span class="line"> return compareOutside && compareInside;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法代码:"><a href="#其他方法代码:" class="headerlink" title="其他方法代码:"></a>其他方法代码:</h3><figure class="highlight java"><table><tbody><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="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="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isSymmetric2</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> Deque<TreeNode> deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> deque.offerFirst(root.left);</span><br><span class="line"> deque.offerLast(root.right);</span><br><span class="line"> <span class="keyword">while</span> (!deque.isEmpty()) {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">leftNode</span> <span class="operator">=</span> deque.pollFirst();</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">rightNode</span> <span class="operator">=</span> deque.pollLast();</span><br><span class="line"> <span class="keyword">if</span> (leftNode == <span class="literal">null</span> && rightNode == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"><span class="comment">// if (leftNode == null && rightNode != null) {</span></span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"><span class="comment">// if (leftNode != null && rightNode == null) {</span></span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"><span class="comment">// if (leftNode.val != rightNode.val) {</span></span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"> <span class="comment">// 以上三个判断条件合并</span></span><br><span class="line"> <span class="keyword">if</span> (leftNode == <span class="literal">null</span> || rightNode == <span class="literal">null</span> || leftNode.val != rightNode.val) {</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"> deque.offerFirst(leftNode.left);</span><br><span class="line"> deque.offerFirst(leftNode.right);</span><br><span class="line"> deque.offerLast(rightNode.right);</span><br><span class="line"> deque.offerLast(rightNode.left);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="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="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isSymmetric3</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> Queue<TreeNode> deque = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> deque.offer(root.left);</span><br><span class="line"> deque.offer(root.right);</span><br><span class="line"> <span class="keyword">while</span> (!deque.isEmpty()) {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">leftNode</span> <span class="operator">=</span> deque.poll();</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">rightNode</span> <span class="operator">=</span> deque.poll();</span><br><span class="line"> <span class="keyword">if</span> (leftNode == <span class="literal">null</span> && rightNode == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> }</span><br><span class="line"><span class="comment">// if (leftNode == null && rightNode != null) {</span></span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"><span class="comment">// if (leftNode != null && rightNode == null) {</span></span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"><span class="comment">// if (leftNode.val != rightNode.val) {</span></span><br><span class="line"><span class="comment">// return false;</span></span><br><span class="line"><span class="comment">// }</span></span><br><span class="line"> <span class="comment">// 以上三个判断条件合并</span></span><br><span class="line"> <span class="keyword">if</span> (leftNode == <span class="literal">null</span> || rightNode == <span class="literal">null</span> || leftNode.val != rightNode.val) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 这里顺序与使用Deque不同</span></span><br><span class="line"> deque.offer(leftNode.left);</span><br><span class="line"> deque.offer(rightNode.right);</span><br><span class="line"> deque.offer(leftNode.right);</span><br><span class="line"> deque.offer(rightNode.left);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 递归 </tag>
<tag> 二叉树 </tag>
</tags>
</entry>
<entry>
<title>打家劫舍</title>
<link href="/2024/05/04/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/"/>
<url>/2024/05/04/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/</url>
<content type="html"><![CDATA[<h2 id="打家劫舍:LK198"><a href="#打家劫舍:LK198" class="headerlink" title="打家劫舍:LK198"></a>打家劫舍:LK198</h2><h3 id="思路:利用动规五部曲,确定dp-i-的含义是第i-个房间的最大值"><a href="#思路:利用动规五部曲,确定dp-i-的含义是第i-个房间的最大值" class="headerlink" title="思路:利用动规五部曲,确定dp[i]的含义是第i 个房间的最大值"></a>思路:利用动规五部曲,确定dp[i]的含义是第i 个房间的最大值</h3><h3 id="其中分为偷与不偷两种情况"><a href="#其中分为偷与不偷两种情况" class="headerlink" title="其中分为偷与不偷两种情况"></a>其中分为偷与不偷两种情况</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK198 {</span><br><span class="line"> public int rob(int[] nums) {</span><br><span class="line"> if (nums == null || nums.length == 0) return 0;</span><br><span class="line"> if (nums.length == 1) return nums[0];</span><br><span class="line"> //1.定义dp[]数组</span><br><span class="line"> int[] dp = new int[nums.length];</span><br><span class="line"> //2.初始化数组</span><br><span class="line"> dp[0] = nums[0];</span><br><span class="line"> dp[1] = Math.max(nums[0], nums[1]);</span><br><span class="line"> //3.确定遍历顺序</span><br><span class="line"> for (int i = 2; i < nums.length; i++) {</span><br><span class="line"> //4.确定递推公式</span><br><span class="line"> //取和不取num[i]的最大值就是dp[i]</span><br><span class="line"> dp[i] = Math.max(dp[i - 2] + nums[i], dp[i -1]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> return dp[nums.length - 1];</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //5.打印数组案例</span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> int[] nums = {2,7,9,3,1};</span><br><span class="line"> LK198 lk198 = new LK198();</span><br><span class="line"> int rob = lk198.rob(nums);</span><br><span class="line"> System.out.println(rob);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="其他方法的代码:"><a href="#其他方法的代码:" class="headerlink" title="其他方法的代码:"></a>其他方法的代码:</h3><figure class="highlight java"><table><tbody><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="comment">// 使用滚动数组思想,优化空间</span></span><br><span class="line"><span class="comment">// 分析本题可以发现,所求结果仅依赖于前两种状态,此时可以使用滚动数组思想将空间复杂度降低为3个空间</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">rob</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> </span><br><span class="line"> <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> nums.length;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (len == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (len == <span class="number">1</span>) <span class="keyword">return</span> nums[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (len == <span class="number">2</span>) <span class="keyword">return</span> Math.max(nums[<span class="number">0</span>],nums[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="type">int</span>[] result = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">3</span>]; <span class="comment">//存放选择的结果</span></span><br><span class="line"> result[<span class="number">0</span>] = nums[<span class="number">0</span>];</span><br><span class="line"> result[<span class="number">1</span>] = Math.max(nums[<span class="number">0</span>],nums[<span class="number">1</span>]);</span><br><span class="line"> </span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i=<span class="number">2</span>;i<len;i++){</span><br><span class="line"></span><br><span class="line"> result[<span class="number">2</span>] = Math.max(result[<span class="number">0</span>]+nums[i],result[<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"> result[<span class="number">0</span>] = result[<span class="number">1</span>];</span><br><span class="line"> result[<span class="number">1</span>] = result[<span class="number">2</span>];</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> result[<span class="number">2</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 进一步对滚动数组的空间优化 dp数组只存与计算相关的两次数据</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">rob</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> <span class="keyword">if</span> (nums.length == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">return</span> nums[<span class="number">0</span>];</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 初始化dp数组</span></span><br><span class="line"> <span class="comment">// 优化空间 dp数组只用2格空间 只记录与当前计算相关的前两个结果</span></span><br><span class="line"> <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">2</span>];</span><br><span class="line"> dp[<span class="number">0</span>] = nums[<span class="number">0</span>];</span><br><span class="line"> dp[<span class="number">1</span>] = Math.max(nums[<span class="number">0</span>],nums[<span class="number">1</span>]);</span><br><span class="line"> <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="comment">// 遍历</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">2</span>; i < nums.length; i++) {</span><br><span class="line"> res = Math.max((dp[<span class="number">0</span>] + nums[i]) , dp[<span class="number">1</span>] );</span><br><span class="line"> dp[<span class="number">0</span>] = dp[<span class="number">1</span>];</span><br><span class="line"> dp[<span class="number">1</span>] = res;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 输出结果</span></span><br><span class="line"> <span class="keyword">return</span> dp[<span class="number">1</span>];</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="打家劫舍2:LK213"><a href="#打家劫舍2:LK213" class="headerlink" title="打家劫舍2:LK213"></a>打家劫舍2:LK213</h2><h3 id="思路:将数组分为不包含首尾元素,包含首元素不包含尾元素以及包含尾元素不包含首元素3种状态!"><a href="#思路:将数组分为不包含首尾元素,包含首元素不包含尾元素以及包含尾元素不包含首元素3种状态!" class="headerlink" title="思路:将数组分为不包含首尾元素,包含首元素不包含尾元素以及包含尾元素不包含首元素3种状态!"></a>思路:将数组分为不包含首尾元素,包含首元素不包含尾元素以及包含尾元素不包含首元素3种状态!</h3><h3 id="但是第一种情况被后面的涵盖了"><a href="#但是第一种情况被后面的涵盖了" class="headerlink" title="但是第一种情况被后面的涵盖了"></a>但是第一种情况被后面的涵盖了</h3><img src="/2024/05/04/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/image-20240504095517024.png" alt="image-20240504095517024" style="zoom:200%;"><img src="/2024/05/04/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/image-20240504095612931.png" alt="image-20240504095612931" style="zoom:200%;"><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a><img src="/2024/05/04/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/image-20240504095706752.png" alt="image-20240504095706752" style="zoom:200%;">代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK213 {</span><br><span class="line"> public int rob(int[] nums) {</span><br><span class="line"> if (nums == null || nums.length == 0) return 0;</span><br><span class="line"> if (nums.length == 1) return nums[0];</span><br><span class="line"> //1.定义dp[]数组</span><br><span class="line"> int[][] dp = new int[2][nums.length];</span><br><span class="line"> //2.初始化数组</span><br><span class="line"> dp[0][0] = nums[0];//含有首元素不含尾元素</span><br><span class="line"> dp[1][0] = 0;//含有尾元素不含有首元素</span><br><span class="line"> dp[0][1] = nums[0];</span><br><span class="line"> dp[1][1] = nums[1];</span><br><span class="line"> //3.确定遍历顺序</span><br><span class="line"> for (int i = 2; i < nums.length - 1; i++) {</span><br><span class="line"> //4.确定递推公式</span><br><span class="line"> //取和不取num[i]的最大值就是dp[i]</span><br><span class="line"> dp[0][i] = Math.max(dp[0][i -2] + nums[i], dp[0][i - 1]);</span><br><span class="line"> }</span><br><span class="line"> for (int i = 2; i < nums.length; i++) {</span><br><span class="line"> //4.确定递推公式</span><br><span class="line"> //取和不取num[i]的最大值就是dp[i]</span><br><span class="line"> dp[1][i] = Math.max(dp[1][i -2] + nums[i], dp[1][i - 1]);</span><br><span class="line"> }</span><br><span class="line"> return Math.max(dp[0][nums.length - 2], dp[1][nums.length - 1]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> //5.打印数组案例</span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> int[] nums = {2,3,1};</span><br><span class="line"> LK213 lk198 = new LK213();</span><br><span class="line"> int rob = lk198.rob(nums);</span><br><span class="line"> System.out.println(rob);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="打家劫舍3:LK337"><a href="#打家劫舍3:LK337" class="headerlink" title="打家劫舍3:LK337"></a>打家劫舍3:LK337</h2><h3 id="思路:利用后序遍历来求取"><a href="#思路:利用后序遍历来求取" class="headerlink" title="思路:利用后序遍历来求取"></a>思路:利用后序遍历来求取</h3><img src="/2024/05/04/%E6%89%93%E5%AE%B6%E5%8A%AB%E8%88%8D/image-20240504111738804.png" alt="image-20240504111738804" style="zoom:200%;"><h3 id="代码:-2"><a href="#代码:-2" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">import org.example.二叉树.Tree;</span><br><span class="line">import org.example.二叉树.Tree1;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class LK337 {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> // 3.状态标记递归</span><br><span class="line"> // 执行用时:0 ms , 在所有 Java 提交中击败了 100% 的用户</span><br><span class="line"> // 不偷:Max(左孩子不偷,左孩子偷) + Max(右孩子不偷,右孩子偷)</span><br><span class="line"> // root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) +</span><br><span class="line"> // Math.max(rob(root.right)[0], rob(root.right)[1])</span><br><span class="line"> // 偷:左孩子不偷+ 右孩子不偷 + 当前节点偷</span><br><span class="line"> // root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;</span><br><span class="line"> public int rob(TreeNode root) {</span><br><span class="line"> int[] res = robAction1(root);</span><br><span class="line"> return Math.max(res[0], res[1]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> int[] robAction1(TreeNode root) {</span><br><span class="line"> int res[] = new int[2];</span><br><span class="line"> if (root == null)</span><br><span class="line"> return res;</span><br><span class="line">//后序遍历 左-右-中</span><br><span class="line"> int[] left = robAction1(root.left);</span><br><span class="line"> int[] right = robAction1(root.right);</span><br><span class="line"></span><br><span class="line"> res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);</span><br><span class="line"> res[1] = root.val + left[0] + right[0];</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 动态规划 </category>
</categories>
<tags>
<tag> 动态规划 </tag>
</tags>
</entry>
<entry>
<title>day011</title>
<link href="/2024/05/03/day14/"/>
<url>/2024/05/03/day14/</url>
<content type="html"><![CDATA[<h2 id="二叉树的递归遍历:"><a href="#二叉树的递归遍历:" class="headerlink" title="二叉树的递归遍历:"></a>二叉树的递归遍历:</h2><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class Tree {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</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"> public List<Integer> preorderTraversal(TreeNode root) {</span><br><span class="line"> ArrayList<Integer> result = new ArrayList<>();</span><br><span class="line"> preorder(root, result);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> public void preorder(TreeNode root, List<Integer> result){</span><br><span class="line"> if (root == null){</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> result.add(root.val);</span><br><span class="line"> preorder(root.left, result);</span><br><span class="line"> preorder(root.right, result);</span><br><span class="line"> }</span><br><span class="line"> //中序遍历</span><br><span class="line"> public List<Integer> inorderTraversal(TreeNode root){</span><br><span class="line"> ArrayList<Integer> result = new ArrayList<>();</span><br><span class="line"> inorder(root, result);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> private void inorder(TreeNode root, List<Integer> result) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> inorder(root.left, result);</span><br><span class="line"> result.add(root.val);</span><br><span class="line"> inorder(root.right, result);</span><br><span class="line"> }</span><br><span class="line"> //后序遍历</span><br><span class="line"> public List<Integer> postorderTraversal(TreeNode root){</span><br><span class="line"> ArrayList<Integer> result = new ArrayList<>();</span><br><span class="line"> postorder(root, result);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> private void postorder(TreeNode root, List<Integer> result) {</span><br><span class="line"> if (root == null){</span><br><span class="line"> return;</span><br><span class="line"> }</span><br><span class="line"> inorder(root.left, result);</span><br><span class="line"> inorder(root.right, result);</span><br><span class="line"> result.add(root.val);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="二叉树的迭代遍历:"><a href="#二叉树的迭代遍历:" class="headerlink" title="二叉树的迭代遍历:"></a>二叉树的迭代遍历:</h2><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.二叉树;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Collections;</span><br><span class="line">import java.util.List;</span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class Tree {</span><br><span class="line"></span><br><span class="line"> public class TreeNode {</span><br><span class="line"> int val;</span><br><span class="line"> TreeNode left;</span><br><span class="line"> TreeNode right;</span><br><span class="line"></span><br><span class="line"> public TreeNode() {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public TreeNode(int val, TreeNode left, TreeNode right) {</span><br><span class="line"> this.val = val;</span><br><span class="line"> this.left = left;</span><br><span class="line"> this.right = right;</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"> public List<Integer> preorderTraversal(TreeNode root){</span><br><span class="line"> ArrayList<Integer> result = new ArrayList<>();</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> if (root == null){</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> stack.push(root);</span><br><span class="line"> while ( !stack.isEmpty()){</span><br><span class="line"> TreeNode node = stack.pop();</span><br><span class="line"> result.add(node.val);</span><br><span class="line"> if (node.right != null){</span><br><span class="line"> stack.push(node.right);</span><br><span class="line"> }</span><br><span class="line"> if (node.left != null){</span><br><span class="line"> stack.push(node.left);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> // 中序遍历顺序: 左-中-右 入栈顺序: 左-右</span><br><span class="line"> public List<Integer> inorderTraversal(TreeNode root) {</span><br><span class="line"> List<Integer> result = new ArrayList<>();</span><br><span class="line"> if (root == null){</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> TreeNode cur = root;</span><br><span class="line"> while (cur != null || !stack.isEmpty()){</span><br><span class="line"> if (cur != null){</span><br><span class="line"> stack.push(cur);</span><br><span class="line"> cur = cur.left;</span><br><span class="line"> }else{</span><br><span class="line"> cur = stack.pop();</span><br><span class="line"> result.add(cur.val);</span><br><span class="line"> cur = cur.right;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> // 后序遍历顺序 左-右-中 入栈顺序:中-左-右 出栈顺序:中-右-左, 最后翻转结果</span><br><span class="line"> public List<Integer> postorderTraversal(TreeNode root) {</span><br><span class="line"> List<Integer> result = new ArrayList<>();</span><br><span class="line"> if (root == null) {</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> Stack<TreeNode> stack = new Stack<>();</span><br><span class="line"> stack.push(root);</span><br><span class="line"> while (!stack.isEmpty()) {</span><br><span class="line"> TreeNode node = stack.pop();</span><br><span class="line"> result.add(node.val);</span><br><span class="line"> if (node.left != null) {</span><br><span class="line"> stack.push(node.left);</span><br><span class="line"> }</span><br><span class="line"> if (node.right != null) {</span><br><span class="line"> stack.push(node.right);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> Collections.reverse(result);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 二叉树 </category>
</categories>
<tags>
<tag> 二叉树的遍历 </tag>
</tags>
</entry>
<entry>
<title>单调栈</title>
<link href="/2024/05/03/%E5%8D%95%E8%B0%83%E6%A0%88/"/>
<url>/2024/05/03/%E5%8D%95%E8%B0%83%E6%A0%88/</url>
<content type="html"><![CDATA[<p>##柱状图中最大的矩形:LK084</p><h3 id="思路:利用单调栈来保持栈内元素单调递增,遇到小于栈口元素时就进行面积的计算,"><a href="#思路:利用单调栈来保持栈内元素单调递增,遇到小于栈口元素时就进行面积的计算," class="headerlink" title="思路:利用单调栈来保持栈内元素单调递增,遇到小于栈口元素时就进行面积的计算,"></a>思路:利用单调栈来保持栈内元素单调递增,遇到小于栈口元素时就进行面积的计算,</h3><p> int mid = st.peek();<br> st.pop();<br> int left = st.peek();<br> int right = i;<br> int w = right - left - 1;<br> int h = heights[mid];<br> result = Math.max(result, w * h);</p><h3 id="分别找到左边界和右边界计算出宽度"><a href="#分别找到左边界和右边界计算出宽度" class="headerlink" title="分别找到左边界和右边界计算出宽度"></a>分别找到左边界和右边界计算出宽度</h3><h3 id="高则是对应下标的-heights-mid"><a href="#高则是对应下标的-heights-mid" class="headerlink" title="高则是对应下标的 heights[mid]"></a>高则是对应下标的 heights[mid]</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.单调栈;</span><br><span class="line"></span><br><span class="line">import java.util.Arrays;</span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class LK084 {</span><br><span class="line"> public int largestRectangleArea3(int[] heights) {</span><br><span class="line"></span><br><span class="line"> int[] newHeight = new int[heights.length + 2];</span><br><span class="line"> System.arraycopy(heights, 0, newHeight, 1, heights.length);</span><br><span class="line"> newHeight[heights.length+1] = 0;</span><br><span class="line"> newHeight[0] = 0;</span><br><span class="line"></span><br><span class="line"> Stack<Integer> stack = new Stack<>();</span><br><span class="line"> stack.push(0);</span><br><span class="line"></span><br><span class="line"> int res = 0;</span><br><span class="line"> for (int i = 1; i < newHeight.length; i++) {</span><br><span class="line"> while (newHeight[i] < newHeight[stack.peek()]) {</span><br><span class="line"> int mid = stack.pop();</span><br><span class="line"> int w = i - stack.peek() - 1;</span><br><span class="line"> int h = newHeight[mid];</span><br><span class="line"> res = Math.max(res, w * h);</span><br><span class="line"> }</span><br><span class="line"> stack.push(i);</span><br><span class="line"> }</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line"> int largestRectangleArea2(int[] heights) {</span><br><span class="line"> Stack<Integer> st = new Stack<Integer>();</span><br><span class="line"></span><br><span class="line"> // 数组扩容,在头和尾各加入一个元素</span><br><span class="line"> int [] newHeights = new int[heights.length + 2];</span><br><span class="line"> newHeights[0] = 0;</span><br><span class="line"> newHeights[newHeights.length - 1] = 0;</span><br><span class="line"> for (int index = 0; index < heights.length; index++){</span><br><span class="line"> newHeights[index + 1] = heights[index];</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> heights = newHeights;</span><br><span class="line"></span><br><span class="line"> st.push(0);</span><br><span class="line"> int result = 0;</span><br><span class="line"> // 第一个元素已经入栈,从下标1开始</span><br><span class="line"> for (int i = 1; i < heights.length; i++) {</span><br><span class="line"> // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下标</span><br><span class="line"> if (heights[i] > heights[st.peek()]) {</span><br><span class="line"> st.push(i);</span><br><span class="line"> } else if (heights[i] == heights[st.peek()]) {</span><br><span class="line"> st.pop(); // 这个可以加,可以不加,效果一样,思路不同</span><br><span class="line"> st.push(i);</span><br><span class="line"> } else {</span><br><span class="line"> while (heights[i] < heights[st.peek()]) { // 注意是while</span><br><span class="line"> int mid = st.peek();</span><br><span class="line"> st.pop();</span><br><span class="line"> int left = st.peek();</span><br><span class="line"> int right = i;</span><br><span class="line"> int w = right - left - 1;</span><br><span class="line"> int h = heights[mid];</span><br><span class="line"> result = Math.max(result, w * h);</span><br><span class="line"> }</span><br><span class="line"> st.push(i);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> public int largestRectangleArea(int[] heights) {</span><br><span class="line"> int length = heights.length;</span><br><span class="line"> int[] minLeftIndex = new int [length];</span><br><span class="line"> int[] minRightIndex = new int [length];</span><br><span class="line"> // 记录左边第一个小于该柱子的下标</span><br><span class="line"> minLeftIndex[0] = -1 ;</span><br><span class="line"> for (int i = 1; i < length; i++) {</span><br><span class="line"> int t = i - 1;</span><br><span class="line"> // 这里不是用if,而是不断向右寻找的过程</span><br><span class="line"> while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];</span><br><span class="line"> minLeftIndex[i] = t;</span><br><span class="line"> }</span><br><span class="line"> // 记录每个柱子右边第一个小于该柱子的下标</span><br><span class="line"> minRightIndex[length - 1] = length;</span><br><span class="line"> for (int i = length - 2; i >= 0; i--) {</span><br><span class="line"> int t = i + 1;</span><br><span class="line"> while(t < length && heights[t] >= heights[i]) t = minRightIndex[t];</span><br><span class="line"> minRightIndex[i] = t;</span><br><span class="line"> }</span><br><span class="line"> // 求和</span><br><span class="line"> int result = 0;</span><br><span class="line"> for (int i = 0; i < length; i++) {</span><br><span class="line"> int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);</span><br><span class="line"> result = Math.max(sum, result);</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> int[] heights = {2,4};</span><br><span class="line"> LK084 lk084 = new LK084();</span><br><span class="line"> int size = lk084.largestRectangleArea(heights);</span><br><span class="line"> System.out.println(size);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 单调栈 </category>
</categories>
<tags>
<tag> 栈与队列 </tag>
</tags>
</entry>
<entry>
<title>day09</title>
<link href="/2024/05/02/day13/"/>
<url>/2024/05/02/day13/</url>
<content type="html"><![CDATA[<h2 id="滑动窗口:LK239"><a href="#滑动窗口:LK239" class="headerlink" title="滑动窗口:LK239"></a>滑动窗口:LK239</h2><h3 id="思路:单调栈,保持队列内的元素单调增!获取队列口元素!"><a href="#思路:单调栈,保持队列内的元素单调增!获取队列口元素!" class="headerlink" title="思路:单调栈,保持队列内的元素单调增!获取队列口元素!"></a>思路:单调栈,保持队列内的元素单调增!获取队列口元素!</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.栈;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayDeque;</span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class num239 {</span><br><span class="line"> public int[] maxSlidingWindow(int[] nums, int k) {</span><br><span class="line"> ArrayDeque<Integer> deque = new ArrayDeque<>();</span><br><span class="line"> int n = nums.length;</span><br><span class="line"> int[] res = new int[n - k + 1];</span><br><span class="line"> int idx = 0;</span><br><span class="line"> for(int i = 0; i < n; i++) {</span><br><span class="line"> // 根据题意,i为nums下标,是要在[i - k + 1, i] 中选到最大值,只需要保证两点</span><br><span class="line"> // 1.队列头结点需要在[i - k + 1, i]范围内,不符合则要弹出</span><br><span class="line"> while(!deque.isEmpty() && deque.peek() < i - k + 1){</span><br><span class="line"> deque.poll();</span><br><span class="line"> }</span><br><span class="line"> // 2.既然是单调,就要保证每次放进去的数字要比末尾的都大,否则也弹出</span><br><span class="line"> while(!deque.isEmpty() && nums[deque.peekLast()] < nums[i]) {</span><br><span class="line"> deque.pollLast();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> deque.offer(i);</span><br><span class="line"></span><br><span class="line"> // 因为单调,当i增长到符合第一个k范围的时候,每滑动一步都将队列头节点放入结果就行了</span><br><span class="line"> if(i >= k - 1){</span><br><span class="line"> res[idx++] = nums[deque.peek()];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> int[] nums = {1, -1};</span><br><span class="line"> num239 num239 = new num239();</span><br><span class="line"> int[] res = num239.maxSlidingWindow(nums, 1);</span><br><span class="line"> for (int re : res) {</span><br><span class="line"> System.out.println(re);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="前K个高频元素:LK347"><a href="#前K个高频元素:LK347" class="headerlink" title="前K个高频元素:LK347"></a>前K个高频元素:LK347</h2><h3 id="思路:运用小顶堆来存储前k个高频元素!优先级队列就是基于堆来实现的!"><a href="#思路:运用小顶堆来存储前k个高频元素!优先级队列就是基于堆来实现的!" class="headerlink" title="思路:运用小顶堆来存储前k个高频元素!优先级队列就是基于堆来实现的!"></a>思路:运用小顶堆来存储前k个高频元素!优先级队列就是基于堆来实现的!</h3><h3 id="小顶堆就是一个二叉树"><a href="#小顶堆就是一个二叉树" class="headerlink" title="小顶堆就是一个二叉树"></a>小顶堆就是一个二叉树</h3><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">public int[] topKFrequent2(int[] nums, int k) {</span><br><span class="line"> Map<Integer,Integer> map = new HashMap<>(); //key为数组元素值,val为对应出现次数</span><br><span class="line"> for (int num : nums) {</span><br><span class="line"> map.put(num, map.getOrDefault(num, 0) + 1);</span><br><span class="line"> }</span><br><span class="line"> //在优先队列中存储二元组(num, cnt),cnt表示元素值num在数组中的出现次数</span><br><span class="line"> //出现次数按从队头到队尾的顺序是从小到大排,出现次数最低的在队头(相当于小顶堆)</span><br><span class="line"> PriorityQueue<int[]> pq = new PriorityQueue<>((pair1, pair2) -> pair1[1] - pair2[1]);</span><br><span class="line"> for (Map.Entry<Integer, Integer> entry : map.entrySet()) { //小顶堆只需要维持k个元素有序</span><br><span class="line"> if (pq.size() < k) { //小顶堆元素个数小于k个时直接加</span><br><span class="line"> pq.add(new int[]{entry.getKey(), entry.getValue()});</span><br><span class="line"> } else {</span><br><span class="line"> if (entry.getValue() > pq.peek()[1]) { //当前元素出现次数大于小顶堆的根结点(这k个元素中出现次数最少的那个)</span><br><span class="line"> pq.poll(); //弹出队头(小顶堆的根结点),即把堆里出现次数最少的那个删除,留下的就是出现次数多的了</span><br><span class="line"> pq.add(new int[]{entry.getKey(), entry.getValue()});</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> int[] ans = new int[k];</span><br><span class="line"> for (int i = k - 1; i >= 0; i--) { //依次弹出小顶堆,先弹出的是堆的根,出现次数少,后面弹出的出现次数多</span><br><span class="line"> ans[i] = pq.poll()[0];</span><br><span class="line"> }</span><br><span class="line"> return ans;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 栈与队列 </category>
</categories>
<tags>
<tag> 栈与队列 </tag>
<tag> 优先级队列 </tag>
<tag> 小顶堆 </tag>
</tags>
</entry>
<entry>
<title>day08</title>
<link href="/2024/04/27/day11/"/>
<url>/2024/04/27/day11/</url>
<content type="html"><![CDATA[<h2 id="有效的括号:LK020😂"><a href="#有效的括号:LK020😂" class="headerlink" title="有效的括号:LK020😂"></a>有效的括号:LK020😂</h2><h3 id="思路:用栈来存储对应遍历的括号的右括号;当遇到右括号就弹栈;如果都匹配最后栈会为空,反之则不会为空!!!"><a href="#思路:用栈来存储对应遍历的括号的右括号;当遇到右括号就弹栈;如果都匹配最后栈会为空,反之则不会为空!!!" class="headerlink" title="思路:用栈来存储对应遍历的括号的右括号;当遇到右括号就弹栈;如果都匹配最后栈会为空,反之则不会为空!!!"></a>思路:用栈来存储对应遍历的括号的右括号;当遇到右括号就弹栈;如果都匹配最后栈会为空,反之则不会为空!!!</h3><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">package org.example.栈;</span><br><span class="line"></span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class num020 {</span><br><span class="line"> public boolean isValid(String s) {</span><br><span class="line"> //括号只能成对</span><br><span class="line"> if (s.length() % 2 != 0 || s.length() == 0) {</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> Stack<Character> stackChars = new Stack<>();</span><br><span class="line"> char ch;</span><br><span class="line"> for (int i = 0; i < s.length(); i++) {</span><br><span class="line"> ch = s.charAt(i);</span><br><span class="line"> if (ch == '(') {</span><br><span class="line"> stackChars.push(')');</span><br><span class="line"> } else if (ch == '[') {</span><br><span class="line"> stackChars.push(']');</span><br><span class="line"> } else if (ch == '{') {</span><br><span class="line"> stackChars.push('}');</span><br><span class="line"> } else if (stackChars.isEmpty() || stackChars.peek() != ch) {</span><br><span class="line"> return false;</span><br><span class="line"> } else {</span><br><span class="line"> stackChars.pop();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return stackChars.isEmpty();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> String s1 = "()[]{}";</span><br><span class="line"> // String s2 = "()}";</span><br><span class="line"> //String s3 = "([]}";</span><br><span class="line"> num020 num020 = new num020();</span><br><span class="line"> boolean s11 = num020.isValid(s1);</span><br><span class="line"> System.out.println(s11);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="删除字符串中的所有相邻重复项-LK1047"><a href="#删除字符串中的所有相邻重复项-LK1047" class="headerlink" title="删除字符串中的所有相邻重复项: LK1047"></a>删除字符串中的所有相邻重复项: LK1047</h2><h3 id="思路:将遍历的字符和栈顶的进行对比,相等就弹栈,不相等就放入栈,最后进行一个结果的搜集!"><a href="#思路:将遍历的字符和栈顶的进行对比,相等就弹栈,不相等就放入栈,最后进行一个结果的搜集!" class="headerlink" title="思路:将遍历的字符和栈顶的进行对比,相等就弹栈,不相等就放入栈,最后进行一个结果的搜集!"></a>思路:将遍历的字符和栈顶的进行对比,相等就弹栈,不相等就放入栈,最后进行一个结果的搜集!</h3><p>代码:</p><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.栈;</span><br><span class="line"></span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class num1047 {</span><br><span class="line"> public String removeDuplicates(String s) {</span><br><span class="line"> Stack<Character> stack = new Stack<>();</span><br><span class="line"> for (int i = 0; i < s.length(); i++) {</span><br><span class="line"> if (stack.isEmpty()){</span><br><span class="line"> stack.push(s.charAt(i));</span><br><span class="line"> }else {</span><br><span class="line"> if (s.charAt(i) != stack.peek()){</span><br><span class="line"> stack.push(s.charAt(i));</span><br><span class="line"> }else {</span><br><span class="line"> stack.pop();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> int lenth = stack.size();</span><br><span class="line"> char[] sresult = new char[lenth];</span><br><span class="line"> while (! stack.isEmpty()){</span><br><span class="line"> sresult[lenth - 1] = stack.pop();</span><br><span class="line"> lenth --;</span><br><span class="line"> }</span><br><span class="line"> String result = new String(sresult);</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="逆波兰表达式:LK150"><a href="#逆波兰表达式:LK150" class="headerlink" title="逆波兰表达式:LK150"></a>逆波兰表达式:LK150</h2><h3 id="思路:将运算符与数字区分开,不是运算符放入栈,是运算符就弹出栈顶的前两个数进行运算后的结果再放入栈,最后返回最终的栈顶元素就是结果!!"><a href="#思路:将运算符与数字区分开,不是运算符放入栈,是运算符就弹出栈顶的前两个数进行运算后的结果再放入栈,最后返回最终的栈顶元素就是结果!!" class="headerlink" title="思路:将运算符与数字区分开,不是运算符放入栈,是运算符就弹出栈顶的前两个数进行运算后的结果再放入栈,最后返回最终的栈顶元素就是结果!!"></a>思路:将运算符与数字区分开,不是运算符放入栈,是运算符就弹出栈顶的前两个数进行运算后的结果再放入栈,最后返回最终的栈顶元素就是结果!!</h3><h3 id="注意事项:在进行减法和除法运算的时候,必须后面的减去前面的或者除以前面的!例如:-a-b-a先入栈后出栈-,所以是:-a-b"><a href="#注意事项:在进行减法和除法运算的时候,必须后面的减去前面的或者除以前面的!例如:-a-b-a先入栈后出栈-,所以是:-a-b" class="headerlink" title="注意事项:在进行减法和除法运算的时候,必须后面的减去前面的或者除以前面的!例如:[a, b, /] a先入栈后出栈 ,所以是: a / b"></a>注意事项:在进行减法和除法运算的时候,必须后面的减去前面的或者除以前面的!例如:[a, b, /] a先入栈后出栈 ,所以是: a / b</h3><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">package org.example.栈与队列;</span><br><span class="line"></span><br><span class="line">import java.util.LinkedList;</span><br><span class="line"></span><br><span class="line">public class LK150 {</span><br><span class="line"> public int evalRPN(String[] tokens) {</span><br><span class="line"></span><br><span class="line"> LinkedList<Integer> stack = new LinkedList<>();</span><br><span class="line"> for (String t : tokens) {</span><br><span class="line"> switch (t){</span><br><span class="line"> case "+" ->{</span><br><span class="line"> Integer a = stack.pop();</span><br><span class="line"> Integer b = stack.pop();</span><br><span class="line"> stack.push(b+a);</span><br><span class="line"> }</span><br><span class="line"> case "-" ->{</span><br><span class="line"> Integer a = stack.pop();</span><br><span class="line"> Integer b = stack.pop();</span><br><span class="line"> stack.push(b-a);</span><br><span class="line"> }</span><br><span class="line"> case "*" ->{</span><br><span class="line"> Integer a = stack.pop();</span><br><span class="line"> Integer b = stack.pop();</span><br><span class="line"> stack.push(b*a);</span><br><span class="line"> }</span><br><span class="line"> case "/" ->{</span><br><span class="line"> Integer a = stack.pop();</span><br><span class="line"> Integer b = stack.pop();</span><br><span class="line"> stack.push(b/a);</span><br><span class="line"> }</span><br><span class="line"> default -> { //数字</span><br><span class="line"> stack.push(Integer.parseInt(t));</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return stack.pop();</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 栈与队列 </category>
</categories>
<tags>
<tag> 栈与队列 </tag>
</tags>
</entry>
<entry>
<title>用栈实现队列</title>
<link href="/2024/04/26/%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/"/>
<url>/2024/04/26/%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/</url>
<content type="html"><![CDATA[<h2 id="用栈实现队列:"><a href="#用栈实现队列:" class="headerlink" title="用栈实现队列:"></a>用栈实现队列:</h2><img src="https://code-thinking.cdn.bcebos.com/gifs/232.%E7%94%A8%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97%E7%89%88%E6%9C%AC2.gif" alt="232.用栈实现队列版本2" style="zoom:200%;"><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.栈;</span><br><span class="line"></span><br><span class="line">import java.util.Stack;</span><br><span class="line"></span><br><span class="line">public class MyQueue {</span><br><span class="line"> Stack<Integer> inStack = new Stack<Integer>();</span><br><span class="line"> Stack<Integer> outStack = new Stack<Integer>();</span><br><span class="line"> public MyQueue() {</span><br><span class="line"> Stack<Integer> inStack = new Stack<Integer>();</span><br><span class="line"> Stack<Integer> outStack = new Stack<Integer>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void push(int x) {</span><br><span class="line"> while (!outStack.isEmpty()){</span><br><span class="line"> inStack.push(outStack.pop());</span><br><span class="line"> }</span><br><span class="line"> outStack.push(x);</span><br><span class="line"> while (!inStack.isEmpty()){</span><br><span class="line"> outStack.push(inStack.pop());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public int pop() {</span><br><span class="line"> int pop = outStack.pop();</span><br><span class="line"> return pop;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public int peek() {</span><br><span class="line"> int peek = inStack.peek();</span><br><span class="line"> return peek;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public boolean empty() {</span><br><span class="line"> return inStack.isEmpty() && outStack.isEmpty();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 栈与队列 </category>
</categories>
<tags>
<tag> 栈与队列 </tag>
</tags>
</entry>
<entry>
<title>用队列实现栈</title>
<link href="/2024/04/26/%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88/"/>
<url>/2024/04/26/%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88/</url>
<content type="html"><![CDATA[<h2 id="用队列实现栈:"><a href="#用队列实现栈:" class="headerlink" title="用队列实现栈:"></a>用队列实现栈:</h2><img src="https://code-thinking.cdn.bcebos.com/gifs/225.%E7%94%A8%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88.gif" alt="225.用队列实现栈" style="zoom: 200%;"><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight java"><table><tbody><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">class</span> <span class="title class_">MyStack</span> {</span><br><span class="line"> <span class="comment">//q1作为主要的队列,其元素排列顺序和出栈顺序相同</span></span><br><span class="line"> Queue<Integer> q1 = <span class="keyword">new</span> <span class="title class_">ArrayDeque</span><>();</span><br><span class="line"> <span class="comment">//q2仅作为临时放置</span></span><br><span class="line"> Queue<Integer> q2 = <span class="keyword">new</span> <span class="title class_">ArrayDeque</span><>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MyStack</span><span class="params">()</span> {</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//在加入元素时先将q1中的元素依次出栈压入q2,然后将新加入的元素压入q1,再将q2中的元素依次出栈压入q1</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> {</span><br><span class="line"> <span class="keyword">while</span> (q1.size() > <span class="number">0</span>) {</span><br><span class="line"> q2.add(q1.poll());</span><br><span class="line"> }</span><br><span class="line"> q1.add(x);</span><br><span class="line"> <span class="keyword">while</span> (q2.size() > <span class="number">0</span>) {</span><br><span class="line"> q1.add(q2.poll());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">pop</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> q1.poll();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">top</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> q1.peek();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">empty</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> q1.isEmpty();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><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">class</span> <span class="title class_">MyStack</span> {</span><br><span class="line"></span><br><span class="line"> Queue<Integer> queue;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MyStack</span><span class="params">()</span> {</span><br><span class="line"> queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//每 offer 一个数(A)进来,都重新排列,把这个数(A)放到队列的队首</span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> {</span><br><span class="line"> queue.offer(x);</span><br><span class="line"> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> queue.size();</span><br><span class="line"> <span class="comment">//移动除了 A 的其它数</span></span><br><span class="line"> <span class="keyword">while</span> (size-- > <span class="number">1</span>)</span><br><span class="line"> queue.offer(queue.poll());</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">pop</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> queue.poll();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">top</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> queue.peek();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">empty</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> queue.isEmpty();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><figure class="highlight java"><table><tbody><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">class</span> <span class="title class_">MyStack</span> {</span><br><span class="line"> Queue<Integer> queue;</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">MyStack</span><span class="params">()</span> {</span><br><span class="line"> queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> {</span><br><span class="line"> queue.add(x);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">pop</span><span class="params">()</span> {</span><br><span class="line"> rePosition();</span><br><span class="line"> <span class="keyword">return</span> queue.poll();</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">top</span><span class="params">()</span> {</span><br><span class="line"> rePosition();</span><br><span class="line"> <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> queue.poll();</span><br><span class="line"> queue.add(result);</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">empty</span><span class="params">()</span> {</span><br><span class="line"> <span class="keyword">return</span> queue.isEmpty();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">rePosition</span><span class="params">()</span>{</span><br><span class="line"> <span class="type">int</span> <span class="variable">size</span> <span class="operator">=</span> queue.size();</span><br><span class="line"> size--;</span><br><span class="line"> <span class="keyword">while</span>(size--><span class="number">0</span>)</span><br><span class="line"> queue.add(queue.poll());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 栈与队列 </category>
</categories>
<tags>
<tag> 栈与队列 </tag>
</tags>
</entry>
<entry>
<title>KMP算法</title>
<link href="/2024/04/25/KMP%E7%AE%97%E6%B3%95/"/>
<url>/2024/04/25/KMP%E7%AE%97%E6%B3%95/</url>
<content type="html"><![CDATA[<h2 id="例:aab"><a href="#例:aab" class="headerlink" title="例:aab"></a>例:aab</h2><h2 id="前缀:a-aa-要包含首字母,不包含尾字母"><a href="#前缀:a-aa-要包含首字母,不包含尾字母" class="headerlink" title="前缀:a,aa(要包含首字母,不包含尾字母)"></a>前缀:a,aa(要包含首字母,不包含尾字母)</h2><h2 id="后缀:b-ab(要包含尾字母,不包含首字母)"><a href="#后缀:b-ab(要包含尾字母,不包含首字母)" class="headerlink" title="后缀:b,ab(要包含尾字母,不包含首字母)"></a>后缀:b,ab(要包含尾字母,不包含首字母)</h2><h2 id="最长的前缀:aa"><a href="#最长的前缀:aa" class="headerlink" title="最长的前缀:aa"></a>最长的前缀:aa</h2><h2 id="最长的后缀:ab"><a href="#最长的后缀:ab" class="headerlink" title="最长的后缀:ab"></a>最长的后缀:ab</h2><h2 id=""><a href="#" class="headerlink" title=""></a></h2><h2 id="next数组就是一个前缀表(prefix-table)"><a href="#next数组就是一个前缀表(prefix-table)" class="headerlink" title="next数组就是一个前缀表(prefix table)"></a>next数组就是一个前缀表(prefix table)</h2><h3 id="KMP是根据模式串来获取next-数组的!!"><a href="#KMP是根据模式串来获取next-数组的!!" class="headerlink" title="KMP是根据模式串来获取next[ ] 数组的!!"></a>KMP是根据模式串来获取next[ ] 数组的!!</h3><h3 id="前缀表有什么作用呢?"><a href="#前缀表有什么作用呢?" class="headerlink" title="前缀表有什么作用呢?"></a>前缀表有什么作用呢?</h3><h3 id="前缀表是用来回退的,它记录了模式串与主串-文本串-不匹配的时候,模式串应该从哪里开始重新匹配。"><a href="#前缀表是用来回退的,它记录了模式串与主串-文本串-不匹配的时候,模式串应该从哪里开始重新匹配。" class="headerlink" title="前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。"></a><strong>前缀表是用来回退的,它记录了模式串与主串(文本串)不匹配的时候,模式串应该从哪里开始重新匹配。</strong></h3><p>sss<img src="https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B21.gif" alt="KMP详解1" style="zoom: 200%;"></p><h2 id="最长公共前后缀"><a href="#最长公共前后缀" class="headerlink" title="最长公共前后缀"></a>最长公共前后缀</h2><h3 id="文章中字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。"><a href="#文章中字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。" class="headerlink" title="文章中字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。"></a>文章中字符串的<strong>前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串</strong>。</h3><h3 id="后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。"><a href="#后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。" class="headerlink" title="后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。"></a><strong>后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串</strong>。</h3><img src="https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B22.gif" alt="KMP精讲2" style="zoom: 200%;"><h2 id="以下我们以前缀表统一减一之后的next数组来做演示。"><a href="#以下我们以前缀表统一减一之后的next数组来做演示。" class="headerlink" title="以下我们以前缀表统一减一之后的next数组来做演示。"></a><strong>以下我们以前缀表统一减一之后的next数组来做演示</strong>。</h2><h3 id="有了next数组,就可以根据next数组来-匹配文本串s,和模式串t了。"><a href="#有了next数组,就可以根据next数组来-匹配文本串s,和模式串t了。" class="headerlink" title="有了next数组,就可以根据next数组来 匹配文本串s,和模式串t了。"></a>有了next数组,就可以根据next数组来 匹配文本串s,和模式串t了。</h3><h3 id="注意next数组是新前缀表(旧前缀表统一减一了)。"><a href="#注意next数组是新前缀表(旧前缀表统一减一了)。" class="headerlink" title="注意next数组是新前缀表(旧前缀表统一减一了)。"></a>注意next数组是新前缀表(旧前缀表统一减一了)。</h3><h3 id="匹配过程动画如下:"><a href="#匹配过程动画如下:" class="headerlink" title="匹配过程动画如下:"></a>匹配过程动画如下:</h3><img src="https://code-thinking.cdn.bcebos.com/gifs/KMP%E7%B2%BE%E8%AE%B24.gif" alt="KMP精讲4" style="zoom: 150%;"><h2 id="找出字符串中第一个匹配项的下标-LK028"><a href="#找出字符串中第一个匹配项的下标-LK028" class="headerlink" title="找出字符串中第一个匹配项的下标:LK028"></a>找出字符串中第一个匹配项的下标:LK028</h2><h3 id="通过模式串的前缀表即next数组来匹配文本串!!"><a href="#通过模式串的前缀表即next数组来匹配文本串!!" class="headerlink" title="通过模式串的前缀表即next数组来匹配文本串!!"></a>通过模式串的前缀表即next数组来匹配文本串!!</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.技巧.KMP算法;</span><br><span class="line"></span><br><span class="line">public class num028 {</span><br><span class="line"> public int strStr(String haystack, String needle) {</span><br><span class="line"> // 如果needle的长度为0,则返回0</span><br><span class="line"> if (needle.length() == 0) return 0;</span><br><span class="line"> // 创建一个next数组,用于存储needle的最长公共前缀和后缀的长度</span><br><span class="line"> int[] next = new int[needle.length()];</span><br><span class="line"> // 调用getNext方法,计算next数组</span><br><span class="line"> getNext(next, needle);</span><br><span class="line"></span><br><span class="line"> // 初始化j为0</span><br><span class="line"> int j = 0;</span><br><span class="line"> // 遍历haystack</span><br><span class="line"> for (int i = 0; i < haystack.length(); i++) {</span><br><span class="line"> // 如果j大于0,且needle的j位置的字符不等于haystack的i位置的字符,则将j置为next[j-1]</span><br><span class="line"> while (j > 0 && needle.charAt(j) != haystack.charAt(i))</span><br><span class="line"> j = next[j - 1];</span><br><span class="line"> // 如果needle的j位置的字符等于haystack的i位置的字符,则j加1</span><br><span class="line"> if (needle.charAt(j) == haystack.charAt(i))</span><br><span class="line"> j++;</span><br><span class="line"> // 如果j等于needle的长度,则说明找到了needle,返回i-needle.length()+1</span><br><span class="line"> if (j == needle.length())</span><br><span class="line"> return i - needle.length() + 1;</span><br><span class="line"> }</span><br><span class="line"> // 如果没有找到needle,则返回-1</span><br><span class="line"> return -1;</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private void getNext(int[] next, String s) {</span><br><span class="line"> // 初始化j为0</span><br><span class="line"> int j = 0;</span><br><span class="line"> // 将next[0]置为0</span><br><span class="line"> next[0] = 0;</span><br><span class="line"> // 遍历s</span><br><span class="line"> for (int i = 1; i < s.length(); i++) {</span><br><span class="line"> // 如果j大于0,且s的j位置的字符不等于s的i位置的字符,则将j置为next[j-1]</span><br><span class="line"> while (j > 0 && s.charAt(j) != s.charAt(i))</span><br><span class="line"> j = next[j - 1];</span><br><span class="line"> // 如果s的j位置的字符等于s的i位置的字符,则j加1</span><br><span class="line"> if (s.charAt(j) == s.charAt(i))</span><br><span class="line"> j++;</span><br><span class="line"> // 将next[i]置为j</span><br><span class="line"> next[i] = j;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="重复的子字符串-LK459"><a href="#重复的子字符串-LK459" class="headerlink" title="重复的子字符串:LK459"></a>重复的子字符串:LK459</h2><h3 id="代码:-1"><a href="#代码:-1" class="headerlink" title="代码:"></a>代码:</h3><figure class="highlight plaintext"><table><tbody><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">package org.example.技巧.KMP算法;</span><br><span class="line"></span><br><span class="line">public class num459 {</span><br><span class="line"> public boolean repeatedSubstringPattern(String s) {</span><br><span class="line"> //KMP算法</span><br><span class="line"> if (s.equals(""))return false;</span><br><span class="line"> int length = s.length();</span><br><span class="line"> char[] chars = s.toCharArray();</span><br><span class="line"> //初始化</span><br><span class="line"> int j = 0;</span><br><span class="line"> int[] next = new int[length];</span><br><span class="line"> next[0] = 0;</span><br><span class="line"> for (int i = 1; i < length; i++) {</span><br><span class="line"> //不相同</span><br><span class="line"> while (j > 0 && chars[i] != chars[j]){j = next[j - 1];}</span><br><span class="line"> //相同</span><br><span class="line"> if (chars[i] == chars[j]){j ++;}</span><br><span class="line"> //更新</span><br><span class="line"> next[i] = j;</span><br><span class="line"> }</span><br><span class="line"> if (next[length - 1] > 0 && length % (length - next[length - 1]) == 0){</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
</categories>
<tags>
<tag> KMP算法 </tag>
</tags>
</entry>
<entry>
<title>背包问题</title>
<link href="/2024/04/25/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E4%B9%8B%E8%83%8C%E5%8C%85/"/>
<url>/2024/04/25/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E4%B9%8B%E8%83%8C%E5%8C%85/</url>
<content type="html"><![CDATA[<h2 id="分割等和子集:LeetCode416"><a href="#分割等和子集:LeetCode416" class="headerlink" title="分割等和子集:LeetCode416"></a>分割等和子集:LeetCode416</h2><h3 id="首先,本题要求集合里能否出现总和为-sum-2-的子集。"><a href="#首先,本题要求集合里能否出现总和为-sum-2-的子集。" class="headerlink" title="首先,本题要求集合里能否出现总和为 sum / 2 的子集。"></a>首先,本题要求集合里能否出现总和为 sum / 2 的子集。</h3><ul><li>背包的体积为sum / 2</li><li>背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值</li><li>背包如果正好装满,说明找到了总和为 sum / 2 的子集。</li><li>背包中每一个元素是不可重复放入。</li></ul><h3 id="滚动数组法:"><a href="#滚动数组法:" class="headerlink" title="滚动数组法:"></a>滚动数组法:</h3><p>1.定义一个 dp[] 数组</p><p>2.dp[j]:表示容量为 j 的背包可以最大装下的值</p><p>3.确定遍历顺序</p><p>4.确定递归条件 dp[j] = max(dp[j], dp[j- weight[i]] + value[i] )</p><img src="/2024/04/25/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E4%B9%8B%E8%83%8C%E5%8C%85/chengyuchun/Desktop/Blogs/source/imgs/image-20240424211005472.jpg" alt="image-20240424211005472" style="zoom:150%;"><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK416 {</span><br><span class="line"> public boolean canPartition(int[] nums) {</span><br><span class="line"> int sum = 0;</span><br><span class="line"> for (int i = 0; i < nums.length; i++) {</span><br><span class="line"> sum += nums[i];</span><br><span class="line"> }</span><br><span class="line"> if (sum % 2 == 1){</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> int target = sum / 2;</span><br><span class="line"> //定义dp[]数组</span><br><span class="line"> int[] dp = new int[target + 1];</span><br><span class="line"> //初始化数组</span><br><span class="line"> dp[0] = 0;</span><br><span class="line"> //确定遍历顺序</span><br><span class="line"> for (int i = 0; i < nums.length; i++) {//物品 物品 i 的重量是 nums[i],其价值也是 nums[i]</span><br><span class="line"> for (int j = target; j >= nums[i]; j --){//背包 j >= nums[i]背包的容量要大于物品的重量</span><br><span class="line"> dp[j] = Math.max(dp[j], dp[j - nums[i]] + nums[i] );</span><br><span class="line"> }</span><br><span class="line"> //如果找到了可以去掉不必要的遍历</span><br><span class="line"> if (dp[target] == target){</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return dp[target] == target;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h3 id="二维数组法:"><a href="#二维数组法:" class="headerlink" title="二维数组法:"></a>二维数组法:</h3><h3 id="首先,本题要求集合里能否出现总和为-sum-2-的子集。-1"><a href="#首先,本题要求集合里能否出现总和为-sum-2-的子集。-1" class="headerlink" title="首先,本题要求集合里能否出现总和为 sum / 2 的子集。"></a>首先,本题要求集合里能否出现总和为 sum / 2 的子集。</h3><ul><li>背包的体积为sum / 2</li><li>背包要放入的商品(集合里的元素)重量为 元素的数值,价值也为元素的数值</li><li>背包如果正好装满,说明找到了总和为 sum / 2 的子集。</li><li>背包中每一个元素是不可重复放入。</li></ul><h3 id="滚动数组法:-1"><a href="#滚动数组法:-1" class="headerlink" title="滚动数组法:"></a>滚动数组法:</h3><p>1.定义一个 dp[][] 数组</p><p>2.dp[i][j]:表示容量为 j 的背包可以最大装下的值</p><p>3.确定遍历顺序</p><p>4.确定递归条件 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - weight[i]] + value[i]);</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">package org.example.动态规划;</span><br><span class="line"></span><br><span class="line">public class LK416_2 {</span><br><span class="line"> public boolean canPartition(int[] nums) {</span><br><span class="line"> //using 2-D DP array.</span><br><span class="line"> int len = nums.length;</span><br><span class="line"> //check edge cases;</span><br><span class="line"> if(len == 0)</span><br><span class="line"> return false;</span><br><span class="line"></span><br><span class="line"> int sum = 0;</span><br><span class="line"> for (int num : nums)</span><br><span class="line"> sum += num;</span><br><span class="line"> //we only deal with even numbers. If sum is odd, return false;</span><br><span class="line"> if(sum % 2 == 1)</span><br><span class="line"> return false;</span><br><span class="line"></span><br><span class="line"> int target = sum / 2;</span><br><span class="line"> int[][] dp = new int[nums.length][target + 1];</span><br><span class="line"> </span><br><span class="line"> //initialize dp array</span><br><span class="line"> for(int j = nums[0]; j <= target; j++){</span><br><span class="line"> dp[0][j] = nums[0];</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for(int i = 1; i < len; i++){</span><br><span class="line"> for(int j = 0; j <= target; j++){</span><br><span class="line"> if (j < nums[i])</span><br><span class="line"> dp[i][j] = dp[i - 1][j];</span><br><span class="line"> else</span><br><span class="line"> dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - nums[i]] + nums[i]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return dp[len - 1][target] == target;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>其中滚动数组他的背包遍历只能从后往前遍历</p>]]></content>
<categories>
<category> 动态规划 </category>
</categories>
<tags>
<tag> 动态规划 </tag>
</tags>
</entry>
<entry>
<title>day07</title>
<link href="/2024/04/24/day07/"/>
<url>/2024/04/24/day07/</url>
<content type="html"><![CDATA[<h2 id="反转字符串:LK334"><a href="#反转字符串:LK334" class="headerlink" title="反转字符串:LK334"></a>反转字符串:LK334</h2><p>双指针法:一个在左边,一个在右边,进行值的交换</p><img src="/2024/04/24/day07/image-20240504061124679.png" alt="image-20240504061124679" style="zoom:200%;"><p>代码:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">public class num344 {</span><br><span class="line"> public void reverseString(char[] s) {</span><br><span class="line"> int left = 0;</span><br><span class="line"> int right = s.length - 1;</span><br><span class="line"> while (left < right){</span><br><span class="line"> char leftChar = s[left];</span><br><span class="line"> char rightChar = s[right];</span><br><span class="line"> s[left] = rightChar;</span><br><span class="line"> s[right] = leftChar;</span><br><span class="line"> left ++;</span><br><span class="line"> right --;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="反转字符串-II:LK541"><a href="#反转字符串-II:LK541" class="headerlink" title="反转字符串 II:LK541"></a>反转字符串 II:LK541</h2><p>和翻转字符串 1 一样,只是这里的反转的组变成了 2 * K ,然后在 2 * K 中进行上述的操作!!!</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">public class num541 {</span><br><span class="line"> public String reverseStr(String s, int k) {</span><br><span class="line"> char[] ch = s.toCharArray();//转换成数组</span><br><span class="line"> for(int i = 0; i < ch.length; i += 2 * k){</span><br><span class="line"> int start = i;</span><br><span class="line"> //这里是判断尾数够不够k个来取决end指针的位置</span><br><span class="line"> int end = Math.min(ch.length - 1, start + k - 1);</span><br><span class="line"> //用异或运算反转 </span><br><span class="line"> while(start < end){</span><br><span class="line"> char temp = ch[start];</span><br><span class="line"> ch[start] = ch[end];</span><br><span class="line"> ch[end] = temp;</span><br><span class="line"> start++;</span><br><span class="line"> end--;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return new String(ch);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="替换数字:CR054"><a href="#替换数字:CR054" class="headerlink" title="替换数字:CR054"></a>替换数字:CR054</h2><p>没有什么技巧和难点!!!</p><p>代码:</p><p>import java.util.Scanner;</p><p>class Main {<br> public static void main(String[] args) {<br> Scanner in = new Scanner(System.in);<br> String s = in.nextLine();<br> StringBuilder sb = new StringBuilder();<br> for (int i = 0; i < s.length(); i++) {<br> if (Character.isDigit(s.charAt(i))) {<br> sb.append(“number”);<br> }else sb.append(s.charAt(i));<br> }<br> System.out.println(sb);<br> }<br>}</p><h2 id="反转字符串中的单词:LK151"><a href="#反转字符串中的单词:LK151" class="headerlink" title="反转字符串中的单词:LK151"></a>反转字符串中的单词:LK151</h2><ul><li>移除多余空格 : “the sky is blue”</li><li>字符串反转:”eulb si yks eht”</li><li>单词反转:”blue is sky the”</li><li>反转一次字符串</li><li>再反转一次单词即可</li></ul><p>代码:</p><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.数组字符串;</span><br><span class="line"></span><br><span class="line">public class num151 {</span><br><span class="line"> public String reverseWords(String s) {</span><br><span class="line"> // 1.去除首尾以及中间多余空格</span><br><span class="line"> StringBuilder sb = removeSpace(s);</span><br><span class="line"> // 2.反转整个字符串</span><br><span class="line"> reverseString(sb, 0, sb.length() - 1);</span><br><span class="line"> // 3.反转各个单词</span><br><span class="line"> reverseEachWord(sb);</span><br><span class="line"> return sb.toString();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private StringBuilder removeSpace(String s) {</span><br><span class="line"> int start = 0;</span><br><span class="line"> int end = s.length() - 1;</span><br><span class="line"> while (s.charAt(start) == ' ') start++;</span><br><span class="line"> while (s.charAt(end) == ' ') end--;</span><br><span class="line"> StringBuilder sb = new StringBuilder();</span><br><span class="line"> while (start <= end) {</span><br><span class="line"> char c = s.charAt(start);</span><br><span class="line"> if (c != ' ' || sb.charAt(sb.length() - 1) != ' ') {</span><br><span class="line"> sb.append(c);</span><br><span class="line"> }</span><br><span class="line"> start++;</span><br><span class="line"> }</span><br><span class="line"> return sb;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> /**</span><br><span class="line"> * 反转字符串指定区间[start, end]的字符</span><br><span class="line"> */</span><br><span class="line"> public void reverseString(StringBuilder sb, int start, int end) {</span><br><span class="line"> while (start < end) {</span><br><span class="line"> char temp = sb.charAt(start);</span><br><span class="line"> sb.setCharAt(start, sb.charAt(end));</span><br><span class="line"> sb.setCharAt(end, temp);</span><br><span class="line"> start++;</span><br><span class="line"> end--;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private void reverseEachWord(StringBuilder sb) {</span><br><span class="line"> int start = 0;</span><br><span class="line"> int end = 1;</span><br><span class="line"> int n = sb.length();</span><br><span class="line"> while (start < n) {</span><br><span class="line"> while (end < n && sb.charAt(end) != ' ') {</span><br><span class="line"> end++;</span><br><span class="line"> }</span><br><span class="line"> reverseString(sb, start, end - 1);</span><br><span class="line"> start = end + 1;</span><br><span class="line"> end = start + 1;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="左旋转字符:CR055"><a href="#左旋转字符:CR055" class="headerlink" title="左旋转字符:CR055"></a>左旋转字符:CR055</h2><p>和上面的反转字符中的单词类似:</p><p>代码:</p><pre><code>import java.util.Scanner;public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int n = Integer.parseInt(in.nextLine()); String s = in.nextLine(); int len = s.length(); //获取字符串长度 char[] chars = s.toCharArray(); reverseString(chars, 0, len - n - 1); //反转前一段字符串,此时的字符串首尾是0,len - n - 1 reverseString(chars, len - n, len - 1); //反转后一段字符串,此时的字符串首尾是len - n,len - 1 reverseString(chars, 0, len - 1); //反转整个字符串 System.out.println(chars);}public static void reverseString(char[] ch, int start, int end) { //异或法反转字符串,参照题目 344.反转字符串的解释 while (start < end) { ch[start] ^= ch[end]; ch[end] ^= ch[start]; ch[start] ^= ch[end]; start++; end--; }}}</code></pre><p>其中可以使用异或运算来进行反转:</p><p>public static void reverseString(char[] ch, int start, int end) {<br> //异或法反转字符串,参照题目 344.反转字符串的解释<br> while (start < end) {<br> ch[start] ^= ch[end];<br> ch[end] ^= ch[start];<br> ch[start] ^= ch[end];<br> start++;<br> end–;<br> }<br>}</p>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 哈希表 </category>
</categories>
<tags>
<tag> 哈希表 </tag>
</tags>
</entry>
<entry>
<title>day06</title>
<link href="/2024/04/23/day06/"/>
<url>/2024/04/23/day06/</url>
<content type="html"><![CDATA[<h2 id="四数相加:LK454"><a href="#四数相加:LK454" class="headerlink" title="四数相加:LK454"></a>四数相加:LK454</h2><p>分别将两个数组合并并进行两数相加的和等于0;</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">JAVA</span><br><span class="line">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">import java.util.HashMap;</span><br><span class="line"></span><br><span class="line">public class num454 {</span><br><span class="line"> public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {</span><br><span class="line"></span><br><span class="line"> HashMap<Integer, Integer> map = new HashMap<>();</span><br><span class="line"> int record = 0;</span><br><span class="line"> for (int num1 : nums1) {</span><br><span class="line"> for (int num2 : nums2) {</span><br><span class="line"> map.put(num1 + num2, map.getOrDefault(num1 + num2, 0) + 1);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (int num3 : nums3) {</span><br><span class="line"> for (int num4 : nums4) {</span><br><span class="line"> int target = 0 - (num3 + num4);</span><br><span class="line"> if (map.containsKey(target)){</span><br><span class="line"> record = record + map.getOrDefault(target, 0);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return record;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="赎金信:LK383"><a href="#赎金信:LK383" class="headerlink" title="赎金信:LK383"></a>赎金信:LK383</h2><p>用数组遍历字符串一加一减,得到ransomNote字符对应的位置是否还是1,是就magazine不含有相应的字符!!!!</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">JAVA</span><br><span class="line">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">public class num383 {</span><br><span class="line"> public boolean canConstruct(String ransomNote, String magazine) {</span><br><span class="line"> int[] record = new int[26];</span><br><span class="line"> for (int i = 0; i < ransomNote.length(); i++) {</span><br><span class="line"> record[ransomNote.charAt(i) - 'a'] ++;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (int i = 0; i < magazine.length(); i++) {</span><br><span class="line"> record[magazine.charAt(i) - 'a'] --;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> for (int chart : record) {</span><br><span class="line"> if (chart > 0){</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="三数之和:LK015"><a href="#三数之和:LK015" class="headerlink" title="三数之和:LK015"></a>三数之和:LK015</h2><p>通过双指针的移动来控制三数之和;</p><img src="https://code-thinking.cdn.bcebos.com/gifs/15.%E4%B8%89%E6%95%B0%E4%B9%8B%E5%92%8C.gif" alt="15.三数之和" style="zoom:200%;"><p>代码:</p><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Arrays;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class num015 {</span><br><span class="line"> public List<List<Integer>> threeSum(int[] nums) {</span><br><span class="line"> List<List<Integer>> result = new ArrayList<>();</span><br><span class="line"> Arrays.sort(nums);</span><br><span class="line"> // 找出a + b + c = 0</span><br><span class="line"> // a = nums[i], b = nums[left], c = nums[right]</span><br><span class="line"> for (int i = 0; i < nums.length; i++) {</span><br><span class="line"> // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了</span><br><span class="line"> if (nums[i] > 0) {</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> if (i > 0 && nums[i] == nums[i - 1]) { // 去重a</span><br><span class="line"> continue;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> int left = i + 1;</span><br><span class="line"> int right = nums.length - 1;</span><br><span class="line"> while (right > left) {</span><br><span class="line"> int sum = nums[i] + nums[left] + nums[right];</span><br><span class="line"> if (sum > 0) {</span><br><span class="line"> right--;</span><br><span class="line"> } else if (sum < 0) {</span><br><span class="line"> left++;</span><br><span class="line"> } else {</span><br><span class="line"> result.add(Arrays.asList(nums[i], nums[left], nums[right]));</span><br><span class="line"> // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重</span><br><span class="line"> while (right > left && nums[right] == nums[right - 1]) right--;</span><br><span class="line"> while (right > left && nums[left] == nums[left + 1]) left++;</span><br><span class="line"></span><br><span class="line"> right--;</span><br><span class="line"> left++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="四数之和:LK018"><a href="#四数之和:LK018" class="headerlink" title="四数之和:LK018"></a>四数之和:LK018</h2><p>和三数之和一样只是在里面再套一层循环来控制起始的数!</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.Arrays;</span><br><span class="line">import java.util.List;</span><br><span class="line"></span><br><span class="line">public class num018 {</span><br><span class="line"> public List<List<Integer>> fourSum(int[] nums, int target) {</span><br><span class="line"> List<List<Integer>> result = new ArrayList<>();</span><br><span class="line"> Arrays.sort(nums);</span><br><span class="line"> // 找出a + b + c = 0</span><br><span class="line"> // a = nums[i], b = nums[left], c = nums[right]</span><br><span class="line"> for (int i = 0; i < nums.length; i++) {</span><br><span class="line"> if (nums[i] > 0 && nums[i] > target){</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"> if (i > 0 && nums[i] == nums[i - 1]) { // 去重a,这里很细节,要先收集在去重,才能达到出去重复的数组</span><br><span class="line"> continue; // {-2,-1,-1,2,3}nums[i] == nums[i + 1] 会导致收集{-1,-1,2}不成功</span><br><span class="line"> }</span><br><span class="line"> for (int j = i + 1; j < nums.length; j++) {</span><br><span class="line"> // 排序之后如果第一个元素已经大于零,那么无论如何组合都不可能凑成三元组,直接返回结果就可以了</span><br><span class="line"> if (j > i + 1 && nums[j] == nums[j - 1]){</span><br><span class="line"> continue;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> int left = j + 1;</span><br><span class="line"> int right = nums.length - 1;</span><br><span class="line"> while (right > left) {</span><br><span class="line"> int sum = nums[i] + nums[left] + nums[right] + nums[j];</span><br><span class="line"> if (sum > target) {</span><br><span class="line"> right--;</span><br><span class="line"> } else if (sum < target) {</span><br><span class="line"> left++;</span><br><span class="line"> } else {</span><br><span class="line"> result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right]));</span><br><span class="line"> // 去重逻辑应该放在找到一个三元组之后,对b 和 c去重</span><br><span class="line"> while (right > left && nums[right] == nums[right - 1]) right--;</span><br><span class="line"> while (right > left && nums[left] == nums[left + 1]) left++;</span><br><span class="line"></span><br><span class="line"> right--;</span><br><span class="line"> left++;</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"> return result;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 哈希表 </category>
</categories>
<tags>
<tag> 哈希表 </tag>
</tags>
</entry>
<entry>
<title>day05</title>
<link href="/2024/04/22/day05/"/>
<url>/2024/04/22/day05/</url>
<content type="html"><![CDATA[<h2 id="有效的字母异位词-LK242"><a href="#有效的字母异位词-LK242" class="headerlink" title="有效的字母异位词:LK242"></a>有效的字母异位词:LK242</h2><p>利用HashMap 来存储对应字符的数值,一个字符串的字符 + 1,一个字符串的字符 - 1,将结果遍历存放入数组中,最后根据数组的值是否为 0 来进行判断</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">import java.util.Arrays;</span><br><span class="line">import java.util.HashMap;</span><br><span class="line"></span><br><span class="line">public class num242 {</span><br><span class="line"> public boolean isAnagram(String s, String t) {</span><br><span class="line"> int slen = s.length();</span><br><span class="line"> int tlen = t.length();</span><br><span class="line"> if (slen != tlen){</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> HashMap<Character , Integer> map = new HashMap<>();</span><br><span class="line"> for (int i = 0; i < slen; i++) {</span><br><span class="line"> char ss = s.charAt(i);</span><br><span class="line"> char tt = t.charAt(i);</span><br><span class="line"> map.put(ss, map.getOrDefault(ss, 0) + 1);</span><br><span class="line"> map.put(tt, map.getOrDefault(tt, 0) - 1);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> int[] res = new int[map.size()];</span><br><span class="line"> var ref = new Object() {</span><br><span class="line"> int i = 0;</span><br><span class="line"> };</span><br><span class="line"> map.forEach((key, value) -> {</span><br><span class="line"> Integer val = map.get(key);</span><br><span class="line"> //System.out.println(val);</span><br><span class="line"> res[ref.i] = val;</span><br><span class="line"> ref.i = ref.i + 1;</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> for (int j = 0; j < res.length; j++) {</span><br><span class="line"> System.out.println(res[j]);</span><br><span class="line"> if (res[j] != 0){</span><br><span class="line"> return false;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return true;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> String s = "anagram";</span><br><span class="line"> String t = "nagaram";</span><br><span class="line"> num242 num242 = new num242();</span><br><span class="line"> boolean anagram = num242.isAnagram(s, t);</span><br><span class="line"> System.out.println(anagram);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>其他版本代码:</p><figure class="highlight java"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 242. 有效的字母异位词 字典解法</span></span><br><span class="line"><span class="comment"> * 时间复杂度O(m+n) 空间复杂度O(1)</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isAnagram</span><span class="params">(String s, String t)</span> {</span><br><span class="line"> <span class="type">int</span>[] record = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">26</span>];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < s.length(); i++) {</span><br><span class="line"> record[s.charAt(i) - <span class="string">'a'</span>]++; <span class="comment">// 并不需要记住字符a的ASCII,只要求出一个相对数值就可以了</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < t.length(); i++) {</span><br><span class="line"> record[t.charAt(i) - <span class="string">'a'</span>]--;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> count: record) {</span><br><span class="line"> <span class="keyword">if</span> (count != <span class="number">0</span>) { <span class="comment">// record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符。</span></span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>; <span class="comment">// record数组所有元素都为零0,说明字符串s和t是字母异位词</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="两个数组的交集-LK349"><a href="#两个数组的交集-LK349" class="headerlink" title="两个数组的交集:LK349"></a>两个数组的交集:LK349</h2><p>和上一题差不多,多了一步去重:</p><p> if (! map.containsKey(nums1[i])){<br> map.put(nums1[i], map.getOrDefault(nums1[i], 0) + 1);<br> }</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">import java.util.ArrayList;</span><br><span class="line">import java.util.HashMap;</span><br><span class="line">import java.util.LinkedList;</span><br><span class="line"></span><br><span class="line">public class num349 {</span><br><span class="line"> public int[] intersection(int[] nums1, int[] nums2) {</span><br><span class="line"> int length1 = nums1.length;</span><br><span class="line"> int length2 = nums2.length;</span><br><span class="line"> HashMap<Integer, Integer> map = new HashMap<>();</span><br><span class="line"> for (int i = 0; i < length1; i++) {</span><br><span class="line"> if (! map.containsKey(nums1[i])){</span><br><span class="line"> map.put(nums1[i], map.getOrDefault(nums1[i], 0) + 1);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> for (int j = 0; j < length2; j++) {</span><br><span class="line"> if (map.containsKey(nums2[j])){</span><br><span class="line"> map.put(nums2[j], map.getOrDefault(nums2[j], 0) + 1);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> LinkedList<Integer> res = new LinkedList<>();</span><br><span class="line"> map.forEach((key, value) -> {</span><br><span class="line"> //System.out.println(value);</span><br><span class="line"> if (value >= 2){</span><br><span class="line"> res.add(key);</span><br><span class="line"> //System.out.println("---------------------------");</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> int[] result = new int[res.size()];</span><br><span class="line"> for (int i = 0; i < res.size(); i++) {</span><br><span class="line"> result[i] = res.get(i);</span><br><span class="line"> System.out.println(result[i]);</span><br><span class="line"> }</span><br><span class="line"> return result;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> int[] nums1 = {1,2,2,1};</span><br><span class="line"> int[] nums2 = {2,2};</span><br><span class="line"> num349 num349 = new num349();</span><br><span class="line"> int[] num = num349.intersection(nums1, nums2);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>其他版本代码:</p><figure class="highlight java"><table><tbody><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.HashSet;</span><br><span class="line"><span class="keyword">import</span> java.util.Set;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span>[] intersection(<span class="type">int</span>[] nums1, <span class="type">int</span>[] nums2) {</span><br><span class="line"> <span class="keyword">if</span> (nums1 == <span class="literal">null</span> || nums1.length == <span class="number">0</span> || nums2 == <span class="literal">null</span> || nums2.length == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>];</span><br><span class="line"> }</span><br><span class="line"> Set<Integer> set1 = <span class="keyword">new</span> <span class="title class_">HashSet</span><>();</span><br><span class="line"> Set<Integer> resSet = <span class="keyword">new</span> <span class="title class_">HashSet</span><>();</span><br><span class="line"> <span class="comment">//遍历数组1</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i : nums1) {</span><br><span class="line"> set1.add(i);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">//遍历数组2的过程中判断哈希表中是否存在该元素</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> i : nums2) {</span><br><span class="line"> <span class="keyword">if</span> (set1.contains(i)) {</span><br><span class="line"> resSet.add(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><br><span class="line"> <span class="keyword">return</span> resSet.stream().mapToInt(x -> x).toArray();</span><br><span class="line"> </span><br><span class="line"> <span class="comment">//方法2:另外申请一个数组存放setRes中的元素,最后返回数组</span></span><br><span class="line"> <span class="type">int</span>[] arr = <span class="keyword">new</span> <span class="title class_">int</span>[resSet.size()];</span><br><span class="line"> <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(<span class="type">int</span> i : resSet){</span><br><span class="line"> arr[j++] = i;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="快乐数:LK202"><a href="#快乐数:LK202" class="headerlink" title="快乐数:LK202"></a>快乐数:LK202</h2><p>求和的过程中,sum会重复出现,这对解题很重要!</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">package org.example.哈希;</span><br><span class="line"></span><br><span class="line">import java.util.HashSet;</span><br><span class="line"></span><br><span class="line">public class num202 {</span><br><span class="line"> public boolean isHappy(int n) {</span><br><span class="line"> HashSet<Integer> record = new HashSet<>();</span><br><span class="line"> while (n != 1 && !record.contains(n)){</span><br><span class="line"> record.add(n);</span><br><span class="line"> n = findNextNum(n);</span><br><span class="line"> }</span><br><span class="line"> return n == 1;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private int findNextNum(int n) {</span><br><span class="line"> int sum = 0;</span><br><span class="line"> while(n > 0){</span><br><span class="line"> int temp = n % 10;</span><br><span class="line"> sum += temp * temp;</span><br><span class="line"> n = n / 10;</span><br><span class="line"> }</span><br><span class="line"> return sum;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="两数之和:LK001"><a href="#两数之和:LK001" class="headerlink" title="两数之和:LK001"></a>两数之和:LK001</h2><p>通过 target - nums[i] 进行Map的一个映射关系</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><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">public int[] twoSum(int[] nums, int target) {</span><br><span class="line"> int[] res = new int[2];</span><br><span class="line"> if(nums == null || nums.length == 0){</span><br><span class="line"> return res;</span><br><span class="line"> }</span><br><span class="line"> Map<Integer, Integer> map = new HashMap<>();</span><br><span class="line"> for(int i = 0; i < nums.length; i++){</span><br><span class="line"> int temp = target - nums[i]; // 遍历当前元素,并在map中寻找是否有匹配的key</span><br><span class="line"> if(map.containsKey(temp)){</span><br><span class="line"> res[1] = i;</span><br><span class="line"> res[0] = map.get(temp);</span><br><span class="line"> break;</span><br><span class="line"> }</span><br><span class="line"> map.put(nums[i], i); // 如果没找到匹配对,就把访问过的元素和下标加入到map中</span><br><span class="line"> }</span><br><span class="line"> return res;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 哈希表 </category>
</categories>
<tags>
<tag> 哈希表 </tag>
</tags>
</entry>
<entry>
<title>day04</title>
<link href="/2024/04/20/day04/"/>
<url>/2024/04/20/day04/</url>
<content type="html"><![CDATA[<h2 id="两两交换链表:LK029"><a href="#两两交换链表:LK029" class="headerlink" title="两两交换链表:LK029"></a>两两交换链表:LK029</h2><p>比较明显且容易想到的就是递归:</p><p>1.确定函数及参数返回值</p><p>2.确定终止条件</p><p>3.确定下一轮递归的条件和值</p><p>代码:</p><figure class="highlight plaintext"><table><tbody><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">package org.example.链表;</span><br><span class="line"></span><br><span class="line">public class num024 {</span><br><span class="line"> public class ListNode {</span><br><span class="line"> int val;</span><br><span class="line"> ListNode next;</span><br><span class="line"> ListNode() {}</span><br><span class="line"> ListNode(int val) { this.val = val; }</span><br><span class="line"> ListNode(int val, ListNode next) { this.val = val; this.next = next; }</span><br><span class="line"> }</span><br><span class="line"> public ListNode swapPairs(ListNode head) {</span><br><span class="line"> // base case 退出提交</span><br><span class="line"> if(head == null || head.next == null) return head;</span><br><span class="line"> // 获取当前节点的下一个节点</span><br><span class="line"> ListNode next = head.next;</span><br><span class="line"> // 进行递归</span><br><span class="line"> ListNode newNode = swapPairs(next.next);</span><br><span class="line"> // 这里进行交换</span><br><span class="line"> next.next = head;</span><br><span class="line"> head.next = newNode;</span><br><span class="line"></span><br><span class="line"> return next;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="删除倒数第n个节点:"><a href="#删除倒数第n个节点:" class="headerlink" title="删除倒数第n个节点:"></a>删除倒数第n个节点:</h2><p>第一次的:通过遍历链表结合n确定删除的节点的位置</p><p>改进的:通过双指针法来</p><p>代码:(自己开始写的不能通过节点数为2的链表)</p><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.链表;</span><br><span class="line"></span><br><span class="line">public class num019 {</span><br><span class="line"> public class ListNode {</span><br><span class="line"> int val;</span><br><span class="line"> ListNode next;</span><br><span class="line"> ListNode() {}</span><br><span class="line"> ListNode(int val) { this.val = val; }</span><br><span class="line"> ListNode(int val, ListNode next) { this.val = val; this.next = next; }</span><br><span class="line"> }</span><br><span class="line"> public ListNode removeNthFromEnd(ListNode head, int n) {</span><br><span class="line"> ListNode dummyNode = new ListNode(0);</span><br><span class="line"> dummyNode.next = head;</span><br><span class="line"> //确定总共的节点个数</span><br><span class="line"> int count = 0;</span><br><span class="line"> ListNode p = head;</span><br><span class="line"> while (p != null){</span><br><span class="line"> count ++;</span><br><span class="line"> p = p.next;</span><br><span class="line"> }</span><br><span class="line"> if (count == 1){</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line"> //确定节点在链表的位置</span><br><span class="line"> int index = count - n;</span><br><span class="line"> ListNode prev = head;</span><br><span class="line"> for (int i = 1; i < index; i++) {</span><br><span class="line"> prev = prev.next;</span><br><span class="line"> }</span><br><span class="line"> //当前要删除的节点</span><br><span class="line"> ListNode cur = prev.next;</span><br><span class="line"> //将指针跳过删除节点就行</span><br><span class="line"> ListNode next = prev.next.next;</span><br><span class="line"> prev.next = next;</span><br><span class="line"> return head;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><p>代码:(改进版)</p><figure class="highlight java"><table><tbody><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> ListNode <span class="title function_">removeNthFromEnd</span><span class="params">(ListNode head, <span class="type">int</span> n)</span>{</span><br><span class="line"> <span class="type">ListNode</span> <span class="variable">dummyNode</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ListNode</span>(<span class="number">0</span>);</span><br><span class="line"> dummyNode.next = head;</span><br><span class="line"></span><br><span class="line"> <span class="type">ListNode</span> <span class="variable">fastIndex</span> <span class="operator">=</span> dummyNode;</span><br><span class="line"> <span class="type">ListNode</span> <span class="variable">slowIndex</span> <span class="operator">=</span> dummyNode;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 只要快慢指针相差 n 个结点即可</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < n ; i++){ </span><br><span class="line"> fastIndex = fastIndex.next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (fastIndex != <span class="literal">null</span>){</span><br><span class="line"> fastIndex = fastIndex.next;</span><br><span class="line"> slowIndex = slowIndex.next;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//此时 slowIndex 的位置就是待删除元素的前一个位置。</span></span><br><span class="line"> <span class="comment">//具体情况可自己画一个链表长度为 3 的图来模拟代码来理解</span></span><br><span class="line"> slowIndex.next = slowIndex.next.next;</span><br><span class="line"> <span class="keyword">return</span> dummyNode.next;</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure><h2 id="环形链表系列问题:LK142"><a href="#环形链表系列问题:LK142" class="headerlink" title="环形链表系列问题:LK142"></a>环形链表系列问题:LK142</h2><p>1.主要通过快慢指针来进行判断是否存在环</p><p>2.通过第一次相遇的位置得到数学逻辑求出入环口</p><figure class="highlight plaintext"><table><tbody><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></pre></td><td class="code"><pre><span class="line">package org.example.链表;</span><br><span class="line"></span><br><span class="line">public class num142 {</span><br><span class="line"> public class ListNode {</span><br><span class="line"> int val;</span><br><span class="line"> ListNode next;</span><br><span class="line"> ListNode() {}</span><br><span class="line"> ListNode(int val) { this.val = val; }</span><br><span class="line"> ListNode(int val, ListNode next) { this.val = val; this.next = next; }</span><br><span class="line"> }</span><br><span class="line"> public ListNode detectCycle(ListNode head) {</span><br><span class="line"> //运用快慢指针来进行判断是否存在环</span><br><span class="line"> //走两步一次</span><br><span class="line"> ListNode fast = head;</span><br><span class="line"> //走一步一次</span><br><span class="line"> ListNode slow = head;</span><br><span class="line"> while (fast != null && fast.next != null){</span><br><span class="line"> slow = slow.next;</span><br><span class="line"> fast = fast.next.next;</span><br><span class="line"> if (slow == fast) {// 有环</span><br><span class="line"> ListNode index1 = fast;</span><br><span class="line"> ListNode index2 = head;</span><br><span class="line"> // 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口</span><br><span class="line"> while (index1 != index2) {</span><br><span class="line"> index1 = index1.next;</span><br><span class="line"> index2 = index2.next;</span><br><span class="line"> }</span><br><span class="line"> return index1;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> return null;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></tbody></table></figure>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 链表 </category>
</categories>
<tags>
<tag> 链表 </tag>
<tag> 递归 </tag>
</tags>
</entry>
<entry>
<title>day03</title>
<link href="/2024/04/20/day03/"/>
<url>/2024/04/20/day03/</url>
<content type="html"><![CDATA[<p>day03<br>删除链表节点:LK203<br>删除节点:<br>1.单链表只改变删除节点上一个节点的指针,让它指向被删除的节点的下一个节点就行</p><p>2.双向链表只改变被删除节点上一个节点的next指针,让它指向被删除的节点的下一个节点,并且被删除的节点的下一个节点prev指针指向被删除节点上一个节点</p><p>注:可以使用哨兵节点来简化头节点的删除</p><p>代码:</p><p>package org.example.链表;</p><p>public class num203 {</p><pre><code>public class ListNode { int val; ListNode next; ListNode() {} ListNode(int val) { this.val = val; } ListNode(int val, ListNode next) { this.val = val; this.next = next; }}public ListNode removeElements(ListNode head, int val) { if (head == null){ return head; } ListNode s = new ListNode(0, head);//定义一个哨兵指向头结点 ListNode prev = s; ListNode curr = head; while (curr != null){ if (curr.val == val){ prev.next = curr.next; }else { prev = curr; } curr = curr.next; } return s.next;}</code></pre><p>}<br>设计链表:LK707<br>1.主要通过node = node.next在node != null 的情况下来实现链表的遍历</p><p>2.这题运用到了</p><p>for (int i = 0; i < index; i++) {//index - 1<br> pred = pred.next;<br>}<br>来定位删除节点的位置</p><p>代码:</p><p>package org.example.链表;</p><p>public class num707 {<br> class MyLinkedList {<br> public class ListNode {<br> int val;<br> ListNode next;<br> ListNode() {}<br> ListNode(int val) { this.val = val; }<br> ListNode(int val, ListNode next) { this.val = val; this.next = next; }<br> }</p><pre><code> int size; ListNode head; public MyLinkedList() { size = 0; head = new ListNode(0); } public int get(int index) { if (index < 0 || index >= size){ return -1; } ListNode cur = head; for (int i = 0; i <= index; i++) { cur = cur.next; } return cur.val; } public void addAtHead(int val) { addAtIndex(0, val); } public void addAtTail(int val) { addAtIndex(size, val); } public void addAtIndex(int index, int val) { if (index > size){ return; } if (index < 0){ index = 0; } size ++; //找到要插入节点的前驱 ListNode pred = head; for (int i = 0; i < index; i++) {//index - 1 pred = pred.next; } ListNode added = new ListNode(val); ListNode next = pred.next; pred.next = added; added.next = next; } public void deleteAtIndex(int index) { if (index > size || index < 0){ return; } //创建哨兵节点方便节点删除 ListNode s = new ListNode(-1, head); //找到要删除的节点的上一个节点 ListNode prevDeleted = head; for (int i = 0; i < index; i++) {//index - 1 prevDeleted = prevDeleted.next; } prevDeleted.next = prevDeleted.next.next; }}</code></pre><p>}<br>class ListNode{<br> int val;<br> ListNode next,prev;<br> ListNode() {};<br> ListNode(int val){<br> this.val = val;<br> }<br>}</p><p>class MyLinkedList { </p><pre><code>//记录链表中元素的数量int size;//记录链表的虚拟头结点和尾结点ListNode head,tail;public MyLinkedList() &#123; //初始化操作 this.size = 0; this.head = new ListNode(0); this.tail = new ListNode(0); //这一步非常关键,否则在加入头结点的操作中会出现null.next的错误!!! head.next=tail; tail.prev=head;&#125;public int get(int index) &#123; //判断index是否有效 if(index<0 || index>=size)&#123; return -1; &#125; ListNode cur = this.head; //判断是哪一边遍历时间更短 if(index >= size / 2)&#123; //tail开始 cur = tail; for(int i=0; i< size-index; i++)&#123; cur = cur.prev; &#125; &#125;else&#123; for(int i=0; i<= index; i++)&#123; cur = cur.next; &#125; &#125; return cur.val;&#125;public void addAtHead(int val) &#123; //等价于在第0个元素前添加 addAtIndex(0,val);&#125;public void addAtTail(int val) &#123; //等价于在最后一个元素(null)前添加 addAtIndex(size,val);&#125;public void addAtIndex(int index, int val) &#123; //index大于链表长度 if(index>size)&#123; return; &#125; //index小于0 if(index<0)&#123; index = 0; &#125; size++; //找到前驱 ListNode pre = this.head; for(int i=0; i<index; i++)&#123; pre = pre.next; &#125; //新建结点 ListNode newNode = new ListNode(val); newNode.next = pre.next; pre.next.prev = newNode; newNode.prev = pre; pre.next = newNode; &#125;public void deleteAtIndex(int index) &#123; //判断索引是否有效 if(index<0 || index>=size)&#123; return; &#125; //删除操作 size--; ListNode pre = this.head; for(int i=0; i<index; i++)&#123; pre = pre.next; &#125; pre.next.next.prev = pre; pre.next = pre.next.next;&#125;</code></pre><p>}<br>反转链表:LK206<br>在指针遍历推进并进行指针反向的过程中定义的临时节点很重要,主要用来记录下一个一个节点,可以类比于数组变量中定义的临时变量记录下一个数值的效果</p><p>代码:</p><p>package org.example.链表;</p><p>public class num206 {<br> public class ListNode {<br> int val;<br> ListNode next;<br> ListNode() {}<br> ListNode(int val) { this.val = val; }<br> ListNode(int val, ListNode next) { this.val = val; this.next = next; }<br> }<br> //双指针法暴力遍历反向<br> public ListNode reverseList(ListNode head) {<br> if (head == null){<br> return head;<br> }<br> ListNode curr = head;<br> ListNode temp = null;<br> ListNode prev = null;<br> while (curr != null){<br> temp= curr.next;//记录下一个节点<br> curr.next = prev;//指针反向<br> //向前推进遍历<br> prev =curr;<br> curr = temp;<br> }<br> return prev;<br> }<br>}<br>// 递归<br>class Solution {<br> public ListNode reverseList(ListNode head) {<br> return reverse(null, head);<br> }</p><pre><code>private ListNode reverse(ListNode prev, ListNode cur) &#123; if (cur == null) &#123; return prev; &#125; ListNode temp = null; temp = cur.next;// 先保存下一个节点 cur.next = prev;// 反转 // 更新prev、cur位置 // prev = cur; // cur = temp; return reverse(cur, temp);&#125;</code></pre><p>}<br>// 从后向前递归<br>class Solution {<br> ListNode reverseList(ListNode head) {<br> // 边缘条件判断<br> if(head == null) return null;<br> if (head.next == null) return head;</p><pre><code> // 递归调用,翻转第二个节点开始往后的链表 ListNode last = reverseList(head.next); // 翻转头节点与第二个节点的指向 head.next.next = head; // 此时的 head 节点为尾节点,next 需要指向 NULL head.next = null; return last;&#125; </code></pre><p>}</p>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 链表 </category>
</categories>
<tags>
<tag> 链表 </tag>
</tags>
</entry>
<entry>
<title>day02</title>
<link href="/2024/04/20/day02/"/>
<url>/2024/04/20/day02/</url>
<content type="html"><![CDATA[<p>day02<br>双指针法:LK997<br>通过数组两端的指针逐步向中间遍历:其中终止条件 while (right >= left) 不能少 ‘=’ 当临界条件 int[] nums = {1};这种情况少了的话会判断不了</p><p>代码:</p><p>package org.example.数组字符串;</p><p>public class num997 {<br> public int[] sortedSquares(int[] nums) {<br> //接收结果<br> int[] result = new int[nums.length];<br> int left = 0;<br> int right = nums.length - 1;<br> int resIndex = nums.length - 1;<br> while (right >= left){<br> int leftPow = nums[left] * nums[left];<br> int rightPow = nums[right] * nums[right];</p><pre><code> //左边的平方大于右边 if (leftPow > rightPow){ left ++; result[resIndex] = leftPow; }else { //右边的平方大于等于左边 result[resIndex] = rightPow; right --; } resIndex --; } return result;}public static void main(String[] args) { int[] nums = {-7,-3,2,3,11}; num997 pow = new num997(); int[] p = pow.sortedSquares(nums); for (int pp : p) { System.out.println(pp); }}</code></pre><p>}<br>滑动窗口法:LK209<br>这题我求取的是连续最小的子数组,但是这题是求取任意的最小子数组,看错变成了等于了</p><p>代码:</p><p>package org.example.数组字符串;</p><p>public class num209 {<br> //滑动窗口法<br> public int minSubArrayLen(int target, int[] nums) {<br> int result = Integer.MAX_VALUE;<br> int slow = 0;<br> int fast = 0;<br> int sum = nums[0];//记录窗口内的和与target进行比较<br> while (fast < nums.length){<br> //sum < target<br> if (sum < target){<br> if (fast != nums.length - 1){<br> fast ++;<br> sum += nums[fast];<br> }else {<br> break;<br> }<br> }<br> //sum > target<br> else if (sum > target){<br> sum -= nums[slow];<br> slow ++;<br> }else {<br> //sum = target<br> result = Math.min(result, fast - slow + 1);<br> if (fast != nums.length - 1){<br> fast ++;<br> sum += nums[fast];<br> }else {<br> break;<br> }<br> }<br> }<br> return result == Integer.MAX_VALUE ? 0 : result;<br> }</p><pre><code>public static void main(String[] args) { int[] nums = {1,2,3,4,5}; num209 num209 = new num209(); int res = num209.minSubArrayLen(11, nums); System.out.println(res);}</code></pre><p>}<br>最小子数组代码:</p><p>public int minSubArrayLen(int target, int[] nums) {<br> int result = Integer.MAX_VALUE;<br> int slow = 0;<br> int fast = 0;<br> int sum = nums[0];//记录窗口内的和与target进行比较<br> while (fast < nums.length){<br> //sum < target<br> if (sum < target){<br> if (fast != nums.length - 1){<br> fast ++;<br> sum += nums[fast];<br> }else {<br> break;<br> }<br> }<br> //sum > target<br> else if (sum > target){<br> result = Math.min(result, fast - slow + 1);<br> sum -= nums[slow];<br> slow ++;<br> }else {<br> //sum = target<br> result = Math.min(result, fast - slow + 1);<br> if (fast != nums.length - 1){<br> fast ++;<br> sum += nums[fast];<br> }else {<br> break;<br> }<br> }<br> }<br> return result == Integer.MAX_VALUE ? 0 : result;<br>}<br>public int minSubArrayLen(int s, int[] nums) {<br> int left = 0;<br> int sum = 0;<br> int result = Integer.MAX_VALUE;<br> for (int right = 0; right < nums.length; right++) {<br> sum += nums[right];<br> while (sum >= s) {<br> result = Math.min(result, right - left + 1);<br> sum -= nums[left++];<br> }<br> }<br> return result == Integer.MAX_VALUE ? 0 : result;<br>}<br>边界条件的合理处理:Lk059<br>这题难点就是终止条件以及边界条件左闭右开 [ ) 的一个判断比较难想到</p><p>代码:</p><p>package org.example.数组字符串;</p><p>public class num059 {<br> //循环不变量原则 左闭右开[ )<br> public int[][] generateMatrix(int n) {<br> int[][] nums = new int[n][n];<br> int startX = 0, startY = 0; // 每一圈的起始点<br> int offset = 1;<br> int count = 1; // 矩阵中需要填写的数字<br> int loop = 1; // 记录当前的圈数<br> int i, j; // j 代表列, i 代表行;</p><pre><code> while (loop <= n / 2) &#123; // 顶部 // 左闭右开,所以判断循环结束时, j 不能等于 n - offset for (j = startY; j < n - offset; j++) &#123; nums[startX][j] = count++; &#125; // 右列 // 左闭右开,所以判断循环结束时, i 不能等于 n - offset for (i = startX; i < n - offset; i++) &#123; nums[i][j] = count++; &#125; // 底部 // 左闭右开,所以判断循环结束时, j != startY for (; j > startY; j--) &#123; nums[i][j] = count++; &#125; // 左列 // 左闭右开,所以判断循环结束时, i != startX for (; i > startX; i--) &#123; nums[i][j] = count++; &#125; startX++; startY++; offset++; loop++; &#125; if (n % 2 == 1) &#123; // n 为奇数时,单独处理矩阵中心的值 nums[startX][startY] = count; &#125; return nums;&#125;</code></pre><p>}</p>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 数组与字符串 </category>
</categories>
<tags>
<tag> 双指针 </tag>
</tags>
</entry>
<entry>
<title>day01</title>
<link href="/2024/04/20/day01/"/>
<url>/2024/04/20/day01/</url>
<content type="html"><![CDATA[<p>day01<br>二分查找:LK704<br>1.主要注意点就是区间的选择</p><p>[ ] 或者 [ ) 其分别对应的终止条件是 right >= left 和 right > left</p><p>代码:</p><p>package org.example.二分查找;</p><p>import org.example.数组.LK704;<br>//二分搜索法</p><ul><li><p>总结:</p></li><li><p>1:[]左闭右闭的区间对应的条件right >= left 极限时[1,1]合法</p></li><li><p>2:[)左开右闭的区间对应的条件right > left 极限时[1,1)不合法所以要求rightindex > leftindex</p></li><li><p>二分查找要求区间合法,主要注意边界值和目标值的关系,要求边界包含目标,不然就返回 -1</p><p> public class num704 {<br> //输入: nums = [-1,0,3,5,9,12], target = 9<br> //输出: 4<br> //解释: 9 出现在 nums 中并且下标为 4<br> public int search(int[] nums, int target) {<br> //采用左闭右闭区间<br> int left = 0;<br> int length = nums.length;<br> int right = length - 1;<br> if (nums[left] > target || target > nums[right])<br> {<br> return -1;<br> }<br> while (right >= left ){<br> int middle = left + ((right - left) >> 1);//取中间值<br> //target在左区间<br> if (nums[middle] > target){<br> right = middle - 1;<br> }else if (target > nums[middle]){<br> //target在右区间<br> left = middle + 1;<br> }else {<br> //找到了target<br> return middle;<br> }<br> }<br> return -1;<br> }<br> public static void main(String[] args) {<br> int[]nums = {-1,0,3,5,9,12};<br> int target = 9;<br> num704 search = new num704();<br> int result = search.search(nums, target);<br> System.out.println(result);<br> }</p></li></ul><p>}</p><h2 id="快慢指针:LK027"><a href="#快慢指针:LK027" class="headerlink" title="快慢指针:LK027"></a>快慢指针:LK027</h2><h3 id="1-快指针进行遍历更新操作,而慢指针进行新数组的一个接收存储记录"><a href="#1-快指针进行遍历更新操作,而慢指针进行新数组的一个接收存储记录" class="headerlink" title="1.快指针进行遍历更新操作,而慢指针进行新数组的一个接收存储记录"></a>1.快指针进行遍历更新操作,而慢指针进行新数组的一个接收存储记录</h3><h3 id="代码:"><a href="#代码:" class="headerlink" title="代码:"></a>代码:</h3><pre><code>package org.example.二分查找;public class num027 {public int removeElement(int[] nums, int val) { //输入:nums = [3,2,2,3], val = 3 //输出:2, nums = [2,2] //数组元素只能覆盖不能真正删除 int slow = 0;//负责接收 int fast = 0;//负责去除及更新 int length = nums.length; for (int i = 0; i < length; i++) { if (nums[fast] != val){ nums[slow ++] = nums[fast]; } fast ++; } return slow;}}</code></pre>]]></content>
<categories>
<category> 代码随想录每日刷题 </category>
<category> 数组与字符串 </category>
</categories>
<tags>
<tag> 二分搜索法 </tag>
</tags>
</entry>
</search>