-
Notifications
You must be signed in to change notification settings - Fork 1
/
atom.xml
888 lines (551 loc) · 246 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
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
863
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>笑话人生</title>
<subtitle>年华易逝 懂得珍惜</subtitle>
<link href="https://www.cylong.com/atom.xml" rel="self"/>
<link href="https://www.cylong.com/"/>
<updated>2023-10-10T14:54:22.000Z</updated>
<id>https://www.cylong.com/</id>
<author>
<name>cylong</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>国内如何注册国外 Apple ID</title>
<link href="https://www.cylong.com/blog/2023/10/10/sign-up-apple-id/"/>
<id>https://www.cylong.com/blog/2023/10/10/sign-up-apple-id/</id>
<published>2023-10-10T14:54:22.000Z</published>
<updated>2023-10-10T14:54:22.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近喜欢玩 ChatGPT,奈何手机 IOS 版的 ChatGPT 没有上架中国大陆区 App Store,所以折腾下想要注册美国的 Apple ID,同时也方便下载一些其他有用的软件,比如 PayPal 和 Shadowrocket,话不多说,操作起来也是很简单的 👻</p><span id="more"></span><h1 id="如何注册"><a href="#如何注册" class="headerlink" title="如何注册"></a>如何注册</h1><ol><li>首先去 Apple 官网 <a href="https://appleid.apple.com/account" title="创建你的 Apple ID">创建你的 Apple ID</a>,这里 <code>国家或地区</code> 选择 <code>美国</code>,其他的填写你的真实信息,手机号可以填写中国大陆手机号,用于后续接收验证码,验证通过后你就获得了一个国外的 Apple ID ,但是还没结束。<img src="/blog/2023/10/10/sign-up-apple-id/创建你的-Apple-ID.png" class="" title="创建你的 Apple ID"></li><li>用你刚刚注册的 Apple ID 登录你的苹果设备,我用我的 iPad 做了小白鼠,第一次登录,有各种验证和同意条款等操作,确认即可。</li><li><code>设置</code> 里点击你的头像,找到 <code>付款与配送</code>,编辑配送地址(如果默认跳转到添加付款方式页面,回到上一页即可),这里要选择一个美国的地址,可以通过 <a href="https://www.meiguodizhi.com/" title="美国地址生成器">美国地址生成器</a>(链接打不开的话直接去谷歌或者百度搜索关键字)随机生成填入后保存。<img src="/blog/2023/10/10/sign-up-apple-id/编辑送货地址.jpg" class="" title="编辑送货地址"></li><li>接下来就可以尽情的在 App Store 里下载各种神秘的软件啦(第3步没设置的话,这里下载软件的时候也是会提示设置送货地址的)<img src="/blog/2023/10/10/sign-up-apple-id/下载ChatGPT.jpg" class="" title="下载ChatGPT"></li></ol><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><blockquote><p><a href="/blog/2023/10/08/sign-up-chatgpt/" title="国内如何注册 ChatGPT 账号 | 笑话人生">国内如何注册 ChatGPT 账号 | 笑话人生</a><br><a href="https://zhuanlan.zhihu.com/p/373675995" title="手把手教你注册国外apple ID(超简单) | 知乎">手把手教你注册国外apple ID(超简单) | 知乎</a><br><a href="https://imtoken.fans/t/topic/243" title="如何创建海外 Apple ID | imToken Fans">如何创建海外 Apple ID | imToken Fans</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近喜欢玩 ChatGPT,奈何手机 IOS 版的 ChatGPT 没有上架中国大陆区 App Store,所以折腾下想要注册美国的 Apple ID,同时也方便下载一些其他有用的软件,比如 PayPal 和 Shadowrocket,话不多说,操作起来也是很简单的 👻</p></summary>
<category term="Apple" scheme="https://www.cylong.com/categories/Apple/"/>
<category term="IOS" scheme="https://www.cylong.com/tags/IOS/"/>
<category term="ChatGPT" scheme="https://www.cylong.com/tags/ChatGPT/"/>
<category term="Apple" scheme="https://www.cylong.com/tags/Apple/"/>
</entry>
<entry>
<title>国内如何注册 ChatGPT 账号</title>
<link href="https://www.cylong.com/blog/2023/10/08/sign-up-chatgpt/"/>
<id>https://www.cylong.com/blog/2023/10/08/sign-up-chatgpt/</id>
<published>2023-10-08T08:30:47.000Z</published>
<updated>2023-10-08T08:30:47.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>OpenAI 公司是一家位于美国的人工智能研究与开发公司,成立于2015年,致力于推动人工智能技术的发展。该公司的使命是确保人工智能技术对整个人类社会的利益产生积极影响。OpenAI 在自然语言处理领域取得了重要突破,其中的一个产品就是 ChatGPT。<br>ChatGPT 是 OpenAI 开发的一款强大的自然语言处理模型。它可以理解和生成人类语言,能够回答问题、执行任务、进行对话等多种应用。ChatGPT 可以用于在线客服、智能助手、内容生成、教育支持等众多领域,为用户提供自然而流畅的文本交互体验。<br>OpenAI 不断改进和扩展 ChatGPT,以提供更准确、有用和安全的服务。这一技术的发展代表了人工智能在改善人们的生活、工作和沟通方面的潜力,并在多个领域产生了广泛的应用。</p><p>在国内并不支持 OpenAI 账号注册,多数会提示:<code>OpenAI's services are not available in your country</code>,查阅了一些资料后发现以下方式可以注册成功。</p><h1 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h1><ol><li>国外梯子,没有的话可以参考 <a href="/blog/2016/05/26/shadowsocks/" title="站在 Shadowsocks 的肩膀上发现精彩的世界 | 笑话人生">站在 Shadowsocks 的肩膀上发现精彩的世界</a></li><li>国外手机号,没有的话可以通过 <a href="https://sms-activate.org/" title="SMS-Activate是在线接收短信的虚拟号码服务">SMS-Activate</a> 购买一个</li></ol><span id="more"></span><h1 id="注册ChatGPT账号"><a href="#注册ChatGPT账号" class="headerlink" title="注册ChatGPT账号"></a>注册ChatGPT账号</h1><ol><li>访问 <a href="https://openai.com/" title="OpenAI">OpenAI</a> 右上角注册(建议使用国外IP),可以使用 Google、Microsoft、Apple 或者直接使用邮箱注册<img src="/blog/2023/10/08/sign-up-chatgpt/注册ChatGPT.png" class="" title="注册ChatGPT"></li><li>这里输入你国外的手机号,如果没有的话,参考下文注册 <a href="https://sms-activate.org/" title="SMS-Activate是在线接收短信的虚拟号码服务">SMS-Activate</a><img src="/blog/2023/10/08/sign-up-chatgpt/验证手机号.png" class="" title="验证手机号"></li><li>OpenAI 产品列表,选择 ChatGPT(DALL·E有兴趣可以玩玩,可以生成与文本描述相关的图像,收费)<img src="/blog/2023/10/08/sign-up-chatgpt/OpenAI产品列表.png" class="" title="OpenAI产品列表"></li><li>初体验 ChatGPT,免费的是 ChatGPT-3.5,想要使用 ChatGPT-4 需要 ChatGPT Plus,收费20$/月<img src="/blog/2023/10/08/sign-up-chatgpt/初体验ChatGPT.png" class="" title="初体验ChatGPT"></li></ol><h1 id="注册短信服务"><a href="#注册短信服务" class="headerlink" title="注册短信服务"></a>注册短信服务</h1><ol><li>打开 <a href="https://sms-activate.org/" title="SMS-Activate是在线接收短信的虚拟号码服务">SMS-Activate</a> 右上角注册登录,登录后右上角充值<img src="/blog/2023/10/08/sign-up-chatgpt/SMS-Activate充值.png" class="" title="SMS-Activate充值"></li><li>往下拉选择支付宝,最低充值2$(吐槽一下,之前没有最低充值额度的,估计是发现了商机😒)<img src="/blog/2023/10/08/sign-up-chatgpt/SMS-Activate支付宝充值.jpg" class="" title="SMS-Activate支付宝充值"></li><li>左侧服务选择搜索 <code>OpenAI</code><img src="/blog/2023/10/08/sign-up-chatgpt/SMS-Activate服务选择.jpg" class="" title="SMS-Activate服务选择"></li><li>国家选择印度并购买<img src="/blog/2023/10/08/sign-up-chatgpt/SMS-Activate国家选择.png" class="" title="SMS-Activate国家选择"></li><li>此时你就有了一个可以接收验证码的国外手机号,用于后续注册 ChatGPT 使用,此号码在20分钟有效期内并且未使用的情况下可以退款取消注册,如果注册 ChatGPT 过程中一直收不到验证码,可以多试几次,我是一次就成功了哈哈哈😁<img src="/blog/2023/10/08/sign-up-chatgpt/SMS-Activate号码.png" class="" title="SMS-Activate号码"></li></ol><h1 id="下载-ChatGPT-IOS-APP"><a href="#下载-ChatGPT-IOS-APP" class="headerlink" title="下载 ChatGPT IOS APP"></a>下载 ChatGPT IOS APP</h1><p>参考:<a href="/blog/2023/10/10/sign-up-apple-id/" title="国内如何注册国外 Apple ID | 笑话人生">国内如何注册国外 Apple ID | 笑话人生</a></p><h1 id="开通-ChatGPT-Plus"><a href="#开通-ChatGPT-Plus" class="headerlink" title="开通 ChatGPT Plus"></a>开通 ChatGPT Plus</h1><p>想要使用 ChatGPT-4 ,需要开通 Plus 订阅,网上找了些资料,还比较复杂,先挖个坑,待实操一遍再埋坑,可以先参考:</p><blockquote><p><a href="https://chatgpt-plus.github.io/chatgpt-plus/" title="国内开通 ChatGPT Plus 保姆级教程">国内开通 ChatGPT Plus 保姆级教程</a><br><a href="https://sites.google.com/view/chatgptcn/ChatGpt-upgrade-Plus-subscription" title="ChatGPT 怎样升级 Plus 订阅">ChatGPT 怎样升级 Plus 订阅</a></p></blockquote><h1 id="一些感想"><a href="#一些感想" class="headerlink" title="一些感想"></a>一些感想</h1><p>自从接触 ChatGPT 之后,平时写文章,写代码都会想到先问问 ChatGPT,确实能得到很多不错的答案。网络上各行各业的人使用后发出感叹,大部分人要失业了。我认为 ChatGPT 不会让大部分人失业,但是会让不会用 ChatGPT 的人失业。</p><img src="/blog/2023/10/08/sign-up-chatgpt/ChatGPT会让大部分人失业么.png" class="" title="ChatGPT会让大部分人失业么"><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><blockquote><p><a href="https://v2ex.com/t/900126" title="OpenAI 推出超神 ChatGPT 注册攻略来了 | V2EX">OpenAI 推出超神 ChatGPT 注册攻略来了 | V2EX</a><br><a href="https://cloud.tencent.com/developer/article/2192253" title="最近很火的 ChatGPT,带你注册体验全攻略 | 腾讯云开发者社区">最近很火的 ChatGPT,带你注册体验全攻略 | 腾讯云开发者社区</a><br><a href="https://readdevdocs.com/blog/tech/如何编程调用OpenAI的ChatGPT API接口.html" title="如何编程调用 OpenAI 的 ChatGPT API 接口 | Read dev Docs 博客">如何编程调用 OpenAI 的 ChatGPT API 接口 | Read dev Docs 博客</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>OpenAI 公司是一家位于美国的人工智能研究与开发公司,成立于2015年,致力于推动人工智能技术的发展。该公司的使命是确保人工智能技术对整个人类社会的利益产生积极影响。OpenAI 在自然语言处理领域取得了重要突破,其中的一个产品就是 ChatGPT。<br>ChatGPT 是 OpenAI 开发的一款强大的自然语言处理模型。它可以理解和生成人类语言,能够回答问题、执行任务、进行对话等多种应用。ChatGPT 可以用于在线客服、智能助手、内容生成、教育支持等众多领域,为用户提供自然而流畅的文本交互体验。<br>OpenAI 不断改进和扩展 ChatGPT,以提供更准确、有用和安全的服务。这一技术的发展代表了人工智能在改善人们的生活、工作和沟通方面的潜力,并在多个领域产生了广泛的应用。</p>
<p>在国内并不支持 OpenAI 账号注册,多数会提示:<code>OpenAI&#39;s services are not available in your country</code>,查阅了一些资料后发现以下方式可以注册成功。</p>
<h1 id="前期准备"><a href="#前期准备" class="headerlink" title="前期准备"></a>前期准备</h1><ol>
<li>国外梯子,没有的话可以参考 <a href="/blog/2016/05/26/shadowsocks/" title="站在 Shadowsocks 的肩膀上发现精彩的世界 | 笑话人生">站在 Shadowsocks 的肩膀上发现精彩的世界</a></li>
<li>国外手机号,没有的话可以通过 <a href="https://sms-activate.org/" title="SMS-Activate是在线接收短信的虚拟号码服务">SMS-Activate</a> 购买一个</li>
</ol></summary>
<category term="人工智能" scheme="https://www.cylong.com/categories/人工智能/"/>
<category term="人工智能" scheme="https://www.cylong.com/tags/人工智能/"/>
<category term="OpenAI" scheme="https://www.cylong.com/tags/OpenAI/"/>
<category term="ChatGPT" scheme="https://www.cylong.com/tags/ChatGPT/"/>
<category term="AIGC" scheme="https://www.cylong.com/tags/AIGC/"/>
<category term="AGI" scheme="https://www.cylong.com/tags/AGI/"/>
</entry>
<entry>
<title>2022届实习生-蚂蚁集团-支付宝事业线</title>
<link href="https://www.cylong.com/blog/2021/03/01/job-alipay/"/>
<id>https://www.cylong.com/blog/2021/03/01/job-alipay/</id>
<published>2021-03-01T03:35:35.000Z</published>
<updated>2021-03-01T03:35:35.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>看到学弟学妹都很热情的联系我,所以我整理了这个博客,方便给大家答疑,并同步招聘动态给大家。先自我介绍一下,我是南大软院17届毕业的,目前在支付宝就职,我叫陈云龙【花名奇策】,作为学长,可以根据自己亲身经历,给学弟学妹量身定做春招实习建议。大家有任何的疑问或者职业咨询,随时都可以联系我。</p><p>周围有其他感兴趣的小伙伴,都可以让他们联系我,哪怕不想投递这个部门,也可以听听学长的唠叨【实习经验、注意事项、简历建议、面试建议等】随时联系学长咨询各种实习内推问题呀~</p><p><strong>微信:cylong1016</strong>【加微信请备注姓名 学校 学历 学弟or学妹】</p><img src="/blog/2021/03/01/job-alipay/微信.jpg" class="" title="微信"><p><strong>PS:加学长微信,可以给你24小时的热心答疑服务!(o゚v゚)ノ</strong></p><h1 id="招聘招聘"><a href="#招聘招聘" class="headerlink" title="招聘招聘"></a>招聘招聘</h1><p>2022届蚂蚁集团-支付宝事业线-区域中台技术部招聘实习生啦(内推),如果有意愿来支付宝的,给自己一次机会简历速度砸过来吧,我帮助大家内推。<strong>后续可转正!</strong></p><p><strong>招聘对象:</strong> 2021.11 - 2022.10毕业的应届毕业生。<br><strong>招聘流程:</strong> 简历投递->在线笔试及测评->面试->发放实习offer->实习入职<br><strong>岗位类型:</strong> Java研发、数据研发、产品经理,欢迎加入钉钉群交流:31091227<br><strong>工作地点:</strong> 杭州、上海、成都<br><strong>简历请发送到邮箱:</strong> [email protected]<br><strong>注意简历命名格式:</strong> 姓名 学校 手机号 邮箱</p><p><strong>附:</strong> 投递简历后学长将把关简历,提供修改建议,并提供实习问题咨询及职业指导,简历快快砸过来~</p><span id="more"></span><h2 id="关于我们"><a href="#关于我们" class="headerlink" title="关于我们"></a>关于我们</h2><p>蚂蚁集团是中国最大的移动支付平台支付宝的母公司,也是全球领先的金融科技开放平台,致力于以科技推动包括金融服务业在内的全球现代服务业的数字化升级,携手合作伙伴为消费者和小微企业提供普惠、绿色、可持续的服务,为世界带来微小而美好的改变,用科技让普通人和小企业,享受平等的金融和生活服务。未来三年,支付宝将携手5万家服务商,帮助4000W服务业商家完成数字化升级。</p><p>我们区域中台技术部正式为了这一目标而诞生。核心团队来自蚂蚁两大超域的主架构。我们用工程、数据、算法、分布式架构等能力,为区域化作战提供:数字化的指挥决策系统、场景覆盖的销售及技术交付系统、区域化运营及招商技术。为推进百城的数字化提供完善的技术支持和保障。</p><p><strong>为什么加入我们</strong></p><ul><li>有数据:支付宝万亿级规模交易/营销数据。</li><li>有挑战:大型分布式系统和大数据应用场景。</li><li>有影响力:支付宝核心团队,各路领域专家和高手。</li><li>有成长:快速成长的通道,助力成长为技术领域大牛。</li></ul><p>期待你的加入,助力你成长为技术大牛!</p><h1 id="春招问题汇总"><a href="#春招问题汇总" class="headerlink" title="春招问题汇总"></a>春招问题汇总</h1><p>整理了下大家问我的问题,有漏的或者后续有其他问题也都可以随时联系我。</p><p><strong>Q:内推投了大概多久面试?内推系统什么时候开放?</strong><br>A:目前是收集简历阶段,内推系统会在3月8号开启,之前给我发简历的同学,我就会投递到系统中,这个时候你就会收到一个我(陈云龙)内推邮件的,你点击确认后就进入后续的春招笔试、面试流程。第一批笔试是3月9日开始,每周两次,之后也会有面试官联系你安排面试。</p><p><strong>Q:内推投递简历截止时间?</strong><br>A:校招系统内推投递通道全年开放,可以长期推荐,主要集中在3-4月。越早投递越有优势~不要因为练题等原因错过最佳投递时间,毕竟机会总是留给有准备的人。</p><p><strong>Q:内推同时只能投一个部门?</strong><br><del>A:是的,如果同时找多个学长学姐内推,投递多个部门,最后可能收到多个内推邮件,只能确认一个部门,只能走一个部门的面试流程。</del><br>A: 校招系统改版,每位同学投递,可以选择三个意向部门。系统默认会把首次内推该简历的同学所在的BG推荐为该学生第一志愿,学生保留修改的权力;学生可自主填写二志愿和三志愿。当前志愿不通过后,会通过一些条件及学生意愿,流转到下一个志愿。</p><p><strong>Q:内推没过的话还能继续投其他部门么?</strong><br><del>A:内推如果没通过,大家还是可以继续去阿里官网,投递其他部门的。(内推和官网申请各有一次机会)</del><br>A:参考上一条答疑,新系统上线后,就是一次投递三个部门了。不分内推和网申了。</p><p><strong>Q:新系统上线,哪些同学会收到填写意向的邮件?</strong></p><ol><li>此前已在官网投递,且处于“新投递”状态下的同学(可登录官网个人中心查看),请留意自己的邮箱,系统会发送意向填写邮件。</li><li>此前已经内推的同学,如果面试未通过,我们会发送意向邮件,同学可以补填第二和第三志愿。</li><li>目前已经被回绝的同学,也将收到填写意向的邮件,可以补填第二及第三志愿。</li></ol><p><strong>Q:实习待遇?</strong><br>A:我们会提供具有市场竞争力的薪酬(不会亏待大家滴),其他的福利如餐补、房补、交通补贴、入职酒店住宿等会根据同学的实际情况按照公司标准给到大家,具体的数字面试通过后HRG会和大家沟通~</p><p><strong>Q:什么时候实习?</strong><br>A:拿到实习Offer后,实习时间可以沟通,随时来实习。如果有意外情况无法来实习,后续也会有秋季校招直通面试(前面的面试结果都会保留)。</p><p><strong>Q:实习可能直接发正式Offer?</strong><br>A:实习后会有述职,评定是否发正式的校招Offer。</p><p><strong>Q:笔试题型是什么样的?有没有编程机试,限不限语言?笔试是统一的么?</strong><br>A:2道在线编程题,知识点为计算机学科基础知识点,如动态规划、字符串、数学问题等,不区分岗位,不区分语言。所有岗位每场考试考同一套题目。还有一块是测评,主要有一些计算、推理,考察逻辑思维等。</p><p><strong>Q:实习生会有同事带么?</strong><br>A:每个人入职都会指定专门的师兄带你,熟悉阿里的文化、生活、熟悉项目组业务,指导你工作上的事情,让你更快的提升自己。除了师兄以外,任何问题都可以找周围的同事,每个人都会愿意为你解答的~</p><p><strong>Q:有同学是海外读书,回国需要隔离,隔离期间能否远程实习?</strong><br>A:目前集团没有远程实习政策,后续是否有新的政策,或者其他特殊情况可以远程实习,可以拿到offer后和HR沟通。</p><p><strong>Q:要怎么查看面试进展?</strong><br>A:请进入<a href="https://campus.alibaba.com/index.htm" title="阿里巴巴校招官网">阿里巴巴校招官网</a>→个人中心查看。【3月8号开放】</p><p><strong>Q:还有N个无法解决的问题?</strong><br>A:有任何问题,可在工作日9:00-12:00、13:00-18:00期间拨打校招热线咨询:0571-81595981。也可咨询校园小蜜人工客服:校园招聘官网右侧→校园小蜜→人工客服</p><p><strong>你们可能的担心、疑虑:</strong></p><ol><li>如果后续不考虑来我们部门,可以不点我的内推确认邮件,就不会走春招流程;</li><li>如果想多些时间准备笔试、面试,那么笔试、面试时间也是可以自己决定的;投递简历、笔试、面试并没有截止时间,目前系统会一直开放的。想晚一点点击确认邮件,想晚一点笔试、想晚一点面试,都不会有影响,想给自己充分的准备时间,都可以由自己决定。但是建议大家在3月份,最晚4月份搞定这些事情,因为不清楚后面会不会有什么政策改变,比如部门人招满了,可能后续就不招了。越早投递越有优势~不要因为练题等原因错过最佳投递时间。</li><li>越早投递简历,越早内推,越早面试,越早拿offer,越早安心。</li><li>后续拿到offer,什么时候实习,也是可以自由决定的。</li></ol><p><strong>总结重点:</strong> 大家有简历现在就可以投递了!现在就可以投递了!现在就可以投递了!不会有任何的后顾之忧!简历后续也是可以随时更新的!</p><h1 id="经验之谈"><a href="#经验之谈" class="headerlink" title="经验之谈"></a>经验之谈</h1><ol><li>大学生实习是一个很好的机会,特别是大公司的实习,千万不要错过,首先实习是很容易拿到正式校招Offer的。如果后续想去其他公司,那么有实习经历,是很大的加分项。</li><li>机会总是给有准备的人,不要等待,要主动出击,先写好简历就是开始,接下来就可以准备,不知道准备啥,就可以看看你简历里写的啥,深入了解,很多面试官都会根据简历问你问题的。不知道简历写啥,可以看平时学校里做的项目(课内作业、课外项目,团队项目写写自己在项目中担任的角色,负责的模块),参加的竞赛,学习的课程知识,有没有发表过论文,比赛获得过什么奖,有没有自己的技术博客,参加过什么交流,讲座,开源项目等等。非技术相关的也可以写,比如社团,学生会的工作职责,参加某某公益,社会项目等等,有没有奖学金,读了哪些书。都可以写,有什么写什么(但是不要太多,太啰嗦,挑亮点写)。</li><li>面试也可以很快的提升自己,不要怕挂,也不要担心其他的,无论通过与否,都会积累很有价值的经验,也能快速的发现自己的不足。</li><li>自信点,大家都是从学校出来的,你可能觉得,要学的太多了,这也不会,那也不会,学无止境,大学不可能都被你学完,学会的。只要认真准备,认真学习了,把大学里的知识学会,就已经超越很多人了。</li><li>很多同学都会问面试可能都面试些什么,我虽然没有做过面试官,但是我被面试过啊,可以跟大家分享下。<br> a. 基础的信息,比如成绩、竞赛、奖学金、社团、学生会、课外实践,这些面试官可能会简单提及下,确认下实际情况(这个可能是HR面试官会问)<br> b. 技术问题就是一些基础知识、由浅入深,就看你都掌握程度。简历里写的优先深入了解。面试官可能还会扩散问,这个时候就看自己知识的广度和深度了。<br> c. 项目面试都话,会问你项目为了解决什么问题,在项目中担任什么角色,负责什么部分,用了什么技术,过程中遇到了什么困难,怎么解决的,最后这个项目做完,有哪些收获总结,自己得到了哪些成长。<br> d. 自己的优点,缺点,自己的价值,自己对未来的规划。<br> e. 最后有没有什么问题要问面试官。</li><li>知乎上有几篇下如何写简历的文章感觉可借鉴,可以参考:<blockquote><p><a href="https://www.zhihu.com/question/26265144">https://www.zhihu.com/question/26265144</a><br><a href="https://www.zhihu.com/question/23734172">https://www.zhihu.com/question/23734172</a></p></blockquote></li></ol><h1 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h1><p>祝愿学弟学妹们都能拿到自己满意的实习Offer(ง •_•)ง</p><p>本文档也会不断的更新,欢迎大家推荐给周围的小伙伴呀(●ˇ∀ˇ●)</p><hr>]]></content>
<summary type="html"><hr>
<h1 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h1><p>看到学弟学妹都很热情的联系我,所以我整理了这个博客,方便给大家答疑,并同步招聘动态给大家。先自我介绍一下,我是南大软院17届毕业的,目前在支付宝就职,我叫陈云龙【花名奇策】,作为学长,可以根据自己亲身经历,给学弟学妹量身定做春招实习建议。大家有任何的疑问或者职业咨询,随时都可以联系我。</p>
<p>周围有其他感兴趣的小伙伴,都可以让他们联系我,哪怕不想投递这个部门,也可以听听学长的唠叨【实习经验、注意事项、简历建议、面试建议等】随时联系学长咨询各种实习内推问题呀~</p>
<p><strong>微信:cylong1016</strong>【加微信请备注姓名 学校 学历 学弟or学妹】</p>
<img src="/blog/2021/03/01/job-alipay/微信.jpg" class="" title="微信">
<p><strong>PS:加学长微信,可以给你24小时的热心答疑服务!(o゚v゚)ノ</strong></p>
<h1 id="招聘招聘"><a href="#招聘招聘" class="headerlink" title="招聘招聘"></a>招聘招聘</h1><p>2022届蚂蚁集团-支付宝事业线-区域中台技术部招聘实习生啦(内推),如果有意愿来支付宝的,给自己一次机会简历速度砸过来吧,我帮助大家内推。<strong>后续可转正!</strong></p>
<p><strong>招聘对象:</strong> 2021.11 - 2022.10毕业的应届毕业生。<br><strong>招聘流程:</strong> 简历投递-&gt;在线笔试及测评-&gt;面试-&gt;发放实习offer-&gt;实习入职<br><strong>岗位类型:</strong> Java研发、数据研发、产品经理,欢迎加入钉钉群交流:31091227<br><strong>工作地点:</strong> 杭州、上海、成都<br><strong>简历请发送到邮箱:</strong> [email protected]<br><strong>注意简历命名格式:</strong> 姓名 学校 手机号 邮箱</p>
<p><strong>附:</strong> 投递简历后学长将把关简历,提供修改建议,并提供实习问题咨询及职业指导,简历快快砸过来~</p></summary>
<category term="招聘" scheme="https://www.cylong.com/categories/招聘/"/>
<category term="招聘" scheme="https://www.cylong.com/tags/招聘/"/>
<category term="实习生" scheme="https://www.cylong.com/tags/实习生/"/>
</entry>
<entry>
<title>求根到叶子节点数字之和</title>
<link href="https://www.cylong.com/blog/2020/10/29/sum-root-to-leaf-numbers/"/>
<id>https://www.cylong.com/blog/2020/10/29/sum-root-to-leaf-numbers/</id>
<published>2020-10-29T14:52:29.000Z</published>
<updated>2020-10-29T14:52:29.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。<br>例如,从根到叶子节点路径 1->2->3 代表数字 123。计算从根到叶子节点生成的所有数字之和。</p><p><strong>说明:</strong> 叶子节点是指没有子节点的节点。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">输入: [1, 2, 3]</span><br><span class="line"> 1</span><br><span class="line"> / \</span><br><span class="line"> 2 3</span><br><span class="line">输出: 25</span><br><span class="line">解释:</span><br><span class="line">从根到叶子节点路径 1->2 代表数字 12.</span><br><span class="line">从根到叶子节点路径 1->3 代表数字 13.</span><br><span class="line">因此,数字总和 = 12 13 = 25.</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">输入: [4, 9, 0, 5, 1]</span><br><span class="line"> 4</span><br><span class="line"> / \</span><br><span class="line"> 9 0</span><br><span class="line"> / \</span><br><span class="line">5 1</span><br><span class="line">输出: 1026</span><br><span class="line">解释:</span><br><span class="line">从根到叶子节点路径 4->9->5 代表数字 495.</span><br><span class="line">从根到叶子节点路径 4->9->1 代表数字 491.</span><br><span class="line">从根到叶子节点路径 4->0 代表数字 40.</span><br><span class="line">因此,数字总和 = 495 491 40 = 1026.</span><br></pre></td></tr></table></figure><span id="more"></span><h1 id="深度优先搜索"><a href="#深度优先搜索" class="headerlink" title="深度优先搜索"></a>深度优先搜索</h1><p>此题中,每个节点都对应一个 0-9 的数字,每条从根节点到叶子节点的路径都代表一个数字。我们只要通过深度优先搜索加回溯算法,求出所有路径组成的数字,再将所有数字相加求和即可。具体的,从根节点开始,遍历每个节点,如果遇到叶子节点,则将组成的数字保存,并进行回溯。如果不是叶子节点,则递归遍历子节点构造数字。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">List<List<Integer>> res = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line">LinkedList<Integer> item = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">sumNumbers</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> dfsBuildNumbers(root);</span><br><span class="line"> <span class="type">int</span> <span class="variable">sum</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (List<Integer> list : res) {</span><br><span class="line"> sum = parseInt(list);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dfsBuildNumbers</span><span class="params">(TreeNode node)</span> {</span><br><span class="line"> <span class="keyword">if</span> (node == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> item.add(node.val);</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"> res.add(<span class="keyword">new</span> <span class="title class_">LinkedList</span><>(item));</span><br><span class="line"> }</span><br><span class="line"> dfsBuildNumbers(node.left);</span><br><span class="line"> dfsBuildNumbers(node.right);</span><br><span class="line"> item.removeLast();</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_">parseInt</span><span class="params">(List<Integer> list)</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="keyword">for</span> (<span class="type">int</span> n : list) {</span><br><span class="line"> res = res * <span class="number">10</span> n;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>其实,每个节点都对应一个数字,等于其父节点对应的数字乘以 10 再加上该节点的值(这里假设根节点的父节点对应的数字是 0)。只要计算出每个叶子节点对应的数字,然后计算所有叶子节点对应的数字之和,即可得到结果。可以通过深度优先搜索实现。</p><img src="/blog/2020/10/29/sum-root-to-leaf-numbers/number.png" class="" title="图解"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">sumNumbers</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> <span class="keyword">return</span> dfs(root, <span class="number">0</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">dfs</span><span class="params">(TreeNode root, <span class="type">int</span> prevSum)</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">sum</span> <span class="operator">=</span> prevSum * <span class="number">10</span> root.val;</span><br><span class="line"> <span class="keyword">if</span> (root.left == <span class="literal">null</span> && root.right == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> sum;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> dfs(root.left, sum) dfs(root.right, sum);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),其中 n 是二叉树的节点个数。对每个节点访问一次。</li><li>空间复杂度:O(n),其中 n 是二叉树的节点个数。空间复杂度主要取决于递归调用的栈空间,递归栈的深度等于二叉树的高度,最坏情况下,二叉树的高度等于节点个数,空间复杂度为 O(n)。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/" title="求根到叶子节点数字之和 | 力扣(LeetCode)">求根到叶子节点数字之和 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/sum-root-to-leaf-numbers/solution/qiu-gen-dao-xie-zi-jie-dian-shu-zi-zhi-he-by-leetc/" title="求根到叶子节点数字之和 | 题解(LeetCode)">求根到叶子节点数字之和 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。<br>例如,从根到叶子节点路径 1-&gt;2-&gt;3 代表数字 123。计算从根到叶子节点生成的所有数字之和。</p>
<p><strong>说明:</strong> 叶子节点是指没有子节点的节点。</p>
<p><strong>示例 1:</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">输入: [1, 2, 3]</span><br><span class="line"> 1</span><br><span class="line"> / \</span><br><span class="line"> 2 3</span><br><span class="line">输出: 25</span><br><span class="line">解释:</span><br><span class="line">从根到叶子节点路径 1-&gt;2 代表数字 12.</span><br><span class="line">从根到叶子节点路径 1-&gt;3 代表数字 13.</span><br><span class="line">因此,数字总和 = 12 13 = 25.</span><br></pre></td></tr></table></figure>
<p><strong>示例 2:</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">输入: [4, 9, 0, 5, 1]</span><br><span class="line"> 4</span><br><span class="line"> / \</span><br><span class="line"> 9 0</span><br><span class="line"> / \</span><br><span class="line">5 1</span><br><span class="line">输出: 1026</span><br><span class="line">解释:</span><br><span class="line">从根到叶子节点路径 4-&gt;9-&gt;5 代表数字 495.</span><br><span class="line">从根到叶子节点路径 4-&gt;9-&gt;1 代表数字 491.</span><br><span class="line">从根到叶子节点路径 4-&gt;0 代表数字 40.</span><br><span class="line">因此,数字总和 = 495 491 40 = 1026.</span><br></pre></td></tr></table></figure></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="回溯算法" scheme="https://www.cylong.com/tags/回溯算法/"/>
<category term="树" scheme="https://www.cylong.com/tags/树/"/>
<category term="二叉树" scheme="https://www.cylong.com/tags/二叉树/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
</entry>
<entry>
<title>二叉树的前序遍历</title>
<link href="https://www.cylong.com/blog/2020/10/27/binary-tree-preorder-traversal/"/>
<id>https://www.cylong.com/blog/2020/10/27/binary-tree-preorder-traversal/</id>
<published>2020-10-27T15:15:09.000Z</published>
<updated>2020-10-27T15:15:09.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给你二叉树的根节点 root ,返回它节点值的 前序 遍历。</p><p><strong>示例 1:</strong></p><img src="/blog/2020/10/27/binary-tree-preorder-traversal/inorder.jpg" class="" title="前序遍历"><blockquote><p>输入:root = [1, null, 2, 3]<br>输出:[1, 2, 3]</p></blockquote><p><strong>示例 2:</strong></p><blockquote><p>输入:root = []<br>输出:[]</p></blockquote><span id="more"></span><h1 id="递归与非递归版本"><a href="#递归与非递归版本" class="headerlink" title="递归与非递归版本"></a>递归与非递归版本</h1><p>前序遍历的输出顺序就是根节点 -> 左子树 -> 右子树。前序遍历是先输出根节点的值,再去递归的输出左子树和右子树。整个遍历过程就是递归的性质,我们可以直接使用递归来完成计算。非递归版本其实是等阶的,只是我们将递归的栈显示的表达出来。下面是递归的版本解法,非递归的解法和二叉树的其他遍历方式可以参考我的另外一篇博客:<a href="/blog/2020/09/02/binary-tree-traverse/" title="二叉树的遍历">二叉树的遍历</a></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">List<Integer> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> List<Integer> <span class="title function_">preorderTraversal</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> dfsPreOrder(root);</span><br><span class="line"> <span class="keyword">return</span> ans;</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_">dfsPreOrder</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><br><span class="line"> }</span><br><span class="line"> ans.add(root.val);</span><br><span class="line"> dfsPreOrder(root.left);</span><br><span class="line"> dfsPreOrder(root.right);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),其中 n 是二叉树的节点数。每一个节点恰好被遍历一次。</li><li>空间复杂度:O(n),为迭代过程中显式栈的开销,平均情况下为 O(logn),最坏情况下树呈现链状,为 O(n)。</li></ul><h1 id="Morris-遍历"><a href="#Morris-遍历" class="headerlink" title="Morris 遍历"></a>Morris 遍历</h1><p>所谓再简单的题,通过看官方的题解,总能发现惊喜。有一种巧妙的方法可以在线性时间内,只占用常数空间来实现前序遍历。这种方法由 J. H. Morris 在 1979 年的论文「Traversing Binary Trees Simply and Cheaply」中首次提出,因此被称为 Morris 遍历。</p><p>Morris 遍历的核心思想是利用树的大量空闲指针,实现空间开销的极限缩减。其前序遍历规则总结如下:</p><ol><li>新建临时节点,令该节点为 root;</li><li>如果当前节点的左子节点为空,将当前节点加入答案,并遍历当前节点的右子节点;</li><li>如果当前节点的左子节点不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点:</li><li>如果前驱节点的右子节点为空,将前驱节点的右子节点设置为当前节点。然后将当前节点加入答案,并将前驱节点的右子节点更新为当前节点。当前节点更新为当前节点的左子节点。</li><li>如果前驱节点的右子节点为当前节点,将它的右子节点重新设为空。当前节点更新为当前节点的右子节点。</li><li>重复步骤 2 和步骤 3,直到遍历结束。</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> List<Integer> <span class="title function_">preorderTraversal</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> List<Integer> res = <span class="keyword">new</span> <span class="title class_">ArrayList</span><Integer>();</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> res;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">p1</span> <span class="operator">=</span> root, p2 = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">while</span> (p1 != <span class="literal">null</span>) {</span><br><span class="line"> p2 = p1.left;</span><br><span class="line"> <span class="keyword">if</span> (p2 != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">while</span> (p2.right != <span class="literal">null</span> && p2.right != p1) {</span><br><span class="line"> p2 = p2.right;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (p2.right == <span class="literal">null</span>) {</span><br><span class="line"> res.add(p1.val);</span><br><span class="line"> p2.right = p1;</span><br><span class="line"> p1 = p1.left;</span><br><span class="line"> <span class="keyword">continue</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> p2.right = <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> res.add(p1.val);</span><br><span class="line"> }</span><br><span class="line"> p1 = p1.right;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),其中 n 是二叉树的节点数。没有左子树的节点只被访问一次,有左子树的节点被访问两次。</li><li>空间复杂度:O(1)。只操作已经存在的指针(树的空闲指针),因此只需要常数的额外空间。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/binary-tree-preorder-traversal/" title="二叉树的前序遍历 | 力扣(LeetCode)">二叉树的前序遍历 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/binary-tree-preorder-traversal/solution/er-cha-shu-de-qian-xu-bian-li-by-leetcode-solution/" title="二叉树的前序遍历 | 题解(LeetCode)">二叉树的前序遍历 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给你二叉树的根节点 root ,返回它节点值的 前序 遍历。</p>
<p><strong>示例 1:</strong></p>
<img src="/blog/2020/10/27/binary-tree-preorder-traversal/inorder.jpg" class="" title="前序遍历">
<blockquote>
<p>输入:root = [1, null, 2, 3]<br>输出:[1, 2, 3]</p>
</blockquote>
<p><strong>示例 2:</strong></p>
<blockquote>
<p>输入:root = []<br>输出:[]</p>
</blockquote></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="树" scheme="https://www.cylong.com/tags/树/"/>
<category term="二叉树" scheme="https://www.cylong.com/tags/二叉树/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
</entry>
<entry>
<title>划分字母区间</title>
<link href="https://www.cylong.com/blog/2020/10/22/partition-labels/"/>
<id>https://www.cylong.com/blog/2020/10/22/partition-labels/</id>
<published>2020-10-22T15:08:50.000Z</published>
<updated>2020-10-22T15:08:50.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。</p><p><strong>示例:</strong></p><blockquote><p>输入:S = “ababcbacadefegdehijhklij”<br>输出:[9, 7, 8]<br>解释:<br>划分结果为 “ababcbaca”, “defegde”, “hijhklij”。<br>每个字母最多出现在一个片段中。像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。</p></blockquote><p><strong>提示:</strong></p><ul><li>S的长度在 [1, 500] 之间。</li><li>S只包含小写字母 ‘a’ 到 ‘z’。</li></ul><span id="more"></span><h1 id="贪心算法"><a href="#贪心算法" class="headerlink" title="贪心算法"></a>贪心算法</h1><p>由于同一个字母只能出现在同一个片段,显然同一个字母的第一次出现的下标位置和最后一次出现的下标位置必须出现在同一个片段。我们从第一个字母开始遍历,初始的时候,我们认为划分的字符串就是当前字母,即 <code>maxLen = 1</code>,然后我们求当前字母的最后一次出现的下标 index。并更新当前划分的字符串最长长度为 <code>maxLen = Math.max(index 1, maxLen)</code>。遍历的截止条件就是 <code>i < maxLen</code>。说明已经满足了题目条件。接下来,我们只要递归的处理剩下的字符串即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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">List<Integer> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> List<Integer> <span class="title function_">partitionLabels</span><span class="params">(String S)</span> {</span><br><span class="line"> <span class="keyword">if</span> (S == <span class="literal">null</span> || S.length() == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> <span class="variable">maxLen</span> <span class="operator">=</span> <span class="number">1</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 < maxLen; i ) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> S.lastIndexOf(S.charAt(i));</span><br><span class="line"> maxLen = Math.max(index <span class="number">1</span>, maxLen);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ans.add(maxLen);</span><br><span class="line"> partitionLabels(S.substring(maxLen));</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>参考官方的解法后发现,其实以上代码有几处可以优化的点,首先就是,我们可以先遍历一遍字符串,求出每个字符最后一次出现的下标位置。在得到每个字母最后一次出现的下标位置之后,可以使用贪心算法和双指针的方法将字符串划分为尽可能多的片段,具体做法如下。</p><ul><li>从左到右遍历字符串,遍历的同时维护当前片段的开始下标 start 和结束下标 end,初始时 <code>start = end = 0</code>。</li><li>对于每个访问到的字母 c,得到当前字母的最后一次出现的下标位置 end_c,则当前片段的结束下标一定不会小于 end_c,因此令 <code>end = max(end, end_c)</code>。</li><li>当访问到下标 end 时,当前片段访问结束,当前片段的下标范围是 [start, end],长度为 <code>end − start 1</code>,将当前片段的长度添加到返回值,然后令 <code>start = end 1</code>,继续寻找下一个片段。</li><li>重复上述过程,直到遍历完字符串。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> List<Integer> <span class="title function_">partitionLabels</span><span class="params">(String S)</span> {</span><br><span class="line"> <span class="type">int</span>[] last = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">26</span>];</span><br><span class="line"> <span class="type">int</span> <span class="variable">length</span> <span class="operator">=</span> S.length();</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 < length; i ) {</span><br><span class="line"> last[S.charAt(i) - <span class="string">'a'</span>] = i;</span><br><span class="line"> }</span><br><span class="line"> List<Integer> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><Integer>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">end</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> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < length; i ) {</span><br><span class="line"> end = Math.max(end, last[S.charAt(i) - <span class="string">'a'</span>]);</span><br><span class="line"> <span class="keyword">if</span> (i == end) {</span><br><span class="line"> ans.add(end - start <span class="number">1</span>);</span><br><span class="line"> start = end <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),其中 n 是字符串的长度。需要遍历字符串两次,第一次遍历时记录每个字母最后一次出现的下标位置,第二次遍历时进行字符串的划分。</li><li>空间复杂度:O(Σ),其中 Σ 是字符串中的字符集大小。这道题中,字符串只包含小写字母,因此 Σ = 26。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/partition-labels/" title="划分字母区间 | 力扣(LeetCode)">划分字母区间 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/partition-labels/solution/hua-fen-zi-mu-qu-jian-by-leetcode-solution/" title="划分字母区间 | 题解(LeetCode)">划分字母区间 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。</p>
<p><strong>示例:</strong></p>
<blockquote>
<p>输入:S = “ababcbacadefegdehijhklij”<br>输出:[9, 7, 8]<br>解释:<br>划分结果为 “ababcbaca”, “defegde”, “hijhklij”。<br>每个字母最多出现在一个片段中。像 “ababcbacadefegde”, “hijhklij” 的划分是错误的,因为划分的片段数较少。</p>
</blockquote>
<p><strong>提示:</strong></p>
<ul>
<li>S的长度在 [1, 500] 之间。</li>
<li>S只包含小写字母 ‘a’ 到 ‘z’。</li>
</ul></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="字符串" scheme="https://www.cylong.com/tags/字符串/"/>
<category term="指针" scheme="https://www.cylong.com/tags/指针/"/>
<category term="双指针" scheme="https://www.cylong.com/tags/双指针/"/>
<category term="贪心算法" scheme="https://www.cylong.com/tags/贪心算法/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
</entry>
<entry>
<title>有序数组的平方</title>
<link href="https://www.cylong.com/blog/2020/10/16/squares-of-a-sorted-array/"/>
<id>https://www.cylong.com/blog/2020/10/16/squares-of-a-sorted-array/</id>
<published>2020-10-15T16:11:00.000Z</published>
<updated>2020-10-15T16:11:00.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。</p><p><strong>示例 1:</strong></p><blockquote><p>输入:[-4, -1, 0, 3, 10]<br>输出:[0, 1, 9, 16, 100]</p></blockquote><p><strong>示例 2:</strong></p><blockquote><p>输入:[-7, -3, 2, 3, 11]<br>输出:[4, 9, 9, 49, 121]</p></blockquote><span id="more"></span><h1 id="双指针"><a href="#双指针" class="headerlink" title="双指针"></a>双指针</h1><p>最简单的方法,我们可以将数组中的元素全部求平方,然后进行排序即可,但是这样操作空间复杂度和时间复杂度都较大,在此我们不多做赘述。我们观察数组的特性可以发现,数组是排序好的,这样我们就可以使用一个比较巧妙的方法进行计算,具体的,对于全正数的数组,直接平方后即满足题意,但是有负数的情况下,负数中越小的负数,计算的结果越大。正数中越大的正数计算的结果越大,题目要求平方后的数组依然是非递减顺序排序,于是我们可以定义两个指针分别指向 0 和 len - 1。不断的移动这两个指针,每次我们将平方后的较大的值逆序的放入数组中。最后完成计算,结果也将是非递减顺序。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span>[] sortedSquares(<span class="type">int</span>[] A) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> A.length;</span><br><span class="line"> <span class="type">int</span>[] ans = <span class="keyword">new</span> <span class="title class_">int</span>[len];</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>, j = len - <span class="number">1</span>, pos = len - <span class="number">1</span>; i <= j;) {</span><br><span class="line"> <span class="keyword">if</span> (A[i] * A[i] > A[j] * A[j]) {</span><br><span class="line"> ans[pos] = A[i] * A[i];</span><br><span class="line"> i ;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> ans[pos] = A[j] * A[j];</span><br><span class="line"> j--;</span><br><span class="line"> }</span><br><span class="line"> pos--;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),其中 n 是数组 A 的长度。</li><li>空间复杂度:O(1)。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/squares-of-a-sorted-array/" title="有序数组的平方 | 力扣(LeetCode)">有序数组的平方 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/squares-of-a-sorted-array/solution/you-xu-shu-zu-de-ping-fang-by-leetcode-solution/" title="有序数组的平方 | 题解(LeetCode)">有序数组的平方 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个按非递减顺序排序的整数数组 A,返回每个数字的平方组成的新数组,要求也按非递减顺序排序。</p>
<p><strong>示例 1:</strong></p>
<blockquote>
<p>输入:[-4, -1, 0, 3, 10]<br>输出:[0, 1, 9, 16, 100]</p>
</blockquote>
<p><strong>示例 2:</strong></p>
<blockquote>
<p>输入:[-7, -3, 2, 3, 11]<br>输出:[4, 9, 9, 49, 121]</p>
</blockquote></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="数组" scheme="https://www.cylong.com/tags/数组/"/>
<category term="指针" scheme="https://www.cylong.com/tags/指针/"/>
<category term="双指针" scheme="https://www.cylong.com/tags/双指针/"/>
</entry>
<entry>
<title>填充每个节点的下一个右侧节点指针</title>
<link href="https://www.cylong.com/blog/2020/10/15/populating-next-right-pointers-in-each-node/"/>
<id>https://www.cylong.com/blog/2020/10/15/populating-next-right-pointers-in-each-node/</id>
<published>2020-10-15T10:46:48.000Z</published>
<updated>2020-10-15T10:46:48.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">struct Node {</span><br><span class="line"> int val;</span><br><span class="line"> Node *left;</span><br><span class="line"> Node *right;</span><br><span class="line"> Node *next;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。</p><p><strong>示例:</strong></p><img src="/blog/2020/10/15/populating-next-right-pointers-in-each-node/116_sample.png" class="" title="完美二叉树"><blockquote><p>输入:{“$id”:”1”,”left”:{“$id”:”2”,”left”:{“$id”:”3”,”left”:null,”next”:null,”right”:null,”val”:4},”next”:null,”right”:{“$id”:”4”,”left”:null,”next”:null,”right”:null,”val”:5},”val”:2},”next”:null,”right”:{“$id”:”5”,”left”:{“$id”:”6”,”left”:null,”next”:null,”right”:null,”val”:6},”next”:null,”right”:{“$id”:”7”,”left”:null,”next”:null,”right”:null,”val”:7},”val”:3},”val”:1}<br>输出:{“$id”:”1”,”left”:{“$id”:”2”,”left”:{“$id”:”3”,”left”:null,”next”:{“$id”:”4”,”left”:null,”next”:{“$id”:”5”,”left”:null,”next”:{“$id”:”6”,”left”:null,”next”:null,”right”:null,”val”:7},”right”:null,”val”:6},”right”:null,”val”:5},”right”:null,”val”:4},”next”:{“$id”:”7”,”left”:{“$ref”:”5”},”next”:null,”right”:{“$ref”:”6”},”val”:3},”right”:{“$ref”:”4”},”val”:2},”next”:null,”right”:{“$ref”:”7”},”val”:1}<br>解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。</p></blockquote><p><strong>提示:</strong></p><ul><li>你只能使用常量级额外空间。</li><li>使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。</li></ul><span id="more"></span><h1 id="按层遍历"><a href="#按层遍历" class="headerlink" title="按层遍历"></a>按层遍历</h1><p>按层遍历是常规的思路,基本框架就是二叉树的按层遍历,这里使用两个队列 queue 记录二叉树的节点,queueLevel 记录二叉树节点所在的层。每次往 queue 队列添加节点的时候,同时记录当前节点所在的层数,遍历层的时候,如果当前节点的层等于下一个节点的层,则执行操作 node.next = nextNode 。最后层次遍历完全部节点后,即完成了填充每个节点的下一个右侧节点的操作。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Node <span class="title function_">connect</span><span class="params">(Node 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"></span><br><span class="line"> Queue<Node> queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> Queue<Integer> queueLevel = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">curLevel</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> queueLevel.offer(<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">while</span> (!queue.isEmpty()) {</span><br><span class="line"> <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line"> <span class="type">Node</span> <span class="variable">nextNode</span> <span class="operator">=</span> queue.peek();</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">level</span> <span class="operator">=</span> queueLevel.poll();</span><br><span class="line"> <span class="type">Integer</span> <span class="variable">nextLevel</span> <span class="operator">=</span> queueLevel.peek();</span><br><span class="line"> <span class="keyword">if</span> (level != <span class="literal">null</span> && level != curLevel) {</span><br><span class="line"> curLevel = level;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (level == nextLevel) {</span><br><span class="line"> node.next = nextNode;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (node.left != <span class="literal">null</span>) {</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> queueLevel.offer(curLevel <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (node.right != <span class="literal">null</span>) {</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> queueLevel.offer(curLevel <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> root;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Node</span> {</span><br><span class="line"> <span class="keyword">public</span> <span class="type">int</span> val;</span><br><span class="line"> <span class="keyword">public</span> Node left;</span><br><span class="line"> <span class="keyword">public</span> Node right;</span><br><span class="line"> <span class="keyword">public</span> Node next;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">()</span> {}</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(<span class="type">int</span> _val)</span> {</span><br><span class="line"> val = _val;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="title function_">Node</span><span class="params">(<span class="type">int</span> _val, Node _left, Node _right, Node _next)</span> {</span><br><span class="line"> val = _val;</span><br><span class="line"> left = _left;</span><br><span class="line"> right = _right;</span><br><span class="line"> next = _next;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(N)。每个节点会被访问一次且只会被访问一次,即从队列中弹出,并建立 next 指针。</li><li>空间复杂度:O(N)。这是一棵完美二叉树,它的最后一个层级包含 N/2 个节点。广度优先遍历的复杂度取决于一个层级上的最大元素数量。这种情况下空间复杂度为 O(N)。</li></ul><h1 id="使用已有的-next-指针"><a href="#使用已有的-next-指针" class="headerlink" title="使用已有的 next 指针"></a>使用已有的 next 指针</h1><p>一棵树中,存在两种类型的 next 指针。</p><ol><li>第一种情况是连接同一个父节点的两个子节点。它们可以通过同一个节点直接访问到,因此执行下面操作即可完成连接。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node.left.next = node.right</span><br></pre></td></tr></table></figure><img src="/blog/2020/10/15/populating-next-right-pointers-in-each-node/node1.png" class="" title="情况1"></li><li>第二种情况在不同父亲的子节点之间建立连接,这种情况不能直接连接。我们可以发现当前节点右节点的指针指向的是当前节点父节点 next 节点的左节点,如果父节点没有 next 节点,则指向 null。<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">node.right.next = node.next != <span class="literal">null</span> ? node.next.left : <span class="literal">null</span>;</span><br></pre></td></tr></table></figure><img src="/blog/2020/10/15/populating-next-right-pointers-in-each-node/node2.png" class="" title="情况2"></li></ol><p>这里我们使用递归可以很方便的解决上述问题。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> Node <span class="title function_">connect</span><span class="params">(Node 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> root;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (root.left != <span class="literal">null</span>) {</span><br><span class="line"> root.left.next = root.right;</span><br><span class="line"> root.right.next = root.next != <span class="literal">null</span> ? root.next.left : <span class="literal">null</span>;</span><br><span class="line"> connect(root.left);</span><br><span class="line"> connect(root.right);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> root;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(N),每个节点只访问一次。</li><li>空间复杂度:O((logn),不需要存储额外的节点。这里只有递归占用的空间,满足题目要求。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/" title="填充每个节点的下一个右侧节点指针 | 力扣(LeetCode)">填充每个节点的下一个右侧节点指针 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node/solution/tian-chong-mei-ge-jie-dian-de-xia-yi-ge-you-ce-2-4/" title="填充每个节点的下一个右侧节点指针 | 题解(LeetCode)">填充每个节点的下一个右侧节点指针 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">struct Node &#123;</span><br><span class="line"> int val;</span><br><span class="line"> Node *left;</span><br><span class="line"> Node *right;</span><br><span class="line"> Node *next;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。</p>
<p><strong>示例:</strong></p>
<img src="/blog/2020/10/15/populating-next-right-pointers-in-each-node/116_sample.png" class="" title="完美二叉树">
<blockquote>
<p>输入:{“$id”:”1”,”left”:{“$id”:”2”,”left”:{“$id”:”3”,”left”:null,”next”:null,”right”:null,”val”:4},”next”:null,”right”:{“$id”:”4”,”left”:null,”next”:null,”right”:null,”val”:5},”val”:2},”next”:null,”right”:{“$id”:”5”,”left”:{“$id”:”6”,”left”:null,”next”:null,”right”:null,”val”:6},”next”:null,”right”:{“$id”:”7”,”left”:null,”next”:null,”right”:null,”val”:7},”val”:3},”val”:1}<br>输出:{“$id”:”1”,”left”:{“$id”:”2”,”left”:{“$id”:”3”,”left”:null,”next”:{“$id”:”4”,”left”:null,”next”:{“$id”:”5”,”left”:null,”next”:{“$id”:”6”,”left”:null,”next”:null,”right”:null,”val”:7},”right”:null,”val”:6},”right”:null,”val”:5},”right”:null,”val”:4},”next”:{“$id”:”7”,”left”:{“$ref”:”5”},”next”:null,”right”:{“$ref”:”6”},”val”:3},”right”:{“$ref”:”4”},”val”:2},”next”:null,”right”:{“$ref”:”7”},”val”:1}<br>解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。</p>
</blockquote>
<p><strong>提示:</strong></p>
<ul>
<li>你只能使用常量级额外空间。</li>
<li>使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。</li>
</ul></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="队列" scheme="https://www.cylong.com/tags/队列/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="树" scheme="https://www.cylong.com/tags/树/"/>
<category term="二叉树" scheme="https://www.cylong.com/tags/二叉树/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="广度优先搜索" scheme="https://www.cylong.com/tags/广度优先搜索/"/>
</entry>
<entry>
<title>环形链表</title>
<link href="https://www.cylong.com/blog/2020/10/09/linked-list-cycle/"/>
<id>https://www.cylong.com/blog/2020/10/09/linked-list-cycle/</id>
<published>2020-10-09T13:45:48.000Z</published>
<updated>2020-10-09T13:45:48.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个链表,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。</p><p>如果链表中存在环,则返回 true 。 否则,返回 false 。</p><p><strong>进阶:</strong></p><p>你能用 O(1)(即,常量)内存解决此问题吗?</p><p><strong>示例 1:</strong></p><img src="/blog/2020/10/09/linked-list-cycle/circularlinkedlist.png" class="" title="环形链表"><blockquote><p>输入:head = [3, 2, 0, -4], pos = 1<br>输出:true<br>解释:链表中有一个环,其尾部连接到第二个节点。</p></blockquote><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>比较简单的方法是遍历整个链表,将每个元素都加入到 HashSet 中,根据 HashSet 的没有重复元素的特性,当遇到重复的元素,说明遍历这个链表访问了重复的元素,即链表中有环。HashSet 是使用 HashMap 实现的,关于实现细节可以直接看源码。</p><blockquote><p><a href="/blog/2019/09/10/hashmap/" title="浅谈 HashMap | 笑话人生">浅谈 HashMap | 笑话人生</a></p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasCycle</span><span class="params">(ListNode head)</span> {</span><br><span class="line"> HashSet<ListNode> visit = <span class="keyword">new</span> <span class="title class_">HashSet</span><ListNode>();</span><br><span class="line"> <span class="keyword">while</span> (head != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">if</span> (!visit.add(head)) {</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"> head = head.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ListNode</span> {</span><br><span class="line"> <span class="type">int</span> val;</span><br><span class="line"> ListNode next;</span><br><span class="line"> ListNode(<span class="type">int</span> x) {</span><br><span class="line"> val = x;</span><br><span class="line"> next = <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(N),其中 N 是链表中的节点数。最坏情况下我们需要遍历每个节点一次。</li><li>空间复杂度:O(N),其中 N 是链表中的节点数。主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次。</li></ul><h1 id="快慢指针"><a href="#快慢指针" class="headerlink" title="快慢指针"></a>快慢指针</h1><p>我们定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">hasCycle</span><span class="params">(ListNode head)</span> {</span><br><span class="line"> <span class="keyword">if</span> (head == <span class="literal">null</span> || head.next == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="type">ListNode</span> <span class="variable">slow</span> <span class="operator">=</span> head;</span><br><span class="line"> <span class="type">ListNode</span> <span class="variable">fast</span> <span class="operator">=</span> head.next;</span><br><span class="line"> <span class="keyword">while</span> (slow != fast) {</span><br><span class="line"> <span class="keyword">if</span> (fast == <span class="literal">null</span> || fast.next == <span class="literal">null</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"> slow = slow.next;</span><br><span class="line"> fast = fast.next.next;</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></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(N),其中 N 是链表中的节点数。<ul><li>当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次。</li><li>当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮。</li></ul></li><li>空间复杂度:O(1)。我们只使用了两个指针的额外空间。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/linked-list-cycle/" title="环形链表 | 力扣(LeetCode)">环形链表 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/linked-list-cycle/solution/huan-xing-lian-biao-by-leetcode-solution/" title="环形链表 | 题解(LeetCode)">环形链表 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个链表,判断链表中是否有环。如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。</p>
<p>如果链表中存在环,则返回 true 。 否则,返回 false 。</p>
<p><strong>进阶:</strong></p>
<p>你能用 O(1)(即,常量)内存解决此问题吗?</p>
<p><strong>示例 1:</strong></p>
<img src="/blog/2020/10/09/linked-list-cycle/circularlinkedlist.png" class="" title="环形链表">
<blockquote>
<p>输入:head = [3, 2, 0, -4], pos = 1<br>输出:true<br>解释:链表中有一个环,其尾部连接到第二个节点。</p>
</blockquote></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="链表" scheme="https://www.cylong.com/tags/链表/"/>
<category term="哈希表" scheme="https://www.cylong.com/tags/哈希表/"/>
<category term="指针" scheme="https://www.cylong.com/tags/指针/"/>
<category term="双指针" scheme="https://www.cylong.com/tags/双指针/"/>
<category term="HashSet" scheme="https://www.cylong.com/tags/HashSet/"/>
</entry>
<entry>
<title>删除排序数组中的重复项</title>
<link href="https://www.cylong.com/blog/2020/09/18/remove-duplicates-from-sorted-array/"/>
<id>https://www.cylong.com/blog/2020/09/18/remove-duplicates-from-sorted-array/</id>
<published>2020-09-18T15:16:31.000Z</published>
<updated>2020-09-18T15:16:31.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。</p><p><strong>示例1:</strong></p><blockquote><p>给定数组 nums = [1, 1, 2],<br>函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。</p></blockquote><p><strong>示例2:</strong></p><blockquote><p>给定 nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4],<br>函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。你不需要考虑数组中超出新长度后面的元素。</p></blockquote><p><strong>说明:</strong></p><blockquote><p>为什么返回数值是整数,但输出的答案是数组呢?<br>请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。<br>你可以想象内部操作如下:</p></blockquote><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝</span></span><br><span class="line"><span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> removeDuplicates(nums);</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="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i < len; i ) {</span><br><span class="line"> print(nums[i]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>题目中是一个排序后的数组,那么相同的元素一定是排列在一起的,我们可以使用两个指针 i 和 j,我们不断的移动j指针,只要 nums[i]=nums[j],我们就进行 j 操作,跳过重复项,直到 nums[i]≠nums[j] 的时候,说明遇到了下一个非重复项,于是我们就将 num[j] 的值复制到 num[i 1] 的位置,接着重复此流程,遍历完全部数组。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">removeDuplicates</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">i</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> <span class="variable">j</span> <span class="operator">=</span> <span class="number">1</span>; j < nums.length; j ) {</span><br><span class="line"> <span class="keyword">if</span> (nums[i] != nums[j]) {</span><br><span class="line"> i ;</span><br><span class="line"> nums[i] = nums[j];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> i <span class="number">1</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),假设数组的长度是 n,那么 i 和 j 分别最多遍历 n 步。</li><li>空间复杂度:O(1)。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/" title="删除排序数组中的重复项 | 力扣(LeetCode)">删除排序数组中的重复项 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/solution/shan-chu-pai-xu-shu-zu-zhong-de-zhong-fu-xiang-by-/" title="删除排序数组中的重复项 | 题解(LeetCode)">删除排序数组中的重复项 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。</p>
<p><strong>示例1:</strong></p>
<blockquote>
<p>给定数组 nums = [1, 1, 2],<br>函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。 你不需要考虑数组中超出新长度后面的元素。</p>
</blockquote>
<p><strong>示例2:</strong></p>
<blockquote>
<p>给定 nums = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4],<br>函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。你不需要考虑数组中超出新长度后面的元素。</p>
</blockquote>
<p><strong>说明:</strong></p>
<blockquote>
<p>为什么返回数值是整数,但输出的答案是数组呢?<br>请注意,输入数组是以「引用」方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。<br>你可以想象内部操作如下:</p>
</blockquote>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝</span></span><br><span class="line"><span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> removeDuplicates(nums);</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="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; len; i ) &#123;</span><br><span class="line"> print(nums[i]);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="数组" scheme="https://www.cylong.com/tags/数组/"/>
<category term="指针" scheme="https://www.cylong.com/tags/指针/"/>
<category term="双指针" scheme="https://www.cylong.com/tags/双指针/"/>
</entry>
<entry>
<title>组合</title>
<link href="https://www.cylong.com/blog/2020/09/17/combinations/"/>
<id>https://www.cylong.com/blog/2020/09/17/combinations/</id>
<published>2020-09-16T16:51:25.000Z</published>
<updated>2020-09-16T16:51:25.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。</p><p><strong>示例:</strong></p><blockquote><p>输入: n = 4, k = 2<br>输出:</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> [2,4],</span><br><span class="line"> [3,4],</span><br><span class="line"> [2,3],</span><br><span class="line"> [1,2],</span><br><span class="line"> [1,3],</span><br><span class="line"> [1,4],</span><br><span class="line">]</span><br></pre></td></tr></table></figure><span id="more"></span><h1 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h1><p>从 n 个当中选 k 个的所有方案对应的枚举是组合型枚举。思路很简单,针对 1 … n 中的每个数,在组合的结果中,我们都有两种结果,选择或者不选择。于是我们从第一个数开始进行递归的判断。详细分析在代码注释中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">List<List<Integer>> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line">LinkedList<Integer> item = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> List<List<Integer>> <span class="title function_">combine</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> k)</span> {</span><br><span class="line"> dfsCombine(n, k, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> ans;</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_">dfsCombine</span><span class="params">(<span class="type">int</span> n, <span class="type">int</span> k, <span class="type">int</span> index)</span> {</span><br><span class="line"> <span class="keyword">if</span> (item.size() == k) {</span><br><span class="line"> <span class="comment">// 如果长度达到k,保存结果。</span></span><br><span class="line"> ans.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span><>(item));</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (item.size() n - index <span class="number">1</span> < k) {</span><br><span class="line"> <span class="comment">// 如果剩下的数字不够组合成k个数,则不满足要求。</span></span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 选择当前元素,然后进行递归。 </span></span><br><span class="line"> item.add(index);</span><br><span class="line"> dfsCombine(n, k, index <span class="number">1</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 不选择当前元素,然后进行递归,也是一种回溯。</span></span><br><span class="line"> item.removeLast();</span><br><span class="line"> dfsCombine(n, k, index <span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/combinations/" title="组合 | 力扣(LeetCode)">组合 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/combinations/solution/zu-he-by-leetcode-solution/" title="组合 | 题解(LeetCode)">组合 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。</p>
<p><strong>示例:</strong></p>
<blockquote>
<p>输入: n = 4, k = 2<br>输出:</p>
</blockquote>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">[</span><br><span class="line"> [2,4],</span><br><span class="line"> [3,4],</span><br><span class="line"> [2,3],</span><br><span class="line"> [1,2],</span><br><span class="line"> [1,3],</span><br><span class="line"> [1,4],</span><br><span class="line">]</span><br></pre></td></tr></table></figure></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="回溯算法" scheme="https://www.cylong.com/tags/回溯算法/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="组合" scheme="https://www.cylong.com/tags/组合/"/>
</entry>
<entry>
<title>汉明距离</title>
<link href="https://www.cylong.com/blog/2020/09/08/hamming-distance/"/>
<id>https://www.cylong.com/blog/2020/09/08/hamming-distance/</id>
<published>2020-09-07T16:10:52.000Z</published>
<updated>2020-09-07T16:10:52.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。</p><p><strong>示例:</strong></p><blockquote><p>输入: x = 1, y = 4<br>输出: 2<br>解释:</p></blockquote><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">1 (0 0 0 1)</span><br><span class="line">4 (0 1 0 0)</span><br><span class="line"> ↑ ↑</span><br></pre></td></tr></table></figure><span id="more"></span><h1 id="位移运算"><a href="#位移运算" class="headerlink" title="位移运算"></a>位移运算</h1><p>根据题意,我们直接使用异或运算两个整数,结果是相同位为 1,不同位为 0,这样我们直接计算异或后整数的 1 的位数,就是汉明距离。检查某一位是否是 1, 可以使用取模运算(i % 2)或者 AND 与运算(i & 1)。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hammingDistance</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">xor</span> <span class="operator">=</span> x ^ y;</span><br><span class="line"> <span class="type">int</span> <span class="variable">ans</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (xor != <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (xor % <span class="number">2</span> == <span class="number">1</span>) {</span><br><span class="line"> ans = <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> xor = xor >> <span class="number">1</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(1),在 Java 中 Integer 的大小是固定的,处理时间也是固定的。 32 位整数需要 32 次迭代。</li><li>空间复杂度:O(1),使用恒定大小的空间。</li></ul><h1 id="Brian-Kernighan-算法"><a href="#Brian-Kernighan-算法" class="headerlink" title="Brian Kernighan 算法"></a>Brian Kernighan 算法</h1><p>上面例子中,遇到最右边的 1 后,如果可以跳过中间的 0,直接跳到下一个 1,效率会高很多。这是布赖恩·克尼根位计数算法的基本思想。该算法使用特定比特位和算术运算移除等于 1 的最右比特位。</p><p>当我们在 number 和 number - 1 上做 AND 位运算时,原数字 number 的最右边等于 1 的比特会被移除。</p><img src="/blog/2020/09/08/hamming-distance/BrianKernighan算法.png" class="" title="Brian Kernighan 算法"><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hammingDistance</span><span class="params">(<span class="type">int</span> x, <span class="type">int</span> y)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">xor</span> <span class="operator">=</span> x ^ y;</span><br><span class="line"> <span class="type">int</span> <span class="variable">ans</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (xor != <span class="number">0</span>) {</span><br><span class="line"> ans = <span class="number">1</span>;</span><br><span class="line"> xor = xor & (xor - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(1),在 Java 中 Integer 的大小是固定的,处理时间也是固定的。 但是该方法需要的迭代操作更少。</li><li>空间复杂度:O(1),使用恒定大小的空间。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/hamming-distance/" title="汉明距离 | 力扣(LeetCode)">汉明距离 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/hamming-distance/solution/yi-ming-ju-chi-by-leetcode/" title="汉明距离 | 题解(LeetCode)">汉明距离 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>两个整数之间的汉明距离指的是这两个数字对应二进制位不同的位置的数目。给出两个整数 x 和 y,计算它们之间的汉明距离。</p>
<p><strong>示例:</strong></p>
<blockquote>
<p>输入: x = 1, y = 4<br>输出: 2<br>解释:</p>
</blockquote>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">1 (0 0 0 1)</span><br><span class="line">4 (0 1 0 0)</span><br><span class="line"> ↑ ↑</span><br></pre></td></tr></table></figure></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="数学" scheme="https://www.cylong.com/tags/数学/"/>
<category term="位运算" scheme="https://www.cylong.com/tags/位运算/"/>
<category term="Brian Kernighan 算法" scheme="https://www.cylong.com/tags/Brian-Kernighan-算法/"/>
</entry>
<entry>
<title>二叉树的遍历</title>
<link href="https://www.cylong.com/blog/2020/09/02/binary-tree-traverse/"/>
<id>https://www.cylong.com/blog/2020/09/02/binary-tree-traverse/</id>
<published>2020-09-02T14:51:37.000Z</published>
<updated>2020-09-21T15:30:34.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近一直在刷 Leetcode ,其中涉及了很多二叉树相关的题,二叉树是一种很重要的数据结构,很多其他的数据结构都是以二叉树为基础,二叉树的遍历涉及很多种,包括前序遍历、中序遍历、后序遍历、层次遍历。开始一直分不清这些遍历是如何工作的,随着后面题刷的越来越多,也渐渐熟悉了二叉树的遍历方式,在这里做一个总结分享给大家。</p><p>四种遍历的主要方式为:</p><ul><li>前序遍历:根节点 -> 左子树 -> 右子树</li><li>中序遍历:左子树 -> 根节点 -> 右子树</li><li>后序遍历:左子树 -> 右子树 -> 根节点</li><li>层次遍历:从上到下按照层遍历</li></ul><p>接下来使用以下的二叉树做样例:</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><br><span class="line"> 1</span><br><span class="line"> / \</span><br><span class="line"> 2 3</span><br><span class="line"> / \ \</span><br><span class="line"> 4 5 6</span><br><span class="line"> / \</span><br><span class="line"> 7 8</span><br><span class="line"></span><br><span class="line">前序遍历:1 2 4 5 7 8 3 6 </span><br><span class="line">中序遍历:4 2 7 5 8 1 3 6</span><br><span class="line">后序遍历:4 7 8 5 2 6 3 1</span><br><span class="line">层次遍历:1 2 3 4 5 6 7 8</span><br></pre></td></tr></table></figure><span id="more"></span><h1 id="前序遍历"><a href="#前序遍历" class="headerlink" title="前序遍历"></a>前序遍历</h1><p>前序遍历:根节点 -> 左子树 -> 右子树。前序遍历是先输出根节点的值,再去递归的输出左子树和右子树。代码实现也包括递归和非递归两个版本。</p><h2 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h2><p>树的结构定义本身就是递归的定义,所以使用递归版本实现树的遍历会使代码更加简洁易于理解。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dfsPreOrderTraverse</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><br><span class="line"> }</span><br><span class="line"> System.out.print(root.val <span class="string">" "</span>);</span><br><span class="line"> dfsPreOrderTraverse(root.left);</span><br><span class="line"> dfsPreOrderTraverse(root.right);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="非递归"><a href="#非递归" class="headerlink" title="非递归"></a>非递归</h2><p>非递归版本,就没有递归版本那么好理解,代码也比较多,我们这里引入栈来保存每个节点,首先将根节点加入栈中,接下来我们遍历此栈,根据栈的后进先出特性,每次将栈顶元素退出,并输出其值,接下来,我们将此节点的右节点和左节点依次加入到栈中,根据栈的后进先出特性,永远都是先输出左子树,然后输出右子树。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">preOrderTraverse</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><br><span class="line"> }</span><br><span class="line"> Stack<TreeNode> stack = <span class="keyword">new</span> <span class="title class_">Stack</span><>();</span><br><span class="line"> stack.push(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">node</span> <span class="operator">=</span> stack.pop();</span><br><span class="line"> System.out.print(node.val <span class="string">" "</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"> }</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"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="中序遍历"><a href="#中序遍历" class="headerlink" title="中序遍历"></a>中序遍历</h1><p>中序遍历:左子树 -> 根节点 -> 右子树。中序遍历是中间输出根节点的值,先递归左子树,然后输出根节点的值,再递归右子树。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dfsInOrderTraverse</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><br><span class="line"> }</span><br><span class="line"> dfsInOrderTraverse(root.left);</span><br><span class="line"> System.out.print(root.val <span class="string">" "</span>);</span><br><span class="line"> dfsInOrderTraverse(root.right);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="后续遍历"><a href="#后续遍历" class="headerlink" title="后续遍历"></a>后续遍历</h1><p>后序遍历:左子树 -> 右子树 -> 根节点。后续遍历是先递归左子树和右子树,最后输出根节点的值。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">dfsPostOrderTraverse</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><br><span class="line"> }</span><br><span class="line"> dfsPostOrderTraverse(root.left);</span><br><span class="line"> dfsPostOrderTraverse(root.right);</span><br><span class="line"> System.out.print(root.val <span class="string">" "</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h1 id="层次遍历"><a href="#层次遍历" class="headerlink" title="层次遍历"></a>层次遍历</h1><p>层次遍历:从上到下按照层遍历。这里不是递归的去遍历,而是横向的遍历每一层,这里我们引入队列,我们先把根节点加入到队列中,接下来,根据队列的先进先出特性,我们先从队列中取出最先加入的节点,输出其值,然后将此节点的左右节点分别加入到队列中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">levelTraverse</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><br><span class="line"> }</span><br><span class="line"> Queue<TreeNode> queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> <span class="keyword">while</span> (!queue.isEmpty()) {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line"> System.out.print(node.val <span class="string">" "</span>);</span><br><span class="line"> <span class="keyword">if</span> (node.left != <span class="literal">null</span>) {</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (node.right != <span class="literal">null</span>) {</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></pre></td></tr></table></figure><p>另外一种比较复杂的按层遍历是每层当成一个列表输出,这个时候我们只要增加另外一个队列,同时记录当时遍历的层数即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> ArrayList<ArrayList<Integer>> <span class="title function_">levelTraverse</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> ArrayList<ArrayList<Integer>> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> ArrayList<Integer> item = <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> ans;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> Queue<TreeNode> queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> Queue<Integer> queueLevel = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">curLevel</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line"> queue.offer(root);</span><br><span class="line"> queueLevel.offer(<span class="number">1</span>);</span><br><span class="line"> <span class="keyword">while</span> (!queue.isEmpty()) {</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> queue.poll();</span><br><span class="line"> <span class="type">int</span> <span class="variable">level</span> <span class="operator">=</span> queueLevel.poll();</span><br><span class="line"> <span class="keyword">if</span> (level != curLevel) {</span><br><span class="line"> curLevel = level;</span><br><span class="line"> ans.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span><>(item));</span><br><span class="line"> item.clear();</span><br><span class="line"> }</span><br><span class="line"> item.add(node.val);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (node.left != <span class="literal">null</span>) {</span><br><span class="line"> queue.offer(node.left);</span><br><span class="line"> queueLevel.offer(curLevel <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (node.right != <span class="literal">null</span>) {</span><br><span class="line"> queue.offer(node.right);</span><br><span class="line"> queueLevel.offer(curLevel <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!item.isEmpty()) {</span><br><span class="line"> ans.add(<span class="keyword">new</span> <span class="title class_">ArrayList</span><>(item));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>输出样例:[[1], [2, 3], [4, 5, 6], [7, 8]]</p><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>其实写完代码就可以发现很多有意思的事情,之前模糊不清的概念也都搞清楚了。</p><ol><li>前序、中序、后序遍历其实是针对根节点来说的,对于左右子节点,都是先左后右。另外无论是哪种遍历方式,都是先遍历(访问)根节点,区别就是什么时候处理根节点(比如输出根节点的值)。</li><li>广度优先搜索对于树来说,其实就是层次遍历,深度优先搜索对于树来说,其实就是先序遍历。</li><li>树的层次遍历和树的先序遍历的非递归版本,其实代码一样,只不过一个使用的是队列,一个使用的是栈。</li></ol><h1 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h1><blockquote><p><a href="https://blog.csdn.net/My_Jobs/article/details/43451187" title="二叉树遍历(前序、中序、后序、层次遍历、深度优先、广度优先)">二叉树遍历(前序、中序、后序、层次遍历、深度优先、广度优先)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>最近一直在刷 Leetcode ,其中涉及了很多二叉树相关的题,二叉树是一种很重要的数据结构,很多其他的数据结构都是以二叉树为基础,二叉树的遍历涉及很多种,包括前序遍历、中序遍历、后序遍历、层次遍历。开始一直分不清这些遍历是如何工作的,随着后面题刷的越来越多,也渐渐熟悉了二叉树的遍历方式,在这里做一个总结分享给大家。</p>
<p>四种遍历的主要方式为:</p>
<ul>
<li>前序遍历:根节点 -&gt; 左子树 -&gt; 右子树</li>
<li>中序遍历:左子树 -&gt; 根节点 -&gt; 右子树</li>
<li>后序遍历:左子树 -&gt; 右子树 -&gt; 根节点</li>
<li>层次遍历:从上到下按照层遍历</li>
</ul>
<p>接下来使用以下的二叉树做样例:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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><br><span class="line"> 1</span><br><span class="line"> / \</span><br><span class="line"> 2 3</span><br><span class="line"> / \ \</span><br><span class="line"> 4 5 6</span><br><span class="line"> / \</span><br><span class="line"> 7 8</span><br><span class="line"></span><br><span class="line">前序遍历:1 2 4 5 7 8 3 6 </span><br><span class="line">中序遍历:4 2 7 5 8 1 3 6</span><br><span class="line">后序遍历:4 7 8 5 2 6 3 1</span><br><span class="line">层次遍历:1 2 3 4 5 6 7 8</span><br></pre></td></tr></table></figure></summary>
<category term="数据结构与算法" scheme="https://www.cylong.com/categories/数据结构与算法/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="数据结构与算法" scheme="https://www.cylong.com/tags/数据结构与算法/"/>
<category term="链表" scheme="https://www.cylong.com/tags/链表/"/>
<category term="队列" scheme="https://www.cylong.com/tags/队列/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="树" scheme="https://www.cylong.com/tags/树/"/>
<category term="二叉树" scheme="https://www.cylong.com/tags/二叉树/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="栈" scheme="https://www.cylong.com/tags/栈/"/>
<category term="中序遍历" scheme="https://www.cylong.com/tags/中序遍历/"/>
<category term="广度优先搜索" scheme="https://www.cylong.com/tags/广度优先搜索/"/>
<category term="前序遍历" scheme="https://www.cylong.com/tags/前序遍历/"/>
<category term="后序遍历" scheme="https://www.cylong.com/tags/后序遍历/"/>
</entry>
<entry>
<title>预测赢家</title>
<link href="https://www.cylong.com/blog/2020/09/01/predict-the-winner/"/>
<id>https://www.cylong.com/blog/2020/09/01/predict-the-winner/</id>
<published>2020-09-01T14:36:03.000Z</published>
<updated>2020-09-01T14:36:03.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。</p><p><strong>示例 1:</strong></p><blockquote><p>输入:[1, 5, 2]<br>输出:False<br>解释:一开始,玩家1可以从1和2中进行选择。<br>如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。<br>所以,玩家 1 的最终分数为 1 2 = 3,而玩家 2 为 5 。<br>因此,玩家 1 永远不会成为赢家,返回 False 。</p></blockquote><p><strong>示例 2:</strong></p><blockquote><p>输入:[1, 5, 233, 7]<br>输出:True<br>解释:玩家 1 一开始选择 1 。然后玩家 2 必须从 5 和 7 中进行选择。无论玩家 2 选择了哪个,玩家 1 都可以选择 233 。最终,玩家 1(234 分)比玩家 2(12 分)获得更多的分数,所以返回 True,表示玩家 1 可以成为赢家。</p></blockquote><p><strong>提示:</strong></p><ul><li>1 <= 给定的数组长度 <= 20.</li><li>数组里所有分数都为非负数且不会大于 10000000 。</li><li>如果最终两个玩家的分数相等,那么玩家 1 仍为赢家。</li></ul><span id="more"></span><h1 id="递归"><a href="#递归" class="headerlink" title="递归"></a>递归</h1><p>为了判断哪个玩家可以获胜,需要计算一个得分差值,即先手得分赢过后手的得分。当数组中的所有数字都被拿取时,如果先手与后手得分之差大于或等于 0,则先手获胜,反之则后手获胜。</p><p>由于每次只能从数组的任意一端拿取数字,因此可以保证数组中剩下的部分一定是连续的。假设数组当前剩下的部分为下标 start 到下标 end,其中 0 ≤ start ≤ end < nums.length。如果 start = end,则只剩一个数字,当前玩家只能拿取这个数字。如果 start < end,则当前玩家可以选择 nums[start] 或 nums[end],然后轮到另一个玩家在数组剩下的部分选取数字。这是一个递归的过程。递归时记录当前做选择的玩家赢过对手的分数。如果大于等于 0,则代表他在这个子问题中赢了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">PredictTheWinner</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> <span class="keyword">return</span> calculate(nums, <span class="number">0</span>, nums.length - <span class="number">1</span>) >= <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">calculate</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> start, <span class="type">int</span> end)</span> {</span><br><span class="line"> <span class="keyword">if</span> (start == end) {</span><br><span class="line"> <span class="keyword">return</span> nums[start];</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> <span class="variable">selectStart</span> <span class="operator">=</span> nums[start] - calculate(nums, start <span class="number">1</span>, end);</span><br><span class="line"> <span class="type">int</span> <span class="variable">selectEnd</span> <span class="operator">=</span> nums[end] - calculate(nums, start, end - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> Math.max(selectStart, selectEnd);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面的递归,其实有很多重复的计算,比如你先选 1,我再选 7,和你先选 7,我再选 1,这两种所带来的子问题是一样的,都是剩下 [5, 233]。我们用数组或哈希表去存储计算过的子问题的解,遇到重复的子问题,就不用再次递归计算。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">Integer[][] maxScore;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">PredictTheWinner</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> maxScore = <span class="keyword">new</span> <span class="title class_">Integer</span>[nums.length][nums.length];</span><br><span class="line"> <span class="keyword">return</span> calculate(nums, <span class="number">0</span>, nums.length - <span class="number">1</span>) >= <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">calculate</span><span class="params">(<span class="type">int</span>[] nums, <span class="type">int</span> start, <span class="type">int</span> end)</span> {</span><br><span class="line"> <span class="keyword">if</span> (maxScore[start][end] != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span> maxScore[start][end];</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (start == end) {</span><br><span class="line"> <span class="keyword">return</span> maxScore[start][end] = nums[start];</span><br><span class="line"> }</span><br><span class="line"> <span class="type">int</span> <span class="variable">selectStart</span> <span class="operator">=</span> nums[start] - calculate(nums, start <span class="number">1</span>, end);</span><br><span class="line"> <span class="type">int</span> <span class="variable">selectEnd</span> <span class="operator">=</span> nums[end] - calculate(nums, start, end - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> maxScore[start][end] = Math.max(selectStart, selectEnd);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(2^n),其中 n 是数组的长度。</li><li>空间复杂度:O(n),其中 n 是数组的长度。空间复杂度取决于递归使用的栈空间。</li></ul><h1 id="动态规划"><a href="#动态规划" class="headerlink" title="动态规划"></a>动态规划</h1><p>定义二维数组 dp,其行数和列数都等于数组的长度,dp[i][j] 表示当数组剩下的部分为下标 i 到下标 j 时,当前玩家与另一个玩家的分数之差的最大值,注意当前玩家不一定是先手。</p><ul><li>只有当 i ≤ j 时,数组剩下的部分才有意义,因此当 i > j 时,dp[i][j] = 0。</li><li>当 i = j 时,只剩一个数字,当前玩家只能拿取这个数字,因此对于所有 0 ≤ i < nums.length,都有 dp[i][i] = nums[i]。</li><li>当 i < j 时,当前玩家可以选择 nums[i] 或 nums[j],然后轮到另一个玩家在数组剩下的部分选取数字。在两种方案中,当前玩家会选择最优的方案,使得自己的分数最大化。因此可以得到如下状态转移方程:<blockquote><p>dp[i][j] = max(nums[i] − dp[i 1][j], nums[j] − dp[i][j − 1])</p></blockquote></li></ul><p>最后判断 dp[0][nums.length − 1] 的值,如果大于或等于 0,则先手得分大于或等于后手得分,因此先手成为赢家,否则后手成为赢家。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">PredictTheWinner</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> <span class="type">int</span>[][] dp = <span class="keyword">new</span> <span class="title class_">int</span>[nums.length][nums.length];</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 < nums.length; i ) {</span><br><span class="line"> dp[i][i] = nums[i];</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> nums.length - <span class="number">2</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i <span class="number">1</span>; j < nums.length; j ) {</span><br><span class="line"> dp[i][j] = Math.max(nums[i] - dp[i <span class="number">1</span>][j], nums[j] - dp[i][j - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp[<span class="number">0</span>][nums.length - <span class="number">1</span>] >= <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上述代码中使用了二维数组 dp。分析状态转移方程可以看到,dp[i][j] 的值只和 dp[i 1][j] 与 dp[i][j − 1] 有关,即在计算 dp 的第 i 行的值时,只需要使用到 dp 的第 i 行和第 i 1 行的值,因此可以使用一维数组代替二维数组,对空间进行优化。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">PredictTheWinner</span><span class="params">(<span class="type">int</span>[] nums)</span> {</span><br><span class="line"> <span class="type">int</span>[] dp = <span class="keyword">new</span> <span class="title class_">int</span>[nums.length];</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 < nums.length; i ) {</span><br><span class="line"> dp[i] = nums[i];</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> nums.length - <span class="number">2</span>; i >= <span class="number">0</span>; i--) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i <span class="number">1</span>; j < nums.length; j ) {</span><br><span class="line"> dp[j] = Math.max(nums[i] - dp[j], nums[j] - dp[j - <span class="number">1</span>]);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> dp[nums.length - <span class="number">1</span>] >= <span class="number">0</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n²),其中 n 是数组的长度。需要计算每个子数组对应的 dp 的值,共有 n * (n 1) / 2 个子数组。</li><li>空间复杂度:O(n),其中 n 是数组的长度。空间复杂度取决于额外创建的数组 dp,如果不优化空间,则空间复杂度是 O(n²),使用一维数组优化之后空间复杂度可以降至 O(n)。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/predict-the-winner/" title="预测赢家 | 力扣(LeetCode)">预测赢家 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/predict-the-winner/solution/yu-ce-ying-jia-by-leetcode-solution/" title="预测赢家 | 题解(LeetCode)">预测赢家 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。</p>
<p><strong>示例 1:</strong></p>
<blockquote>
<p>输入:[1, 5, 2]<br>输出:False<br>解释:一开始,玩家1可以从1和2中进行选择。<br>如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。<br>所以,玩家 1 的最终分数为 1 2 = 3,而玩家 2 为 5 。<br>因此,玩家 1 永远不会成为赢家,返回 False 。</p>
</blockquote>
<p><strong>示例 2:</strong></p>
<blockquote>
<p>输入:[1, 5, 233, 7]<br>输出:True<br>解释:玩家 1 一开始选择 1 。然后玩家 2 必须从 5 和 7 中进行选择。无论玩家 2 选择了哪个,玩家 1 都可以选择 233 。最终,玩家 1(234 分)比玩家 2(12 分)获得更多的分数,所以返回 True,表示玩家 1 可以成为赢家。</p>
</blockquote>
<p><strong>提示:</strong></p>
<ul>
<li>1 &lt;= 给定的数组长度 &lt;= 20.</li>
<li>数组里所有分数都为非负数且不会大于 10000000 。</li>
<li>如果最终两个玩家的分数相等,那么玩家 1 仍为赢家。</li>
</ul></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="记忆化" scheme="https://www.cylong.com/tags/记忆化/"/>
<category term="动态规划" scheme="https://www.cylong.com/tags/动态规划/"/>
<category term="滚动数组" scheme="https://www.cylong.com/tags/滚动数组/"/>
<category term="零和博弈" scheme="https://www.cylong.com/tags/零和博弈/"/>
</entry>
<entry>
<title>钥匙和房间</title>
<link href="https://www.cylong.com/blog/2020/08/31/keys-and-rooms/"/>
<id>https://www.cylong.com/blog/2020/08/31/keys-and-rooms/</id>
<published>2020-08-31T15:50:07.000Z</published>
<updated>2020-08-31T15:50:07.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N - 1,并且房间里可能有一些钥匙能使你进入下一个房间。在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0, 1,…,N - 1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。</p><p>最初,除 0 号房间外的其余所有房间都被锁住。你可以自由地在房间之间来回走动。如果能进入每个房间返回 true,否则返回 false。</p><p><strong>示例 1:</strong></p><blockquote><p>输入: [[1], [2], [3], []]<br>输出: true<br>解释:<br>我们从 0 号房间开始,拿到钥匙 1。<br>之后我们去 1 号房间,拿到钥匙 2。<br>然后我们去 2 号房间,拿到钥匙 3。<br>最后我们去了 3 号房间。<br>由于我们能够进入每个房间,我们返回 true。</p></blockquote><p><strong>示例 2:</strong></p><blockquote><p>输入:[[1, 3], [3, 0, 1], [2], [0]]<br>输出:false<br>解释:我们不能进入 2 号房间。</p></blockquote><p><strong>提示:</strong></p><ul><li>1 <= rooms.length <= 1000</li><li>0 <= rooms[i].length <= 1000</li><li>所有房间中的钥匙数量总计不超过 3000。</li></ul><span id="more"></span><h1 id="深度优先搜索"><a href="#深度优先搜索" class="headerlink" title="深度优先搜索"></a>深度优先搜索</h1><p>此题我们将房间理解成节点,房间 A 到房间 B 理解成边,这样这道题就变成了,我们从图的 0 点出发,能否到达所有节点的问题。</p><p>具体实现上,我们使用一个变量 count 记录访问过的房间数,每次访问过一个房间后就标记为访问过 <code>visited[room] = true</code>,并将 <code>count </code> ,如果最后 count 等于房间的数量,那么就说明可以访问所有的房间。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="type">boolean</span>[] visited;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canVisitAllRooms</span><span class="params">(List<List<Integer>> rooms)</span> {</span><br><span class="line"> visited = <span class="keyword">new</span> <span class="title class_">boolean</span>[rooms.size()];</span><br><span class="line"> visitRooms(rooms, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">return</span> count == rooms.size();</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_">visitRooms</span><span class="params">(List<List<Integer>> rooms, Integer room)</span> {</span><br><span class="line"> visited[room] = <span class="literal">true</span>;</span><br><span class="line"> count ;</span><br><span class="line"> List<Integer> keyList = rooms.get(room);</span><br><span class="line"> <span class="keyword">for</span> (Integer nextRoom : keyList) {</span><br><span class="line"> <span class="keyword">if</span> (!visited[nextRoom]) {</span><br><span class="line"> visitRooms(rooms, nextRoom);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n m),其中 n 是房间的数量,m 是所有房间中的钥匙数量的总数。</li><li>空间复杂度:O(n),其中 n 是房间的数量。主要为栈空间的开销。</li></ul><h1 id="广度优先搜索"><a href="#广度优先搜索" class="headerlink" title="广度优先搜索"></a>广度优先搜索</h1><p>同样的,我们可以使用广度优先搜索解决此问题。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">canVisitAllRooms</span><span class="params">(List<List<Integer>> rooms)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> rooms.size();</span><br><span class="line"> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">boolean</span>[] visited = <span class="keyword">new</span> <span class="title class_">boolean</span>[n];</span><br><span class="line"> Queue<Integer> queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span><Integer>();</span><br><span class="line"> visited[<span class="number">0</span>] = <span class="literal">true</span>;</span><br><span class="line"> queue.offer(<span class="number">0</span>);</span><br><span class="line"> <span class="keyword">while</span> (!queue.isEmpty()) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">room</span> <span class="operator">=</span> queue.poll();</span><br><span class="line"> count ;</span><br><span class="line"> <span class="keyword">for</span> (<span class="type">int</span> key : rooms.get(room)) {</span><br><span class="line"> <span class="keyword">if</span> (!visited[key]) {</span><br><span class="line"> visited[key] = <span class="literal">true</span>;</span><br><span class="line"> queue.offer(key);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> count == n;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n m),其中 n 是房间的数量,m 是所有房间中的钥匙数量的总数。</li><li>空间复杂度:O(n),其中 n 是房间的数量。主要为队列的开销。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/keys-and-rooms/" title="钥匙和房间 | 力扣(LeetCode)">钥匙和房间 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/keys-and-rooms/solution/yao-chi-he-fang-jian-by-leetcode-solution/" title="钥匙和房间 | 题解(LeetCode)">钥匙和房间 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N - 1,并且房间里可能有一些钥匙能使你进入下一个房间。在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0, 1,…,N - 1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。</p>
<p>最初,除 0 号房间外的其余所有房间都被锁住。你可以自由地在房间之间来回走动。如果能进入每个房间返回 true,否则返回 false。</p>
<p><strong>示例 1:</strong></p>
<blockquote>
<p>输入: [[1], [2], [3], []]<br>输出: true<br>解释:<br>我们从 0 号房间开始,拿到钥匙 1。<br>之后我们去 1 号房间,拿到钥匙 2。<br>然后我们去 2 号房间,拿到钥匙 3。<br>最后我们去了 3 号房间。<br>由于我们能够进入每个房间,我们返回 true。</p>
</blockquote>
<p><strong>示例 2:</strong></p>
<blockquote>
<p>输入:[[1, 3], [3, 0, 1], [2], [0]]<br>输出:false<br>解释:我们不能进入 2 号房间。</p>
</blockquote>
<p><strong>提示:</strong></p>
<ul>
<li>1 &lt;= rooms.length &lt;= 1000</li>
<li>0 &lt;= rooms[i].length &lt;= 1000</li>
<li>所有房间中的钥匙数量总计不超过 3000。</li>
</ul></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="队列" scheme="https://www.cylong.com/tags/队列/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="广度优先搜索" scheme="https://www.cylong.com/tags/广度优先搜索/"/>
<category term="图" scheme="https://www.cylong.com/tags/图/"/>
</entry>
<entry>
<title>重新安排行程</title>
<link href="https://www.cylong.com/blog/2020/08/28/reconstruct-itinerary/"/>
<id>https://www.cylong.com/blog/2020/08/28/reconstruct-itinerary/</id>
<published>2020-08-28T15:10:29.000Z</published>
<updated>2020-08-28T15:10:29.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。</p><p><strong>提示:</strong></p><ul><li>如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。</li><li>所有的机场都用三个大写字母表示(机场代码)。</li><li>假定所有机票至少存在一种合理的行程。</li><li>所有的机票必须都用一次且只能用一次。</li></ul><p><strong>示例 1:</strong></p><blockquote><p>输入:[[“MUC”, “LHR”], [“JFK”, “MUC”], [“SFO”, “SJC”], [“LHR”, “SFO”]]<br>输出:[“JFK”, “MUC”, “LHR”, “SFO”, “SJC”]</p></blockquote><p><strong>示例 2:</strong></p><blockquote><p>输入:[[“JFK”, “SFO”],[“JFK”, “ATL”],[“SFO”, “ATL”],[“ATL”, “JFK”],[“ATL”, “SFO”]]<br>输出:[“JFK”, “ATL”, “JFK”, “SFO”, “ATL”, “SFO”]<br>解释:另一种有效的行程是 [“JFK”, “SFO”, “ATL”, “JFK”, “ATL”, “SFO”]。但是它自然排序更大更靠后。</p></blockquote><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>本题是一道求解欧拉回路/欧拉通路的问题。也叫「一笔画」问题,下面给出定义。</p><ul><li>通过图中所有边恰好一次且行遍所有顶点的通路称为欧拉通路。</li><li>通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。</li><li>具有欧拉回路的无向图称为欧拉图。</li><li>具有欧拉通路但不具有欧拉回路的无向图称为半欧拉图。</li></ul><p>因为本题保证至少存在一种合理的路径,也就告诉了我们,这张图是一个欧拉图或者半欧拉图。我们只需要输出这条欧拉通路的路径即可。如果没有保证至少存在一种合理的路径,我们需要判别这张图是否是欧拉图或者半欧拉图,具体地:</p><ul><li><p>对于无向图 G,G 是欧拉图当且仅当 G 是连通的且没有奇度顶点。</p></li><li><p>对于无向图 G,G 是半欧拉图当且仅当 G 是连通的且 G 中恰有 2 个奇度顶点。</p></li><li><p>对于有向图 G,G 是欧拉图当且仅当 G 的所有顶点属于同一个强连通分量且每个顶点的入度和出度相同。</p></li><li><p>对于有向图 G,G 是半欧拉图当且仅当 G 的所有顶点属于同一个强连通分量且。</p></li><li><p>恰有一个顶点的出度与入度差为 1;</p></li><li><p>恰有一个顶点的入度与出度差为 1;</p></li><li><p>所有其他顶点的入度和出度相同。</p><p>接下来我们考虑如下的行程:合法路径为 JFK→BBB→JFK→AAA</p></li></ul><img src="/blog/2020/08/28/reconstruct-itinerary/欧拉通路.png" class="" title="欧拉通路"><p>算法 Hierholzer 算法用于在连通图中寻找欧拉路径,其流程如下:</p><ol><li>从起点出发,进行深度优先搜索。</li><li>每次沿着某条边从某个顶点移动到另外一个顶点的时候,都需要删除这条边。</li><li>如果没有可移动的路径,则将所在节点加入到栈中,并返回。</li></ol><p>当我们顺序地考虑该问题时,我们也许很难解决该问题,根据上图我们可以发现,如果我们先走到 AAA 的顶点,就回不去了,我们走入了「死胡同」,从而导致无法遍历到其他还未访问的边。于是我们希望能够遍历完当前节点所连接的其他节点后再进入「死胡同」。</p><blockquote><p>注意对于每一个节点,它只有最多一个「死胡同」分支。依据前言中对于半欧拉图的描述,只有那个入度与出度差为 1 的节点会导致死胡同。</p></blockquote><p>不妨倒过来思考。我们注意到只有那个入度与出度差为 1 的节点会导致死胡同。而该节点必然是最后一个遍历到的节点。我们可以改变入栈的规则,当我们遍历完一个节点所连的所有节点后,我们才将该节点入栈(即逆序入栈)。对于当前节点而言,从它的每一个非「死胡同」分支出发进行深度优先搜索,都将会搜回到当前节点。而从它的「死胡同」分支出发进行深度优先搜索将不会搜回到当前节点。也就是说当前节点的死胡同分支将会优先于其他非「死胡同」分支入栈。另外为了保证我们能够快速找到当前节点所连的节点中字典序最小的那一个,我们可以使用优先队列存储当前节点所连到的点。</p><p>这样就能保证我们可以「一笔画」地走完所有边,最终的栈中逆序地保存了「一笔画」的结果。我们只要将栈中的内容反转,即可得到答案。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">Map<String, PriorityQueue<String>> graph = <span class="keyword">new</span> <span class="title class_">HashMap</span><>();</span><br><span class="line">List<String> ans = <span class="keyword">new</span> <span class="title class_">LinkedList</span><>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> List<String> <span class="title function_">findItinerary</span><span class="params">(List<List<String>> tickets)</span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (List<String> ticket : tickets) {</span><br><span class="line"> <span class="keyword">if</span> (graph.containsKey(ticket.get(<span class="number">0</span>))) {</span><br><span class="line"> graph.get(ticket.get(<span class="number">0</span>)).add(ticket.get(<span class="number">1</span>));</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> PriorityQueue<String> queue = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span><>();</span><br><span class="line"> queue.add(ticket.get(<span class="number">1</span>));</span><br><span class="line"> graph.put(ticket.get(<span class="number">0</span>), queue);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> buildEulerPath(<span class="string">"JFK"</span>);</span><br><span class="line"> Collections.reverse(ans);</span><br><span class="line"> <span class="keyword">return</span> ans;</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_">buildEulerPath</span><span class="params">(String travel)</span> {</span><br><span class="line"> <span class="keyword">while</span> (graph.containsKey(travel) && graph.get(travel).size() > <span class="number">0</span>) {</span><br><span class="line"> <span class="type">String</span> <span class="variable">tmpTravel</span> <span class="operator">=</span> graph.get(travel).poll();</span><br><span class="line"> buildEulerPath(tmpTravel);</span><br><span class="line"> }</span><br><span class="line"> ans.add(travel);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(mlogm),其中 m 是边的数量。对于每一条边我们需要 O(logm) 地删除它,最终的答案序列长度为 m 1。</li><li>空间复杂度:O(m),其中 m 是边的数量。我们需要存储每一条边。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/reconstruct-itinerary/" title="重新安排行程 | 力扣(LeetCode)">重新安排行程 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/reconstruct-itinerary/solution/zhong-xin-an-pai-xing-cheng-by-leetcode-solution/" title="重新安排行程 | 题解(LeetCode)">重新安排行程 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个机票的字符串二维数组 [from, to],子数组中的两个成员分别表示飞机出发和降落的机场地点,对该行程进行重新规划排序。所有这些机票都属于一个从 JFK(肯尼迪国际机场)出发的先生,所以该行程必须从 JFK 开始。</p>
<p><strong>提示:</strong></p>
<ul>
<li>如果存在多种有效的行程,请你按字符自然排序返回最小的行程组合。例如,行程 [“JFK”, “LGA”] 与 [“JFK”, “LGB”] 相比就更小,排序更靠前。</li>
<li>所有的机场都用三个大写字母表示(机场代码)。</li>
<li>假定所有机票至少存在一种合理的行程。</li>
<li>所有的机票必须都用一次且只能用一次。</li>
</ul>
<p><strong>示例 1:</strong></p>
<blockquote>
<p>输入:[[“MUC”, “LHR”], [“JFK”, “MUC”], [“SFO”, “SJC”], [“LHR”, “SFO”]]<br>输出:[“JFK”, “MUC”, “LHR”, “SFO”, “SJC”]</p>
</blockquote>
<p><strong>示例 2:</strong></p>
<blockquote>
<p>输入:[[“JFK”, “SFO”],[“JFK”, “ATL”],[“SFO”, “ATL”],[“ATL”, “JFK”],[“ATL”, “SFO”]]<br>输出:[“JFK”, “ATL”, “JFK”, “SFO”, “ATL”, “SFO”]<br>解释:另一种有效的行程是 [“JFK”, “SFO”, “ATL”, “JFK”, “ATL”, “SFO”]。但是它自然排序更大更靠后。</p>
</blockquote></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="回溯算法" scheme="https://www.cylong.com/tags/回溯算法/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="图" scheme="https://www.cylong.com/tags/图/"/>
<category term="优先队列" scheme="https://www.cylong.com/tags/优先队列/"/>
<category term="欧拉路径" scheme="https://www.cylong.com/tags/欧拉路径/"/>
</entry>
<entry>
<title>数字范围按位与</title>
<link href="https://www.cylong.com/blog/2020/08/26/bitwise-and-of-numbers-range/"/>
<id>https://www.cylong.com/blog/2020/08/26/bitwise-and-of-numbers-range/</id>
<published>2020-08-26T14:52:24.000Z</published>
<updated>2020-08-26T14:52:24.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定范围 [m, n],其中 0 <= m <= n <= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。</p><p><strong>示例 1:</strong></p><blockquote><p>输入: [5, 7]<br>输出: 4</p></blockquote><p><strong>示例 2:</strong></p><blockquote><p>输入: [0, 1]<br>输出: 0</p></blockquote><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>我们观察按位与运算的性质。对于一系列的位,例如 [1, 1, 0, 1, 1],只要有一个零的位,那么这一系列位的按位与运算结果都将为零。对于此题,我们将一系列数字变成二进制展示,如下:</p><table><thead><tr><th align="center">-</th><th align="center">1</th><th align="center">2</th><th align="center">3</th><th align="center">4</th><th align="center">5</th><th align="center">6</th><th align="center">7</th><th align="center">8</th></tr></thead><tbody><tr><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td></tr><tr><td align="center">1</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td></tr><tr><td align="center">2</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td></tr><tr><td align="center">3</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td></tr><tr><td align="center">4</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">0</td></tr><tr><td align="center">5</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">1</td></tr><tr><td align="center">6</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td><td align="center">0</td></tr><tr><td align="center">7</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td><td align="center">1</td></tr><tr><td align="center">8</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">0</td><td align="center">0</td></tr><tr><td align="center">9</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">0</td><td align="center">1</td></tr><tr><td align="center">10</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">1</td><td align="center">0</td></tr><tr><td align="center">11</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">1</td><td align="center">1</td></tr><tr><td align="center">12</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td><td align="center">0</td><td align="center">0</td></tr><tr><td align="center">13</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td><td align="center">0</td><td align="center">1</td></tr><tr><td align="center">14</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td><td align="center">1</td><td align="center">0</td></tr><tr><td align="center">15</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">1</td><td align="center">1</td><td align="center">1</td></tr><tr><td align="center">16</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">1</td><td align="center">0</td><td align="center">0</td><td align="center">0</td><td align="center">0</td></tr></tbody></table><p>上面表格中,我将[0, 16] 全部展开成8进制展示,上图中,我们任意取出一个范围,比如[9, 12],我们可以发现,对所有数字执行按位与运算的结果是所有对应二进制字符串的公共前缀再用零补上后面的剩余位。那么这个规律是否正确呢?我们可以进行简单的证明。假设对于所有这些二进制串,前 i 位均相同,第 i 1 位开始不同,由于 [m, n] 连续,所以第 i 1 位在 [m, n] 的数字范围从小到大列举出来一定是前面全部是 0,后面全部是 1,在上图中对应 [9, 11] 均为 0,[12, 12] 均为 1。并且一定存在连续的两个数 x 和 x 1,满足 x 的第 i 1 位为 0,后面全为 1,x 1 的第 i 1 位为 1,后面全为 0,对应上图中的例子即为 11 和 12。这种形如 0111… 和 1000… 的二进制串的按位与的结果一定为 0000…,因此第 i 1 位开始的剩余位均为 0,前 i 位由于均相同,因此按位与结果不变。最后的答案即为二进制字符串的公共前缀再用零补上后面的剩余位。</p><p>进一步来说,所有这些二进制字符串的公共前缀也即指定范围的起始和结束数字 m 和 n 的公共前缀(即在上面的示例中分别为 9 和 12)。因此,最终我们可以将问题重新表述为:给定两个整数,我们要找到它们对应的二进制字符串的公共前缀。</p><h1 id="位移操作"><a href="#位移操作" class="headerlink" title="位移操作"></a>位移操作</h1><p>我们的想法是将两个数字不断向右移动,直到数字相等,即数字被缩减为它们的公共前缀。计算移动的次数,然后,通过将公共前缀向左移动相同次数,将零添加到公共前缀的右边以获得最终结果。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">rangeBitwiseAnd</span><span class="params">(<span class="type">int</span> m, <span class="type">int</span> n)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (m != n) {</span><br><span class="line"> m = m >> <span class="number">1</span>;</span><br><span class="line"> n = n >> <span class="number">1</span>;</span><br><span class="line"> count ;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> m << count;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(logn)。算法的时间复杂度取决于 m 和 n 的二进制位数,由于 m ≤ n,因此时间复杂度取决于 n 的二进制位数。</li><li>空间复杂度:O(1)。我们只需要常数空间存放若干变量。</li></ul><h1 id="Brian-Kernighan-算法"><a href="#Brian-Kernighan-算法" class="headerlink" title="Brian Kernighan 算法"></a>Brian Kernighan 算法</h1><p>还有一个位移相关的算法叫做「Brian Kernighan 算法」,它用于清除二进制串中最右边的 1。Brian Kernighan 算法的关键在于我们每次对 n 和 n − 1 之间进行按位与运算后,n 中最右边的 1 会被抹去变成 0。</p><img src="/blog/2020/08/26/bitwise-and-of-numbers-range/BK算法.png" class="" title="BK算法"><p>基于上述技巧,我们可以用它来计算两个二进制字符串的公共前缀。其思想是,对于给定的范围 [m, n](m < n),我们可以对数字 n 迭代地应用上述技巧,清除最右边的 1,直到它小于或等于 m,此时非公共前缀部分的 1 均被消去。因此最后我们返回 n 即可。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">rangeBitwiseAnd</span><span class="params">(<span class="type">int</span> m, <span class="type">int</span> n)</span> {</span><br><span class="line"> <span class="keyword">while</span> (m < n) {</span><br><span class="line"> n = n & (n - <span class="number">1</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> n;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析-1"><a href="#复杂度分析-1" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(logn)。和位移方法类似,算法的时间复杂度取决于 m 和 n 二进制展开的位数。尽管和位移方法具有相同的渐近复杂度,但 Brian Kernighan 的算法需要的迭代次数会更少,因为它跳过了两个数字之间的所有零位。</li><li>空间复杂度:O(1)。我们只需要常数空间存放若干变量。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/" title="数字范围按位与 | 力扣(LeetCode)">数字范围按位与 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/bitwise-and-of-numbers-range/solution/shu-zi-fan-wei-an-wei-yu-by-leetcode-solution/" title="数字范围按位与 | 题解(LeetCode)">数字范围按位与 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定范围 [m, n],其中 0 &lt;= m &lt;= n &lt;= 2147483647,返回此范围内所有数字的按位与(包含 m, n 两端点)。</p>
<p><strong>示例 1:</strong></p>
<blockquote>
<p>输入: [5, 7]<br>输出: 4</p>
</blockquote>
<p><strong>示例 2:</strong></p>
<blockquote>
<p>输入: [0, 1]<br>输出: 0</p>
</blockquote></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="数学" scheme="https://www.cylong.com/tags/数学/"/>
<category term="位运算" scheme="https://www.cylong.com/tags/位运算/"/>
<category term="Brian Kernighan 算法" scheme="https://www.cylong.com/tags/Brian-Kernighan-算法/"/>
</entry>
<entry>
<title>将二叉搜索树变平衡</title>
<link href="https://www.cylong.com/blog/2020/08/20/balance-a-binary-search-tree/"/>
<id>https://www.cylong.com/blog/2020/08/20/balance-a-binary-search-tree/</id>
<published>2020-08-20T08:25:13.000Z</published>
<updated>2020-08-20T08:25:13.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给你一棵二叉搜索树,请你返回一棵平衡后的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是平衡的。如果有多种构造方法,请你返回任意一种。</p><p><strong>示例:</strong></p><img src="/blog/2020/08/20/balance-a-binary-search-tree/二叉搜索树.png" class="" title="二叉搜索树"><img src="/blog/2020/08/20/balance-a-binary-search-tree/平衡二叉搜索树.png" class="" title="平衡二叉搜索树"><blockquote><p>输入:root = [1, null, 2, null, 3, null, 4, null, null]<br>输出:[2, 1, 3, null, null, null, 4]<br>解释:这不是唯一的正确答案,[3, 1, 4, null, 2, null, null] 也是一个可行的构造方案。</p></blockquote><p><strong>提示:</strong></p><ul><li>树节点的数目在 1 到 10^4 之间。</li><li>树节点的值互不相同,且在 1 到 10^5 之间。</li></ul><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>「平衡」要求它是一棵空树或它的左右两个子树的高度差的绝对值不超过 1,这很容易让我们产生这样的想法——左右子树的大小越「平均」,这棵树会不会越平衡?于是一种贪心策略就形成了:我们可以通过中序遍历将原来的二叉搜索树转化为一个有序序列,然后对这个有序序列递归建树,对于区间 [L, R]:</p><ul><li>取 <code>mid = (L R) / 2</code>,即中心位置做为当前节点的值。</li><li>如果 <code>L ≤ mid − 1</code>,那么递归地将区间 <code>[L, mid − 1]</code> 作为当前节点的左子树。</li><li>如果 <code>mid 1 ≤ R</code>,那么递归地将区间 <code>[mid 1, R]</code> 作为当前节点的右子树。</li></ul><p>经过证明此方法是可行的,关于证明方式在此不做赘述,想要了解的同学可以参考下面官方的题解。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><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">List<Integer> treeValList = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> TreeNode <span class="title function_">balanceBST</span><span class="params">(TreeNode root)</span> {</span><br><span class="line"> dfsGetTreeValList(root);</span><br><span class="line"> <span class="keyword">return</span> buildBalanceBST(<span class="number">0</span>, treeValList.size() - <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> TreeNode <span class="title function_">buildBalanceBST</span><span class="params">(<span class="type">int</span> left, <span class="type">int</span> right)</span> {</span><br><span class="line"> <span class="type">int</span> <span class="variable">mid</span> <span class="operator">=</span> right - ((right - left) >> <span class="number">1</span>);</span><br><span class="line"> <span class="type">TreeNode</span> <span class="variable">node</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TreeNode</span>(treeValList.get(mid));</span><br><span class="line"> node.left = left < mid ? buildBalanceBST(left, mid - <span class="number">1</span>) : <span class="literal">null</span>;</span><br><span class="line"> node.right = mid < right ? buildBalanceBST(mid <span class="number">1</span>, right) : <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">return</span> node;</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_">dfsGetTreeValList</span><span class="params">(TreeNode node)</span> {</span><br><span class="line"> <span class="keyword">if</span> (node == <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> dfsGetTreeValList(node.left);</span><br><span class="line"> treeValList.add(node.val);</span><br><span class="line"> dfsGetTreeValList(node.right);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span> {</span><br><span class="line"> <span class="type">int</span> 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 class="type">int</span> x) {</span><br><span class="line"> val = x;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),获得中序遍历的时间代价是 O(n);建立平衡二叉树的时建立每个点的时间代价为 O(1),总时间也是 O(n)。故渐进时间复杂度为 O(n)。</li><li>空间复杂度:O(n),这里使用了一个数组作为辅助空间,存放中序遍历后的有序序列,故渐进空间复杂度为 O(n)。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/balance-a-binary-search-tree/" title="将二叉搜索树变平衡 | 力扣(LeetCode)">将二叉搜索树变平衡 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/balance-a-binary-search-tree/solution/jiang-er-cha-sou-suo-shu-bian-ping-heng-by-leetcod/" title="将二叉搜索树变平衡 | 题解(LeetCode)">将二叉搜索树变平衡 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给你一棵二叉搜索树,请你返回一棵平衡后的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是平衡的。如果有多种构造方法,请你返回任意一种。</p>
<p><strong>示例:</strong></p>
<img src="/blog/2020/08/20/balance-a-binary-search-tree/二叉搜索树.png" class="" title="二叉搜索树">
<img src="/blog/2020/08/20/balance-a-binary-search-tree/平衡二叉搜索树.png" class="" title="平衡二叉搜索树">
<blockquote>
<p>输入:root = [1, null, 2, null, 3, null, 4, null, null]<br>输出:[2, 1, 3, null, null, null, 4]<br>解释:这不是唯一的正确答案,[3, 1, 4, null, 2, null, null] 也是一个可行的构造方案。</p>
</blockquote>
<p><strong>提示:</strong></p>
<ul>
<li>树节点的数目在 1 到 10^4 之间。</li>
<li>树节点的值互不相同,且在 1 到 10^5 之间。</li>
</ul></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="贪心算法" scheme="https://www.cylong.com/tags/贪心算法/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="树" scheme="https://www.cylong.com/tags/树/"/>
<category term="二叉树" scheme="https://www.cylong.com/tags/二叉树/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="中序遍历" scheme="https://www.cylong.com/tags/中序遍历/"/>
<category term="二叉搜索树" scheme="https://www.cylong.com/tags/二叉搜索树/"/>
<category term="平衡二叉树" scheme="https://www.cylong.com/tags/平衡二叉树/"/>
</entry>
<entry>
<title>平衡二叉树</title>
<link href="https://www.cylong.com/blog/2020/08/17/balanced-binary-tree/"/>
<id>https://www.cylong.com/blog/2020/08/17/balanced-binary-tree/</id>
<published>2020-08-17T07:19:04.000Z</published>
<updated>2020-08-17T07:19:04.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。</p><p><strong>示例 1:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">给定二叉树 [3, 9, 20, null, null, 15, 7]</span><br><span class="line"> 3</span><br><span class="line"> / \</span><br><span class="line"> 9 20</span><br><span class="line"> / \</span><br><span class="line"> 15 7</span><br><span class="line">返回 true 。</span><br></pre></td></tr></table></figure><p><strong>示例 2:</strong></p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">给定二叉树 [1, 2, 2, 3, 3, null, null, 4, 4]</span><br><span class="line"> 1</span><br><span class="line"> / \</span><br><span class="line"> 2 2</span><br><span class="line"> / \</span><br><span class="line"> 3 3</span><br><span class="line"> / \</span><br><span class="line"> 4 4</span><br><span class="line">返回 false 。</span><br></pre></td></tr></table></figure><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>根据题意一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。那么我们可以递归的判断一棵树的左右子树,判断是否是平衡二叉树。对于当前遍历到的节点,先递归地判断其左右子树是否平衡,再判断以当前节点为根的子树是否平衡。如果一棵子树是平衡的,则返回其高度(高度一定是非负整数),否则返回 -1。如果存在一棵子树不平衡,则整个二叉树一定不平衡。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><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> height(root) != -<span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">height</span><span class="params">(TreeNode node)</span> {</span><br><span class="line"> <span class="keyword">if</span> (node == <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><br><span class="line"> <span class="type">int</span> <span class="variable">leftHeight</span> <span class="operator">=</span> height(node.left);</span><br><span class="line"> <span class="type">int</span> <span class="variable">rightHeight</span> <span class="operator">=</span> height(node.right);</span><br><span class="line"> <span class="keyword">if</span> (leftHeight == -<span class="number">1</span> || rightHeight == -<span class="number">1</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 class="keyword">else</span> {</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></table></figure><p>对于上述代码,我们还可以简单优化下,如果左子树不是平衡的,那么也就不需要再递归的求右子树是否平衡了。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><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> height(root) != -<span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">height</span><span class="params">(TreeNode node)</span> {</span><br><span class="line"> <span class="keyword">if</span> (node == <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><br><span class="line"> <span class="type">int</span> leftHeight;</span><br><span class="line"> <span class="type">int</span> rightHeight;</span><br><span class="line"> <span class="keyword">if</span> ((leftHeight = height(node.left)) == -<span class="number">1</span></span><br><span class="line"> || (rightHeight = height(node.right)) == -<span class="number">1</span></span><br><span class="line"> || 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 class="keyword">else</span> {</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></table></figure><p>另外也可以先看一道简单的求树的高度的题:<a href="/blog/2020/07/28/maximum-depth-of-binary-tree/">二叉树的最大深度 | 笑话人生</a></p><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(n),其中 n 是二叉树中的节点个数。使用自底向上的递归,每个节点的计算高度和判断是否平衡都只需要处理一次,最坏情况下需要遍历二叉树中的所有节点,因此时间复杂度是 O(n)。</li><li>空间复杂度:O(n),其中 n 是二叉树中的节点个数。空间复杂度主要取决于递归调用的层数,递归调用的层数不会超过 n。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/balanced-binary-tree/" title="平衡二叉树 | 力扣(LeetCode)">平衡二叉树 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/balanced-binary-tree/solution/ping-heng-er-cha-shu-by-leetcode-solution/" title="平衡二叉树 | 题解(LeetCode)">平衡二叉树 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定一个二叉树,判断它是否是高度平衡的二叉树。本题中,一棵高度平衡二叉树定义为:一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1。</p>
<p><strong>示例 1:</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">给定二叉树 [3, 9, 20, null, null, 15, 7]</span><br><span class="line"> 3</span><br><span class="line"> / \</span><br><span class="line"> 9 20</span><br><span class="line"> / \</span><br><span class="line"> 15 7</span><br><span class="line">返回 true 。</span><br></pre></td></tr></table></figure>
<p><strong>示例 2:</strong></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">给定二叉树 [1, 2, 2, 3, 3, null, null, 4, 4]</span><br><span class="line"> 1</span><br><span class="line"> / \</span><br><span class="line"> 2 2</span><br><span class="line"> / \</span><br><span class="line"> 3 3</span><br><span class="line"> / \</span><br><span class="line"> 4 4</span><br><span class="line">返回 false 。</span><br></pre></td></tr></table></figure></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="递归" scheme="https://www.cylong.com/tags/递归/"/>
<category term="树" scheme="https://www.cylong.com/tags/树/"/>
<category term="二叉树" scheme="https://www.cylong.com/tags/二叉树/"/>
<category term="深度优先搜索" scheme="https://www.cylong.com/tags/深度优先搜索/"/>
<category term="平衡二叉树" scheme="https://www.cylong.com/tags/平衡二叉树/"/>
</entry>
<entry>
<title>区间列表的交集</title>
<link href="https://www.cylong.com/blog/2020/08/15/interval-list-intersections/"/>
<id>https://www.cylong.com/blog/2020/08/15/interval-list-intersections/</id>
<published>2020-08-15T06:31:00.000Z</published>
<updated>2020-08-15T06:31:00.000Z</updated>
<content type="html"><![CDATA[<hr><h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定两个由一些闭区间组成的列表,每个区间列表都是成对不相交的,并且已经排序。返回这两个区间列表的交集。<br>(形式上,闭区间 [a, b](其中 <code>a <= b</code>)表示实数 x 的集合,而 <code>a <= x <= b</code>。两个闭区间的交集是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3]。)</p><p><strong>示例:</strong></p><img src="/blog/2020/08/15/interval-list-intersections/闭区间列表.png" class="" title="闭区间列表"><blockquote><p>输入:A = [[0, 2], [5, 10], [13, 23], [24, 25]], B = [[1, 5], [8, 12], [15, 24], [25, 26]]<br>输出:[[1, 2], [5, 5], [8, 10], [15, 23], [24, 24], [25, 25]]</p></blockquote><p>提示:</p><ul><li>0 <= A.length < 1000</li><li>0 <= B.length < 1000</li><li>0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9</li></ul><span id="more"></span><h1 id="题解"><a href="#题解" class="headerlink" title="题解"></a>题解</h1><p>最开始我的想法是使用一个指针 start 扫描两个闭区间的值,判断当前 start 的值是否在 A[indexA] 和 B[indexB] 的区间内,发现进入到区间后,那么我们再引入 end 指针,值为 start 的值,然后移动 end 指针,直到出了 A[indexA] 或者 B[indexB] 的区间范围,那么 start 和 end - 1 的值就是两个区间的交集。然后将 end 的值赋值给 start 并判断 start ,如果超出了 A[indexA] 或者 B[indexB] 的区间,则分别进行 <code>indexA </code> 或者 <code>indexB </code> 的操作。代码如下:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="type">int</span>[][] intervalIntersection(<span class="type">int</span>[][] A, <span class="type">int</span>[][] B) {</span><br><span class="line"> <span class="keyword">if</span> (A == <span class="literal">null</span> || B == <span class="literal">null</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"> List<<span class="type">int</span>[]> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">indexA</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">indexB</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span>[] common = <span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">2</span>];</span><br><span class="line"> <span class="keyword">while</span> (indexA < A.length && indexB < B.length) {</span><br><span class="line"> <span class="keyword">if</span> (A[indexA][<span class="number">0</span>] <= start && start <= A[indexA][<span class="number">1</span>] && B[indexB][<span class="number">0</span>] <= start && start <= B[indexB][<span class="number">1</span>]) {</span><br><span class="line"> common[<span class="number">0</span>] = start;</span><br><span class="line"> <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> start;</span><br><span class="line"> <span class="keyword">while</span> (A[indexA][<span class="number">0</span>] <= end && end <= A[indexA][<span class="number">1</span>] && B[indexB][<span class="number">0</span>] <= end && end <= B[indexB][<span class="number">1</span>]) {</span><br><span class="line"> end ;</span><br><span class="line"> }</span><br><span class="line"> common[<span class="number">1</span>] = end - <span class="number">1</span>;</span><br><span class="line"> ans.add(common.clone());</span><br><span class="line"> start = end;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> start ;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (start > A[indexA][<span class="number">1</span>]) {</span><br><span class="line"> indexA ;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (start > B[indexB][<span class="number">1</span>]) {</span><br><span class="line"> indexB ;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans.toArray(<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>][]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面的方法在提交后超时了,分析用例和代码发现,上面的代码有以下两个问题:</p><ul><li>start 的值从 0 开始,如果 A[0] 和 B[0] 的起始值比较大,那么就做了很多无用的 <code>start </code> 操作。</li><li>end 的值从 start 开始,遍历到区间的最大值,如果区间范围过大,也会导致频繁的 <code>end </code>。</li></ul><p>于是我们根据上面的问题进行优化,先从 A[0] 和 B[0] 开始找规律,假设两个闭区间有交集,那么我们可以发现,交集的起始值 <code>start = max(A[0][0], B[0][0])</code>,交集的终止值 <code>end = min(A[0][1], B[0][1])</code>。这样我们相比上面的方法,减少了很多无用的 <code> </code> 操作。延申而来,对于任意的 A[indexA] 和 B[indexB] 都可以这样求出交集。但是如果求出 <code>start > end</code> 则认为这两个区间没有交集,然后我们对于提前结束的集合,即集合的最大值等于 end 的集合,我们对其指针进行 <code>index </code> 操作。因为较早结束的集合,已经计算完交集了,而另外一个范围比较大的集合,还有有值没有计算是否相交。下面看代码将会更好的理解。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><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="keyword">public</span> <span class="type">int</span>[][] intervalIntersection(<span class="type">int</span>[][] A, <span class="type">int</span>[][] B) {</span><br><span class="line"> <span class="keyword">if</span> (A == <span class="literal">null</span> || B == <span class="literal">null</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"> List<<span class="type">int</span>[]> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">indexA</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">indexB</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (indexA < A.length && indexB < B.length) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> Math.max(A[indexA][<span class="number">0</span>], B[indexB][<span class="number">0</span>]);</span><br><span class="line"> <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> Math.min(A[indexA][<span class="number">1</span>], B[indexB][<span class="number">1</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (start <= end) {</span><br><span class="line"> ans.add(<span class="keyword">new</span> <span class="title class_">int</span>[]{start, end});</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (A[indexA][<span class="number">1</span>] == end) {</span><br><span class="line"> indexA ;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (B[indexB][<span class="number">1</span>] == end) {</span><br><span class="line"> indexB ;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans.toArray(<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>][]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>进一步我们使用条件运算符优化下 16 行开始的代码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span>[][] intervalIntersection(<span class="type">int</span>[][] A, <span class="type">int</span>[][] B) {</span><br><span class="line"> <span class="keyword">if</span> (A == <span class="literal">null</span> || B == <span class="literal">null</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"> List<<span class="type">int</span>[]> ans = <span class="keyword">new</span> <span class="title class_">ArrayList</span><>();</span><br><span class="line"> <span class="type">int</span> <span class="variable">indexA</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="type">int</span> <span class="variable">indexB</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span> (indexA < A.length && indexB < B.length) {</span><br><span class="line"> <span class="type">int</span> <span class="variable">start</span> <span class="operator">=</span> Math.max(A[indexA][<span class="number">0</span>], B[indexB][<span class="number">0</span>]);</span><br><span class="line"> <span class="type">int</span> <span class="variable">end</span> <span class="operator">=</span> A[indexA][<span class="number">1</span>] < B[indexB][<span class="number">1</span>] ? A[indexA ][<span class="number">1</span>] : B[indexB ][<span class="number">1</span>];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (start <= end) {</span><br><span class="line"> ans.add(<span class="keyword">new</span> <span class="title class_">int</span>[]{start, end});</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans.toArray(<span class="keyword">new</span> <span class="title class_">int</span>[<span class="number">0</span>][]);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="复杂度分析"><a href="#复杂度分析" class="headerlink" title="复杂度分析"></a>复杂度分析</h2><ul><li>时间复杂度:O(M N),其中 M, N 分别是数组 A 和 B 的长度。</li><li>空间复杂度:O(M N),答案中区间数量的上限。</li></ul><h1 id="来源"><a href="#来源" class="headerlink" title="来源"></a>来源</h1><blockquote><p><a href="https://leetcode-cn.com/problems/interval-list-intersections/" title="区间列表的交集 | 力扣(LeetCode)">区间列表的交集 | 力扣(LeetCode)</a><br><a href="https://leetcode-cn.com/problems/interval-list-intersections/solution/qu-jian-lie-biao-de-jiao-ji-by-leetcode/" title="区间列表的交集 | 题解(LeetCode)">区间列表的交集 | 题解(LeetCode)</a></p></blockquote><hr>]]></content>
<summary type="html"><hr>
<h1 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h1><p>给定两个由一些闭区间组成的列表,每个区间列表都是成对不相交的,并且已经排序。返回这两个区间列表的交集。<br>(形式上,闭区间 [a, b](其中 <code>a &lt;= b</code>)表示实数 x 的集合,而 <code>a &lt;= x &lt;= b</code>。两个闭区间的交集是一组实数,要么为空集,要么为闭区间。例如,[1, 3] 和 [2, 4] 的交集为 [2, 3]。)</p>
<p><strong>示例:</strong></p>
<img src="/blog/2020/08/15/interval-list-intersections/闭区间列表.png" class="" title="闭区间列表">
<blockquote>
<p>输入:A = [[0, 2], [5, 10], [13, 23], [24, 25]], B = [[1, 5], [8, 12], [15, 24], [25, 26]]<br>输出:[[1, 2], [5, 5], [8, 10], [15, 23], [24, 24], [25, 25]]</p>
</blockquote>
<p>提示:</p>
<ul>
<li>0 &lt;= A.length &lt; 1000</li>
<li>0 &lt;= B.length &lt; 1000</li>
<li>0 &lt;= A[i].start, A[i].end, B[i].start, B[i].end &lt; 10^9</li>
</ul></summary>
<category term="LeetCode" scheme="https://www.cylong.com/categories/LeetCode/"/>
<category term="Java" scheme="https://www.cylong.com/tags/Java/"/>
<category term="LeetCode" scheme="https://www.cylong.com/tags/LeetCode/"/>
<category term="学习笔记" scheme="https://www.cylong.com/tags/学习笔记/"/>
<category term="数组" scheme="https://www.cylong.com/tags/数组/"/>
<category term="指针" scheme="https://www.cylong.com/tags/指针/"/>
<category term="双指针" scheme="https://www.cylong.com/tags/双指针/"/>
</entry>
</feed>