-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
4898 lines (4525 loc) · 323 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[剑指offer题解(Python实现)]]></title>
<url>/2019/01/31/%E5%89%91%E6%8C%87offer%E9%A2%98%E8%A7%A3%EF%BC%88Python%E5%AE%9E%E7%8E%B0%EF%BC%89/</url>
<content type="html"><![CDATA[<p>每个题目标题链接到 <a href="https://github.com/Hk4Fun/algorithm_offer/tree/master/target_offer" target="_blank" rel="external">github</a>,可以查看其他的解题思路、测试用例以及拓展题目</p>
<p>这里只贴上书中题目的最优解法和代码</p>
<p>另外,这里参考的是《剑指offer》第一版的题目顺序</p>
<p>补充部分添加的是《剑指offer》第二版多出来的几道题目</p>
<a id="more"></a>
<h2 id="二维数组中的查找"><a href="#二维数组中的查找" class="headerlink" title=" 二维数组中的查找"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/3_1_%E4%BA%8C%E7%BB%B4%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%9F%A5%E6%89%BE.py" target="_blank" rel="external"> 二维数组中的查找</a></h2><h3 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h3><p>在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。</p>
<p>请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。</p>
<h3 id="最优题解"><a href="#最优题解" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间(O(m + n)),空间(O(1))</p>
<p>从右上角或左下角开始查找,这里选择右上角</p>
<p>如果当前元素大于 target,剔除 target 所在列(col 左移 - 1)</p>
<p>如果当前元素小于 target,剔除 target 所在行(row 下移 + 1)</p>
<p>否则等于,结束查找</p>
<p>每一次查找都在数组的查找范围中剔除一行或一列,每一步都缩小了查找的范围</p>
<p>直到找到要查找的数字,或者查找范围为空</p>
<h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def Find(self, target, array):
if array is None: return False
i , j = 0, len(array[0]) - 1
while i < len(array) and j >= 0:
if array[i][j] > target:
j -= 1
elif array[i][j] < target:
i += 1
else:
return True
return False
</code></pre>
<h3 id="拓展题目"><a href="#拓展题目" class="headerlink" title="拓展题目"></a>拓展题目</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/3_2_%E6%8E%92%E5%BA%8F%E7%9F%A9%E9%98%B5%E4%B8%AD%E7%9A%84%E4%BB%8E%E5%B0%8F%E5%88%B0%E5%A4%A7%E7%AC%ACk%E4%B8%AA%E6%95%B0.py" target="_blank" rel="external">排序矩阵中从小到大第k个数</a></li>
</ul>
<h2 id="替换空格"><a href="#替换空格" class="headerlink" title="替换空格"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/4_%E6%9B%BF%E6%8D%A2%E7%A9%BA%E6%A0%BC.py" target="_blank" rel="external">替换空格</a></h2><h3 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h3><p>请实现一个函数,将一个字符串中的空格替换成 “%20”。</p>
<p>例如,当字符串为 “We Are Happy”,则经过替换之后的字符串为 “We%20Are%20Happy”。</p>
<h3 id="最优题解-1"><a href="#最优题解-1" class="headerlink" title="最优题解"></a>最优题解</h3><p>使用内置方法 replace() 最佳,这里给出书上的解法:</p>
<p>原地替换,需要移动替换位置之后的字符,若从左到右扫描,一边移动一边替换,时间复杂度为 O(n^2)</p>
<p>可以考虑从右到左扫描,使用两个索引</p>
<p>一个指向源字符串的末尾 oldIdx,另一个指向替换后字符串的末尾 newIdx</p>
<p> 没碰到空格时直接复制,碰到空格时 newIdx 左移 3 格写入 ‘20%’,oldIdx 左移一格</p>
<p>(每替换一个空格,长度增加 2,因此替换后字符串的长度 = 原来长度 + 2 * 空格数目)</p>
<p>直到 newIdx 越过 oldIdx 来到 oldIdx 的左边则扫描结束</p>
<p>注:如果是 c / c++ 可以实现原地替换,但 python 中的 str 为不可变对象,只能返回新的字符串</p>
<p>所以这里的原地替换只是模拟书中的方法,实际上还是返回一个新的字符串</p>
<h3 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def replaceSpace(self, s):
if s is None: return ''
spaceCount = sum(ch == ' ' for ch in s)
newIdx = len(s) + spaceCount * 2 - 1
oldIdx = len(s) - 1
newStr = list(s) + [''] * (spaceCount * 2)
while 0 <= oldIdx < newIdx:
if newStr[oldIdx] == ' ':
newStr[newIdx - 2: newIdx + 1] = '%20'
newIdx -= 3
else:
newStr[newIdx] = newStr[oldIdx]
newIdx -= 1
oldIdx -= 1
return ''.join(newStr)
</code></pre>
<h2 id="从尾到头打印链表"><a href="#从尾到头打印链表" class="headerlink" title="从尾到头打印链表"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/5_%E4%BB%8E%E5%B0%BE%E5%88%B0%E5%A4%B4%E6%89%93%E5%8D%B0%E9%93%BE%E8%A1%A8.py" target="_blank" rel="external">从尾到头打印链表</a></h2><h3 id="题目描述-2"><a href="#题目描述-2" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个链表,从尾到头打印链表每个节点的值(不能改变原链表结构)</p>
<h3 id="最优题解-2"><a href="#最优题解-2" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间(O(n)),空间(O(n))</p>
<p>可以自己用 list 模拟栈,也可以使用递归借助系统栈,这里使用递归实现</p>
<h3 id="代码-2"><a href="#代码-2" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回从尾部到头部的列表值序列
def ListReverse(self, listNode):
def recursive(node):
if node:
recursive(node.next)
res.append(node.val)
res = []
recursive(listNode)
return res
</code></pre>
<h2 id="重建二叉树"><a href="#重建二叉树" class="headerlink" title="重建二叉树"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/6_1_%E9%87%8D%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91%EF%BC%88%E5%89%8D%E5%BA%8F%E4%B8%AD%E5%BA%8F%EF%BC%89.py" target="_blank" rel="external">重建二叉树</a></h2><h3 id="题目描述-3"><a href="#题目描述-3" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。</p>
<p>假设输入的前序遍历和中序遍历的结果中都不含重复的数字。</p>
<h3 id="最优题解-3"><a href="#最优题解-3" class="headerlink" title="最优题解"></a>最优题解</h3><p>前序的第一个元素是根结点的值,在中序中找到该值</p>
<p>中序中该值的左边的元素是根结点的左子树,右边是右子树,然后递归的处理左边和右边</p>
<h3 id="代码-3"><a href="#代码-3" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回构造的TreeNode根节点
def reConstructBinaryTree(self, pre, tin):
def build(preL, preR, tinL, tinR):
if preL > preR or tinL > tinR: return
idx = tin.index(pre[preL])
node = TreeNode(pre[preL])
node.left = build(preL + 1, idx + preL - tinL, tinL, idx - 1)
node.right = build(idx + preL - tinL + 1, preR, idx + 1, tinR)
return node
return build(0, len(pre) - 1, 0, len(tin) - 1)
</code></pre>
<h3 id="举一反三"><a href="#举一反三" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/6_2_%E9%87%8D%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91%EF%BC%88%E4%B8%AD%E5%BA%8F%E5%90%8E%E5%BA%8F%EF%BC%89.py" target="_blank" rel="external">重建二叉树(中序后序)</a></li>
</ul>
<h2 id="用两个栈实现队列"><a href="#用两个栈实现队列" class="headerlink" title="用两个栈实现队列"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/7_1_%E7%94%A8%E4%B8%A4%E4%B8%AA%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97.py" target="_blank" rel="external">用两个栈实现队列</a></h2><h3 id="题目描述-4"><a href="#题目描述-4" class="headerlink" title="题目描述"></a>题目描述</h3><p>用两个栈来实现一个队列,完成队列的入队和出队操作</p>
<h3 id="最优题解-4"><a href="#最优题解-4" class="headerlink" title="最优题解"></a>最优题解</h3><p>stack1 用来入队,stack2 用来出队</p>
<p>出队时若 stack2 有数据直接弹出,无数据就要把 stack1 中的全部弹出并压入 stack2,然后 stack2 继续出队</p>
<p>入队时不管 stack1 有没有数据,直接压入</p>
<h3 id="代码-4"><a href="#代码-4" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def __init__(self):
self.stack1 = []
self.stack2 = []
def In(self, x): # 入队时不管stack1有没有数据,直接压入
self.stack1.append(x)
def Out(self):
if not self.stack1 and not self.stack2:
return
if not self.stack2: # stack2无数据就要把stack1中的全部弹出并压入stack2
while self.stack1:
self.stack2.append(self.stack1.pop())
return self.stack2.pop() # stack2继续出队
</code></pre>
<h3 id="举一反三-1"><a href="#举一反三-1" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/7_2_%E7%94%A8%E4%B8%A4%E4%B8%AA%E9%98%9F%E5%88%97%E5%AE%9E%E7%8E%B0%E6%A0%88.py" target="_blank" rel="external">用两个队列实现栈</a></li>
</ul>
<h2 id="旋转数组的最小数字"><a href="#旋转数组的最小数字" class="headerlink" title="旋转数组的最小数字"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/8_%E6%97%8B%E8%BD%AC%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%B0%8F%E6%95%B0%E5%AD%97.py" target="_blank" rel="external">旋转数组的最小数字</a></h2><h3 id="题目描述-5"><a href="#题目描述-5" class="headerlink" title="题目描述"></a>题目描述</h3><p>把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转</p>
<p>输入一个递增排序的数组的一个旋转,输出旋转数组的最小元素</p>
<p>例如数组 [3, 4, 5, 1, 2] 为 [1, 2, 3, 4, 5] 的一个旋转,该数组的最小值为 1</p>
<h3 id="最优题解-5"><a href="#最优题解-5" class="headerlink" title="最优题解"></a>最优题解</h3><p>部分排序,则可用二分查找,注意在等于的时候让 high - 1,顺序查找,退化成 O(n)</p>
<p>(注意到 [1, 0, 1, 1, 1] 和 [1, 1, 1, 0, 1] 的区别,此时无法判断最小值在哪边,故只能用顺序查找):</p>
<ul>
<li><p>array[mid] > array[high]</p>
<p>出现这种情况的 array 类似 [3, 4, 5, 6, 0, 1, 2]</p>
<p>此时最小数字一定在 mid 的右边</p>
<p>low = mid + 1</p>
</li>
<li><p>array[mid] == array[high]</p>
<p>出现这种情况的 array 类似 [1, 0, 1, 1, 1] 或者[1, 1, 1, 0, 1]</p>
<p>此时最小数字不好判断在 mid 左边还是右边,这时只好一个一个试</p>
<p>high = high - 1</p>
</li>
<li><p>array[mid] < array[high]</p>
<p>出现这种情况的 array 类似[2, 2, 3, 4, 5, 6, 6]</p>
<p>此时最小数字一定就是 array[mid] 或者在 mid 的左边,因为右边必然都是递增的</p>
<p>若 mid = 0 ,说明 low 与 high 之间未发生旋转,最小数字就是 array[mid]</p>
<p>或者 array[mid - 1] > array[mid],则最小数字也是 array[mid]</p>
<p>否则 mid 不为零且 array[mid - 1] <= array[mid],最小数字在 mid 的左边,high = mid - 1</p>
</li>
</ul>
<h3 id="代码-5"><a href="#代码-5" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def minNumber(self, rotateArray):
if not rotateArray: return 0
l, r = 0, len(rotateArray) - 1
while l <= r:
m = l + ((r - l) >> 1)
if rotateArray[m] > rotateArray[-1]:
l = m + 1
elif rotateArray[m] < rotateArray[-1]:
if m == 0 or rotateArray[m - 1] > rotateArray[m]:
return rotateArray[m]
else:
r = m - 1
else:
r -= 1
</code></pre>
<h2 id="斐波那契数列"><a href="#斐波那契数列" class="headerlink" title="斐波那契数列"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/9_1_%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97.py" target="_blank" rel="external">斐波那契数列</a></h2><h3 id="题目描述-6"><a href="#题目描述-6" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个整数 n,输出斐波那契数列的第 n 项 f(n)(n 从 0 开始,f(0) = 0,f(1 )= 1)</p>
<h3 id="最优题解-6"><a href="#最优题解-6" class="headerlink" title="最优题解"></a>最优题解</h3><p>a, b = b, a + b</p>
<h3 id="代码-6"><a href="#代码-6" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def Fibonacci(self, n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
</code></pre>
<h3 id="举一反三-2"><a href="#举一反三-2" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/9_2_%E9%9D%92%E8%9B%99%E8%B7%B3%E5%8F%B0%E9%98%B6.py" target="_blank" rel="external">青蛙跳台阶</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/9_3_%E5%8F%98%E6%80%81%E9%9D%92%E8%9B%99%E8%B7%B3%E5%8F%B0%E9%98%B6.py" target="_blank" rel="external">变态青蛙跳台阶</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/9_4_%E7%9F%A9%E5%BD%A2%E8%A6%86%E7%9B%96.py" target="_blank" rel="external">矩形覆盖</a></li>
</ul>
<h2 id="二进制中1的个数"><a href="#二进制中1的个数" class="headerlink" title="二进制中1的个数"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/10_1_%E4%BA%8C%E8%BF%9B%E5%88%B6%E4%B8%AD1%E7%9A%84%E4%B8%AA%E6%95%B0.py" target="_blank" rel="external">二进制中1的个数</a></h2><h3 id="题目描述-7"><a href="#题目描述-7" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个整数,输出该数二进制表示中 1 的个数。其中负数用补码表示。</p>
<h3 id="最优题解-7"><a href="#最优题解-7" class="headerlink" title="最优题解"></a>最优题解</h3><p>利用一个位运算技巧:一个整数减 1 后总是把它二进制表示的最右边的 1 变为 0</p>
<p>这里有两种情况:最右边的 1 在最末位和不在最末位</p>
<p>但无论怎样,减一后的数与原数相与就一定可以把最右的 1 变为 0</p>
<p>有一点要注意:由于 c / c++ / java 中 int 位数限定 32 位</p>
<p>所以 n 最后一定被全部变为 0,循环退出</p>
<p>但 python 就有区别了,python 的整数位数不止 32 位</p>
<p>所以在负数情况下 1 的位数会多出很多,所以应先 &0xffffffff,保留后面 32 位,前面全部变成 0</p>
<h3 id="代码-7"><a href="#代码-7" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def NumberOf1(self, n):
n &= 0xffffffff
count = 0
while n:
n &= n - 1
count += 1
return count
</code></pre>
<h3 id="举一反三-3"><a href="#举一反三-3" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/10_2_2%E7%9A%84%E6%95%B4%E6%95%B0%E6%AC%A1%E6%96%B9.py" target="_blank" rel="external">2的整数次方</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/10_3_%E6%B1%89%E6%98%8E%E8%B7%9D%E7%A6%BB.py" target="_blank" rel="external">汉明距离</a></li>
</ul>
<h2 id="数值的整数次方"><a href="#数值的整数次方" class="headerlink" title="数值的整数次方"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/11_%E6%95%B0%E5%80%BC%E7%9A%84%E6%95%B4%E6%95%B0%E6%AC%A1%E6%96%B9.py" target="_blank" rel="external">数值的整数次方</a></h2><h3 id="题目描述-8"><a href="#题目描述-8" class="headerlink" title="题目描述"></a>题目描述</h3><p>给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent。求 base 的 exponent 次方</p>
<h3 id="最优题解-8"><a href="#最优题解-8" class="headerlink" title="最优题解"></a>最优题解</h3><p>注意点:</p>
<ol>
<li><p>base = 0 且 exponent < 0 时发生除零错误;</p>
</li>
<li><p>exponent < 0 时要作倒数;</p>
</li>
<li><p>0 ^ 0 = 1</p>
</li>
<li><p>判断 base 是否等于 0 时不能直接 == ,因为 base 为浮点数,有误差</p>
<p>如果两个小数的差的绝对值很小,比如小于 0.0000001,就可以认为它们相等;</p>
</li>
<li><p>乘方可以考虑用快速幂(递归实现)</p>
</li>
</ol>
<h3 id="代码-8"><a href="#代码-8" class="headerlink" title="代码"></a>代码</h3><pre><code>class Solution:
def Power(self, base, exponent):
def is_equal(num1, num2):
return abs(num1 - num2) < 0.0000001
def PowerWithUnsignedExponent(base, exponent):
if exponent == 0:
return 1
if exponent == 1:
return base
result = PowerWithUnsignedExponent(base, exponent >> 1) # 除2用位运算
result *= result
if exponent & 1 == 1: # 判奇偶模2用位运算
result *= base
return result
if is_equal(base, 0.0) and exponent < 0:
return
result = PowerWithUnsignedExponent(base, abs(exponent))
if exponent < 0:
return 1.0 / result
return result
</code></pre><h2 id="打印1到最大的n位数"><a href="#打印1到最大的n位数" class="headerlink" title="打印1到最大的n位数"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/12_%E6%89%93%E5%8D%B01%E5%88%B0%E6%9C%80%E5%A4%A7%E7%9A%84n%E4%BD%8D%E6%95%B0.py" target="_blank" rel="external">打印1到最大的n位数</a></h2><h3 id="题目描述-9"><a href="#题目描述-9" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入数字 n, 按顺序打印从 1 最大的 n 位十进制数,比如输入 3, 则打印出 1、2、3、到最大的 3 位数即 999</p>
<p>(这里不打印,而是返回一个 list)</p>
<h3 id="最优题解-9"><a href="#最优题解-9" class="headerlink" title="最优题解"></a>最优题解</h3><p>由于没有位数限制,所以要考虑大数问题,用字符串或数组表示大数</p>
<p>需要注意的问题是字符串或者数组的最高位对于数字上的最低位</p>
<p>在字符串表达的数字上模拟加法,然后将字符串表达的数值打印出来</p>
<h3 id="代码-9"><a href="#代码-9" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def Print1ToMaxOfNDigits(self, n):
def RmStartZero(number): # 去掉前面多余的0
if int(''.join(number)) == 0: # 全0直接返回
return number
num = number[:] # 拷贝,因为pop操作会修改number的长度
while not int(num[0]):
num.pop(0)
return num
def Increment(number):
isOverflow = False
nTakeOver = 0
nLength = len(number)
for i in range(nLength - 1, -1, -1):
nSum = int(number[i]) + nTakeOver
if i == nLength - 1:
nSum += 1
if nSum >= 10:
if i == 0:
isOverflow = True
else:
nSum -= 10
nTakeOver = 1
number[i] = str(nSum)
else:
number[i] = str(nSum)
break
return isOverflow
if n <= 0: return
res = []
number = ['0'] * n
while not Increment(number):
res.append(''.join(RmStartZero(number)))
return res
</code></pre>
<h2 id="在O-1-时间删除链表节点"><a href="#在O-1-时间删除链表节点" class="headerlink" title="在O(1)时间删除链表节点"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/13_%E5%9C%A8O%281%29%E6%97%B6%E9%97%B4%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E8%8A%82%E7%82%B9.py" target="_blank" rel="external">在O(1)时间删除链表节点</a></h2><h3 id="题目描述-10"><a href="#题目描述-10" class="headerlink" title="题目描述"></a>题目描述</h3><p>给定单向链表的头指针和一个结点指针,定义一个函数在 O(1) 时间删除该结点</p>
<h3 id="最优题解-10"><a href="#最优题解-10" class="headerlink" title="最优题解"></a>最优题解</h3><p>不必顺序查找到结点 i 的前一个节点再删除,这样是 O(n)</p>
<p>要删除结点 i,可以先把 i 的下一个结点 j 的内容复制到 i,然后把 i 的指针指向 j 的下一个结点</p>
<p>最后再删除结点 j,其效果刚好是把结点 i 给删除了</p>
<p>即:当我们想删除一个结点时,并不一定要删除这个结点本身</p>
<p>可以先把下一个结点的内容复制出来覆盖被删除结点的内容,然后把下一个结点删除</p>
<p>考虑两种特殊情况:</p>
<pre><code> 1. 如果要删除的是尾结点,它没有下一个结点,此时只能从头开始顺序遍历得到该节点的前序结点,并完成删除
2. 如果链表中只有一个结点,即要删除的节点是头结点(它连前序结点都没有),需要单独处理(删除后设为 NULL)
</code></pre><h3 id="代码-10"><a href="#代码-10" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class ListNode:
def __init__(self, x=None):
self.val = x
self.next = None
def delete(self):
self.val = None
self.next = None
class Solution:
def DeleteNode(self, ListHead, ToBeDeleted):
# 返回删除后链表的头结点
if not ListHead or not ToBeDeleted: return # 头结点和要删除结点都为空返回None
if ToBeDeleted.next is not None: # 要删除的结点不是尾结点
Next = ToBeDeleted.next
ToBeDeleted.val = Next.val
ToBeDeleted.next = Next.next
Next.delete()
elif ListHead == ToBeDeleted: # 要删除的结点是头结点
ListHead.delete()
else: # 要删除的结点是尾结点
Node = ListHead
while Node.next is not ToBeDeleted:
Node = Node.next
Node.next = None
ToBeDeleted.delete()
return ListHead
</code></pre>
<h2 id="使奇数位于偶数前面"><a href="#使奇数位于偶数前面" class="headerlink" title="使奇数位于偶数前面"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/14_%E4%BD%BF%E5%A5%87%E6%95%B0%E4%BD%8D%E4%BA%8E%E5%81%B6%E6%95%B0%E5%89%8D%E9%9D%A2.py" target="_blank" rel="external">使奇数位于偶数前面</a></h2><h3 id="题目描述-11"><a href="#题目描述-11" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个整数数组,实现一个函数来调整该数组中数字的顺序</p>
<p>使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分</p>
<p>并保证奇数和奇数,偶数和偶数之间的相对位置不变。</p>
<h3 id="最优题解-11"><a href="#最优题解-11" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间(O(n)),空间(O(n))</p>
<p>首先统计奇数的个数 i,然后新建一个等长数组,遍历原数组</p>
<p>设置两个指针, 奇数从 0 开始复制,偶数从 i 开始(奇数末尾)</p>
<p>稳定排序,时间复杂度降下来了,但空间复杂度提高了</p>
<h3 id="代码-11"><a href="#代码-11" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def reOrderArray(self, array):
odd = 0 # 奇数开始位置
even = sum(i & 1 for i in array) # 统计奇数个数,为偶数开始位置
res = [0] * len(array)
for i in array:
if i & 1:
res[odd] = i
odd += 1
else:
res[even] = i
even += 1
return res
</code></pre>
<h2 id="链表倒数第-k-个结点"><a href="#链表倒数第-k-个结点" class="headerlink" title="链表倒数第 k 个结点"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/15_1_%E9%93%BE%E8%A1%A8%E5%80%92%E6%95%B0%E7%AC%ACk%E4%B8%AA%E7%BB%93%E7%82%B9.py" target="_blank" rel="external">链表倒数第 k 个结点</a></h2><h3 id="题目描述-12"><a href="#题目描述-12" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个单向链表,输出该链表中倒数第 k 个结点。</p>
<h3 id="最优题解-12"><a href="#最优题解-12" class="headerlink" title="最优题解"></a>最优题解</h3><p>双指针问题</p>
<p>定义两个指针,第一个先从头开始走 k 步,第二个保持不动;从第 k 步开始,第二个指针也开始从头遍历</p>
<p>由于两个指针的距离保持在 k,所以当第一个指针遍历完整个链表时,第二个指针正好来到倒数第 k 个结点</p>
<h3 id="代码-12"><a href="#代码-12" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def FindKthToTail(self, head, k):
if not head: return
l = r = head
while k and r:
r = r.next
k -= 1
if k != 0 and r is None: return # k 大于链表长度
while r:
l, r = l.next, r.next
return l
</code></pre>
<h3 id="举一反三-4"><a href="#举一反三-4" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/15_2_%E9%93%BE%E8%A1%A8%E4%B8%AD%E9%97%B4%E7%BB%93%E7%82%B9.py" target="_blank" rel="external">链表中间结点</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/15_3_%E5%88%A4%E6%96%AD%E9%93%BE%E8%A1%A8%E6%98%AF%E5%90%A6%E5%90%AB%E7%8E%AF.py" target="_blank" rel="external">判断链表是否含环</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/15_4_%E5%90%AB%E7%8E%AF%E9%93%BE%E8%A1%A8%E7%9A%84%E5%85%A5%E5%8F%A3%E7%82%B9.py" target="_blank" rel="external">含环链表的入口点</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/15_5_%E5%90%AB%E7%8E%AF%E9%93%BE%E8%A1%A8%E7%9A%84%E9%95%BF%E5%BA%A6%E5%92%8C%E7%8E%AF%E7%9A%84%E9%95%BF%E5%BA%A6.py" target="_blank" rel="external">含环链表的长度和环的长度</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/15_6_%E6%97%A0%E7%8E%AF%E5%8D%95%E5%90%91%E9%93%BE%E8%A1%A8%E7%9A%84%E7%9B%B8%E4%BA%A4.py" target="_blank" rel="external">无环单向链表的相交</a></li>
</ul>
<h2 id="反转链表"><a href="#反转链表" class="headerlink" title="反转链表"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/16_%E5%8F%8D%E8%BD%AC%E9%93%BE%E8%A1%A8.py" target="_blank" rel="external">反转链表</a></h2><h3 id="题目描述-13"><a href="#题目描述-13" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个链表的头结点,反转该链表并输出反转后链表的头结点</p>
<h3 id="最优题解-13"><a href="#最优题解-13" class="headerlink" title="最优题解"></a>最优题解</h3><p>定义三个指针,分别指向当前遍历到的结点、它的前一个结点以及后一个结点</p>
<p>指向后一个结点的指针是为了防止链表断裂,因为需要把当前结点的下一个指针指向前一个结点</p>
<h3 id="代码-13"><a href="#代码-13" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
# 返回ListNode
def ReverseList(self, pHead):
pre, cur = None, pHead
while cur:
cur.next, cur, pre = pre, cur.next, cur
# 上面的一行相当于下面四行
# next = cur.next # 先保存下一个结点防止断裂
# cur.next = pre
# pre = cur
# cur = next
return pre
</code></pre>
<h2 id="合并两个排序的链表"><a href="#合并两个排序的链表" class="headerlink" title="合并两个排序的链表"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/17_%E5%90%88%E5%B9%B6%E4%B8%A4%E4%B8%AA%E6%8E%92%E5%BA%8F%E7%9A%84%E9%93%BE%E8%A1%A8.py" target="_blank" rel="external">合并两个排序的链表</a></h2><h3 id="题目描述-14"><a href="#题目描述-14" class="headerlink" title="题目描述"></a>题目描述</h3><p>合并两个单调递增的链表,使得合并后的链表仍然单调递增</p>
<h3 id="最优题解-14"><a href="#最优题解-14" class="headerlink" title="最优题解"></a>最优题解</h3><p>使用一个尾指针,每次比较把较小的结点连接到尾结点后面,记得最后将剩余的链表链接到尾结点后面</p>
<h3 id="代码-14"><a href="#代码-14" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
# 返回合并后的链表头结点
def Merge(self, pHead1, pHead2):
dummy = tail = ListNode(0)
while pHead1 and pHead2:
if pHead1.val <= pHead2.val:
tail.next = pHead1
pHead1 = pHead1.next
else:
tail.next = pHead2
pHead2 = pHead2.next
tail = tail.next
tail.next = pHead1 or pHead2 # 记得将剩余的链表链接到尾结点后面
return dummy.next # 伪结点的下一个结点才是真正的头结点
</code></pre>
<h2 id="树的子结构"><a href="#树的子结构" class="headerlink" title="树的子结构"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/18_%E6%A0%91%E7%9A%84%E5%AD%90%E7%BB%93%E6%9E%84.py" target="_blank" rel="external">树的子结构</a></h2><h3 id="题目描述-15"><a href="#题目描述-15" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入两棵二叉树 A,B,判断 B 是不是 A 的子结构(空树不是任意一个树的子结构)</p>
<h3 id="最优题解-15"><a href="#最优题解-15" class="headerlink" title="最优题解"></a>最优题解</h3><p>先递归遍历(先序遍历)树A,找到相同的根结点子树</p>
<p>再用递归分别判断该子树的左右子树是否与 B 一样,递归结束的条件是来到 B 的叶结点</p>
<h3 id="代码-15"><a href="#代码-15" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# pRoot1是A的根节点,pRoot2是B的根节点
def HasSubtree(self, pRoot1, pRoot2):
def check(root1, root2):
# 用于递归判断树的每个节点是否相同
# 需要注意的地方是: 前两个if语句不可以颠倒顺序
# 如果颠倒顺序, 会先判断root1是否为None,
# 其实这个时候root2的结点已经遍历完并确定相等了,但是返回了False
if root2 is None: return True
if root1 is None: return False
if root1.val != root2.val: return False
return check(root1.left, root2.left) and check(root1.right, root2.right)
if not pRoot1 or not pRoot2: return False
if check(pRoot1, pRoot2): return True
return self.HasSubtree(pRoot1.left, pRoot2) or self.HasSubtree(pRoot1.right, pRoot2)
</code></pre>
<h2 id="二叉树的镜像"><a href="#二叉树的镜像" class="headerlink" title="二叉树的镜像"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/19_%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%95%9C%E5%83%8F.py" target="_blank" rel="external">二叉树的镜像</a></h2><h3 id="题目描述-16"><a href="#题目描述-16" class="headerlink" title="题目描述"></a>题目描述</h3><p>将给定的二叉树变换为原二叉树的镜像。</p>
<h3 id="最优题解-16"><a href="#最优题解-16" class="headerlink" title="最优题解"></a>最优题解</h3><p>递归实现,前序遍历二叉树的每个结点</p>
<p>如果遍历到的结点有子结点,就交换它的两个子结点</p>
<p>当交换完所有的非叶子结点的左右子结点之后,就得到了树的镜像</p>
<h3 id="代码-16"><a href="#代码-16" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回镜像树的根节点
def Mirror(self, root):
if root is None: return
root.left, root.right = root.right, root.left
self.Mirror(root.left)
self.Mirror(root.right)
return root
</code></pre>
<h2 id="顺时针打印矩阵"><a href="#顺时针打印矩阵" class="headerlink" title="顺时针打印矩阵"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/20_%E9%A1%BA%E6%97%B6%E9%92%88%E6%89%93%E5%8D%B0%E7%9F%A9%E9%98%B5.py" target="_blank" rel="external">顺时针打印矩阵</a></h2><h3 id="题目描述-17"><a href="#题目描述-17" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字</p>
<p>例如,如果输入如下矩阵:</p>
<p>[[ 1, 2, 3, 4],<br> [ 5, 6, 7, 8],<br> [ 9, 10, 11, 12],<br> [13, 14, 15, 16]]</p>
<p>则依次打印出数字 1, 2, 3, 4, 8, 12, 16, 15, 14, 13, 9, 5, 6, 7, 11, 10.</p>
<h3 id="最优题解-17"><a href="#最优题解-17" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间 O(n * m), 空间 O(1)</p>
<p>一个圈其实只要左上角(startR, startC)和右下角(endR, endC)确定了整个圈也就确定了,</p>
<p>因此外层循环控制左上角和右下角的变化,内层循环根据这两个坐标分情况绕圈打印就可以了</p>
<p>外层循环如何控制两个坐标?只需每次让 (startR++, startC++)和 (endR–, endC–)</p>
<p>什么时候退出?当右下角来到左上角的左上方时退出,换句话讲就是当</p>
<p>startR <= endR and startC <= endC 时我们才可以进入内层循环打印圈</p>
<p>那内层循环如何分情况打印圈?情况就三种:</p>
<ol>
<li>当 startR == endR 时,说明只剩一行</li>
<li>当 startC == endC 时,说明只剩一列</li>
<li>否则为一般情况,绕圈打印即可</li>
</ol>
<h3 id="代码-17"><a href="#代码-17" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
# matrix类型为二维列表,需要返回列表
def PrintMatrix(self, matrix):
def printEdge(startR, startC, endR, endC):
if startR == endR: # 只剩一行
for col in range(startC, endC + 1):
res.append(matrix[startR][col])
elif startC == endC: # 只剩一列
for row in range(startR, endR + 1):
res.append(matrix[row][startC])
else: # 一般情况
curR, curC = startR, startC
while curC != endC: # 从左到右
res.append(matrix[curR][curC])
curC += 1
while curR != endR: # 从上到下
res.append(matrix[curR][curC])
curR += 1
while curC != startC: # 从右到左
res.append(matrix[curR][curC])
curC -= 1
while curR != startR: # 从下到上
res.append(matrix[curR][curC])
curR -= 1
if not matrix: return
startR, startC, endR, endC = 0, 0, len(matrix) - 1, len(matrix[0]) - 1
res = []
while startR <= endR and startC <= endC:
printEdge(startR, startC, endR, endC)
startR += 1
startC += 1
endR -= 1
endC -= 1
return res
</code></pre>
<h3 id="举一反三-5"><a href="#举一反三-5" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/20_2_%E9%A1%BA%E6%97%B6%E9%92%88%E5%A1%AB%E5%85%85%E7%9F%A9%E9%98%B5.py" target="_blank" rel="external">顺时针填充矩阵</a></li>
</ul>
<h2 id="包含min函数的栈"><a href="#包含min函数的栈" class="headerlink" title="包含min函数的栈"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/21_%E5%8C%85%E5%90%ABmin%E5%87%BD%E6%95%B0%E7%9A%84%E6%A0%88.py" target="_blank" rel="external">包含min函数的栈</a></h2><h3 id="题目描述-18"><a href="#题目描述-18" class="headerlink" title="题目描述"></a>题目描述</h3><p>定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的 min 函数。<br>在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)</p>
<h3 id="最优题解-18"><a href="#最优题解-18" class="headerlink" title="最优题解"></a>最优题解</h3><p>设置一个辅助栈,每次入栈时把最小元素</p>
<p>(之前的最小值(辅助栈栈顶)和新压入栈的元素两者的较小值)保存在辅助栈中</p>
<p>出栈时辅助栈一起出栈,这样就可以保证辅助栈的栈顶是最小值</p>
<h3 id="代码-18"><a href="#代码-18" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def __init__(self):
self.stack = []
self.minstack = []
def push(self, node):
self.stack.append(node)
if not self.minstack or node <= self.min():
self.minstack.append(node)
else:
self.minstack.append(self.min())
def pop(self):
if not self.stack: return
self.minstack.pop()
return self.stack.pop()
def top(self):
return self.stack[-1] if self.stack else None
def min(self):
return self.minstack[-1] if self.minstack else None
</code></pre>
<h2 id="栈的压入弹出序列"><a href="#栈的压入弹出序列" class="headerlink" title="栈的压入弹出序列"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/22_%E6%A0%88%E7%9A%84%E5%8E%8B%E5%85%A5%E5%BC%B9%E5%87%BA%E5%BA%8F%E5%88%97.py" target="_blank" rel="external">栈的压入弹出序列</a></h2><h3 id="题目描述-19"><a href="#题目描述-19" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序</p>
<p>假设压入栈的所有数字均不相等</p>
<p>例如序列 1, 2, 3, 4, 5 是某栈的压入顺序,序列 4, 5, 3, 2, 1 是该压栈序列对应的一个弹出序列</p>
<p>但 4, 3, 5, 1, 2 就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)</p>
<h3 id="最优题解-19"><a href="#最优题解-19" class="headerlink" title="最优题解"></a>最优题解</h3><p>设置一个辅助栈,用来装压栈序列,不断压入压栈序列的数,每压入一次</p>
<p>就看一下当前栈顶元素是否为当前弹出数,是的话弹出并遍历下一个弹出数</p>
<p>继续检查,直到辅助栈空或者当前栈顶元素不为弹出序列第一个</p>
<p>就继续压入压栈序列的数,直到压完所有的数,最终检查辅助栈是否为空,空则说明该序列为弹出序列</p>
<p>即:如果下一个弹出的数字刚好是栈顶数字,那么直接弹出。如果下一个弹出的数字不在栈顶</p>
<p>就把压栈序列中还没入栈的数字压入辅助栈,直到把下一个需要弹出的数字压入栈顶为止</p>
<p>如果所有的数字都压入栈了仍然没有找到下一个弹出的数字,那么该序列就不可能是一个弹出序列</p>
<h3 id="代码-19"><a href="#代码-19" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def IsPopOrder(self, pushV, popV):
if not pushV or not popV or len(pushV) != len(popV):
return False
stack, i = [], 0
for num in pushV:
stack.append(num)
while stack and stack[-1] == popV[i]:
i += 1
stack.pop()
return stack == []
</code></pre>
<h2 id="从上往下打印二叉树"><a href="#从上往下打印二叉树" class="headerlink" title="从上往下打印二叉树"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/23_%E4%BB%8E%E4%B8%8A%E5%BE%80%E4%B8%8B%E6%89%93%E5%8D%B0%E4%BA%8C%E5%8F%89%E6%A0%91.py" target="_blank" rel="external">从上往下打印二叉树</a></h2><h3 id="题目描述-20"><a href="#题目描述-20" class="headerlink" title="题目描述"></a>题目描述</h3><p>从上往下打印出二叉树的每个节点,同层节点从左至右打印。</p>
<h3 id="最优题解-20"><a href="#最优题解-20" class="headerlink" title="最优题解"></a>最优题解</h3><p>相当于宽度优先搜索(BFS),用队列来实现</p>
<p>每次从头部取出一个结点时,如果该结点有子结点</p>
<p>就把该结点的子结点从左到右依次放入队列末尾</p>
<p>重复前面的步骤,直到队列为空</p>
<h3 id="代码-20"><a href="#代码-20" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回从上到下每个节点值列表,例:[1,2,3]
def PrintFromTopToBottom(self, root):
if root is None: return []
res, level = [], [root]
while level:
next_level = []
for node in level:
res.append(node.val)
if node.left:
next_level.append(node.left)
if node.right:
next_level.append(node.right)
level = next_level
return res
</code></pre>
<h2 id="二叉搜索树的后序遍历序列"><a href="#二叉搜索树的后序遍历序列" class="headerlink" title="二叉搜索树的后序遍历序列"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/24_2_%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E5%90%8E%E5%BA%8F%E9%81%8D%E5%8E%86%E5%BA%8F%E5%88%97.py" target="_blank" rel="external">二叉搜索树的后序遍历序列</a></h2><h3 id="题目描述-21"><a href="#题目描述-21" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果</p>
<p>假设输入的数组的任意两个数字都互不相同。</p>
<h3 id="最优题解-21"><a href="#最优题解-21" class="headerlink" title="最优题解"></a>最优题解</h3><p>二叉搜索树对于每一个非叶子节点, 均有结点左子节点 < 当前节点 < 结点右子结点</p>
<p>后序序列的最后一个值为二叉搜索树根节点的值,前面含有左子树和右子树结点</p>
<p>根据二叉搜索树的特性,根节点前面的序列分为两个区域,左边为左子树区</p>
<p>值都比根节点小,右边为右子树区,值都比根节点大</p>
<p>不满足该特性的序列就不是某二叉搜索树的后序遍历的结果</p>
<p>注意左子树区域和右子树区域分别又是一棵二叉搜索树,因此递归检查这两个区域</p>
<h3 id="代码-21"><a href="#代码-21" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def VerifySquenceOfBST(self, sequence):
def verify(seq):
if not seq or len(seq) == 1: return True
for i in range(len(seq)):
if seq[i] > seq[-1]: break
# 如果是前序遍历就必须加上 i+=1
# 这里不用的原因在于seq[-1]是哨兵
# i一定会在小于区的右边界的右边元素停下
for j in range(i, len(seq) - 1):
if seq[j] < seq[-1]: return False
return verify(seq[:i]) and verify(seq[i:-1])
if not sequence: return False
return verify(sequence)
</code></pre>
<h3 id="举一反三-6"><a href="#举一反三-6" class="headerlink" title="举一反三"></a>举一反三</h3><p><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/24_1_%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E5%89%8D%E5%BA%8F%E9%81%8D%E5%8E%86%E5%BA%8F%E5%88%97.py" target="_blank" rel="external">二叉搜索树的前序遍历序列</a></p>
<h2 id="二叉树中和为某一值的路径"><a href="#二叉树中和为某一值的路径" class="headerlink" title="二叉树中和为某一值的路径"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/25_%E4%BA%8C%E5%8F%89%E6%A0%91%E4%B8%AD%E5%92%8C%E4%B8%BA%E6%9F%90%E4%B8%80%E5%80%BC%E7%9A%84%E8%B7%AF%E5%BE%84.py" target="_blank" rel="external">二叉树中和为某一值的路径</a></h2><h3 id="题目描述-22"><a href="#题目描述-22" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一颗二叉树和一个整数,输出二叉树中结点值的和为输入整数的所有路径</p>
<p>路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径</p>
<h3 id="最优题解-22"><a href="#最优题解-22" class="headerlink" title="最优题解"></a>最优题解</h3><p>前序遍历,每来到一个结点就检查当前和是否为期望和,所以需要把当前和作为参数传递。</p>
<h3 id="代码-22"><a href="#代码-22" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
# 返回二维列表,内部每个列表表示找到的路径
def FindPath(self, root, expectNumber):
def find(root, cursum):
if not root: return
path.append(root.val)
cursum += root.val
if not root.left and not root.right and cursum == expectNumber:
res.append(path[:])
find(root.left, cursum)
find(root.right, cursum)
path.pop() # 结点返回时记得把当前结点从路径中删除
res, path = [], []
find(root, 0)
return res
</code></pre>
<h2 id="复杂链表的复制"><a href="#复杂链表的复制" class="headerlink" title="复杂链表的复制"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/26_%E5%A4%8D%E6%9D%82%E9%93%BE%E8%A1%A8%E7%9A%84%E5%A4%8D%E5%88%B6.py" target="_blank" rel="external">复杂链表的复制</a></h2><h3 id="题目描述-23"><a href="#题目描述-23" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个复杂链表(每个节点中有节点值,以及两个指针</p>
<p>next指向下一个节点,random指向任意一个节点)</p>
<p>返回结果为复制后复杂链表的head</p>
<h3 id="最优题解-23"><a href="#最优题解-23" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间复杂度 O(n),空间复杂度 O(1)</p>
<p>第一步,复制原链表的结点N并创建新结点N’,再把N’链接到N的后面</p>
<p>第二步,设置每个N’的random。如果原链表上的结点N的random指向S,则它对应的复制结点N’的random指向S的下一个结点S’</p>
<p>第三步,把这个长链表拆分成两个链表,奇数位置上的结点组成原链表,偶数位置上的结点组成复制链表</p>
<h3 id="代码-23"><a href="#代码-23" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class RandomListNode:
def __init__(self, x):
self.label = x
self.next = None
self.random = None
class Solution:
def Clone(self, pHead):
def clone(head):
while head:
cloneNode = RandomListNode(head.label)
cloneNode.next = head.next
head.next = cloneNode
head = cloneNode.next
def connect(head):
clone = head.next
while head:
clone.random = head.random.next if head.random else None
head = clone.next
clone = head.next if head else None
def split(head):
clone = cloneHead = head.next
while head:
head.next = clone.next
head = head.next
clone.next = head.next if head else None
clone = clone.next
return cloneHead
if pHead is None: return
clone(pHead)
connect(pHead)
return split(pHead)
</code></pre>
<h2 id="二叉搜索树与双向链表"><a href="#二叉搜索树与双向链表" class="headerlink" title="二叉搜索树与双向链表"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/27_%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E4%B8%8E%E5%8F%8C%E5%90%91%E9%93%BE%E8%A1%A8.py" target="_blank" rel="external">二叉搜索树与双向链表</a></h2><h3 id="题目描述-24"><a href="#题目描述-24" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表</p>
<p>要求不能创建任何新的结点,只能调整树中结点指针的指向</p>
<h3 id="最优题解-24"><a href="#最优题解-24" class="headerlink" title="最优题解"></a>最优题解</h3><p><img src="https://ws1.sinaimg.cn/large/006giLD5ly1fwx65siwcwj30h103bjr8.jpg" alt="转换示意图"></p>
<p>中序遍历,同时设置一个指针指向当前双向链表的最后一个结点</p>
<p>每个被遍历到的结点都与当前双向链表的最后一个结点互相连接</p>
<p>同时更新该指针为当前被遍历到的结点</p>
<h3 id="代码-24"><a href="#代码-24" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def __init__(self):
self.last = None
def Convert(self, pRootOfTree):
def convert(root):
if not root: return
convert(root.left)
self.last.right = root
root.left = self.last
self.last = root
convert(root.right)
if not pRootOfTree: return
dummy = self.last = TreeNode(0)
convert(pRootOfTree)
dummy.right.left = None # 去掉伪结点
return dummy.right # 伪结点的右边是真正的头结点
</code></pre>
<h2 id="字符串的排列"><a href="#字符串的排列" class="headerlink" title="字符串的排列"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/28_1_%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%8E%92%E5%88%97.py" target="_blank" rel="external">字符串的排列</a></h2><h3 id="题目描述-25"><a href="#题目描述-25" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个字符串,按字典序打印出该字符串中字符的所有排列</p>
<p>例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab和 cba</p>
<p>输入描述:</p>
<p>输入一个字符串,长度不超过 9 (可能有字符重复),字符只包括大小写字母</p>
<h3 id="最优题解-25"><a href="#最优题解-25" class="headerlink" title="最优题解"></a>最优题解</h3><p>递归,从头到尾逐个字符抽出来,剩下字符的进行排列</p>
<p>最后再把抽出来的那个字符与排列好的字符串拼在一起返回即可</p>
<p>只剩下一个字符时直接返回该字符</p>
<h3 id="代码-25"><a href="#代码-25" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def Permutation(self, ss):
def permutation(ss):
if len(ss) == 1: return [ss]
res = []
for i in range(len(ss)):
for s in permutation(ss[:i] + ss[i + 1:]):
res.append(ss[i] + s)
return res
if not ss: return []
return sorted(list(set(permutation(ss))))
</code></pre>
<h3 id="举一反三-7"><a href="#举一反三-7" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/28_2_%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E7%BB%84%E5%90%88.py" target="_blank" rel="external">字符串的组合</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/28_3_%E5%85%AB%E7%9A%87%E5%90%8E%E9%97%AE%E9%A2%98.py" target="_blank" rel="external">八皇后问题</a></li>
</ul>
<h2 id="数组中出现次数超过一半的数字"><a href="#数组中出现次数超过一半的数字" class="headerlink" title="数组中出现次数超过一半的数字"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/29_%E6%95%B0%E7%BB%84%E4%B8%AD%E5%87%BA%E7%8E%B0%E6%AC%A1%E6%95%B0%E8%B6%85%E8%BF%87%E4%B8%80%E5%8D%8A%E7%9A%84%E6%95%B0%E5%AD%97.py" target="_blank" rel="external">数组中出现次数超过一半的数字</a></h2><h3 id="题目描述-26"><a href="#题目描述-26" class="headerlink" title="题目描述"></a>题目描述</h3><p>数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字</p>
<p>例如输入一个长度为 9 的数组 [1, 2, 3, 2, 2, 2, 5, 4, 2]</p>
<p>由于数字 2 在数组中出现了 5 次,超过数组长度的一半,因此输出 2。如果不存在则输出 0</p>
<h3 id="最优题解-26"><a href="#最优题解-26" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间 O(n),空间 O(1)</p>
<p>采用阵地攻守的思想</p>
<p>先让第一个数作为守阵地的士兵,HP = 1</p>
<p>遇到相同元素,相当于支援兵,补血,HP + 1</p>
<p>遇到不相同元素,相当于敌人,掉血,HP - 1</p>
<p>当 HP 削减为 0 时,以下一个数作为守阵地的士兵</p>
<p>继续下去,到最后还留在阵地上的士兵,有可能是最强士兵(士兵个数超过一半)</p>
<p>为防止该士兵坐收渔翁之利,需再加一次循环,检查该士兵个数是否真的超过一半</p>
<h3 id="代码-26"><a href="#代码-26" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def MoreThanHalfNum(self, numbers):
if not numbers: return 0
master = numbers[0]
hp = 1
for num in numbers[1:]:
if hp == 0:
master = num
hp = 1
elif num == master:
hp += 1
else:
hp -= 1
isHalf = sum(num == master for num in numbers) > len(numbers) >> 1
return master if isHalf else 0
</code></pre>
<h2 id="最小的K个数"><a href="#最小的K个数" class="headerlink" title="最小的K个数"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/30_%E6%9C%80%E5%B0%8F%E7%9A%84K%E4%B8%AA%E6%95%B0.py" target="_blank" rel="external">最小的K个数</a></h2><h3 id="题目描述-27"><a href="#题目描述-27" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入 n 个整数,找出其中最小的 K 个数。例如输入 4, 5, 1, 6, 2, 7, 3, 8 这 8 个数字,则最小的 4 个数字是 1, 2, 3, 4</p>
<h3 id="最优题解-27"><a href="#最优题解-27" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间 O(nlogk),空间 O(1)</p>
<p>基于 Partition 的算法,只有当我们可以修改输入的数组时可用</p>
<p>利用 Partition 找到第 (k - 1) 小的数 (从 0 开始),则数组左边的k个数字就是最小的 k 个数字(这 k 个数字不一定是排序的)</p>
<h3 id="代码-27"><a href="#代码-27" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def GetLeastNumbers(self, tinput, k):
def partition(l, r): # 基于快排的partition
pivot = tinput[l] # 首元素为枢轴标杆
while l < r:
while l < r and tinput[r] >= pivot: r -= 1
tinput[l] = tinput[r]
while l < r and tinput[l] <= pivot: l += 1
tinput[r] = tinput[l]
tinput[l] = pivot
return l
if not tinput or k <= 0 or len(tinput) < k: return []
l, r = 0, len(tinput) - 1
# 以下类似二分, 只不过分界线不再是mid,而是通过partition求得
while l <= r:
idx = partition(l, r)
if idx < k - 1:
l = idx + 1
elif idx > k - 1:
r = idx - 1
else:
return sorted(tinput[:k])
</code></pre>
<h2 id="连续子数组的最大和"><a href="#连续子数组的最大和" class="headerlink" title="连续子数组的最大和"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/31_%E8%BF%9E%E7%BB%AD%E5%AD%90%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%A4%A7%E5%92%8C.py" target="_blank" rel="external">连续子数组的最大和</a></h2><h3 id="题目描述-28"><a href="#题目描述-28" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个整型数组,数组中一个或连续的多个整数组成一个子数组。<br>求所有子数组的和的最大值,要求时间复杂度为 O(n)</p>
<h3 id="最优题解-28"><a href="#最优题解-28" class="headerlink" title="最优题解"></a>最优题解</h3><p>动态规划,设 f(i) 为以第 i 个数结尾的连续子数组的最大和,则状态转移方程为:</p>
<p><code>f(i) = max(f(i-1)+array[i], array[i])</code></p>
<p>最后结果为 max(f(i))</p>
<p>优化:f(i) 只与 f(i - 1) 有关,即只与前一状态有关,所以空间上可以由 O(n) 降为 O(1)</p>
<p>该状态转移方程的意义是:</p>
<p>如果以第 i - 1 个数字结尾的子数组中所有数字的和加上当前第 i 个数字比当前第 i 个数字本身还要小</p>
<p>那么就舍弃前面的累加而直接选择第 i 个数字本身作为最大值</p>
<p>最后求所有以第 i 个数结尾的连续子数组最大和的最大值</p>
<h3 id="代码-28"><a href="#代码-28" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def FindGreatestSumOfSubArray(self, array):
if not array: return
curSum = maxSum = array[0]
for num in array[1:]:
curSum = max(num, curSum + num)
maxSum = max(maxSum, curSum)
return maxSum
</code></pre>
<h2 id="从-1-到-n-中-1-出现的次数"><a href="#从-1-到-n-中-1-出现的次数" class="headerlink" title="从 1 到 n 中 1 出现的次数"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/32_%E4%BB%8E1%E5%88%B0n%E4%B8%AD1%E5%87%BA%E7%8E%B0%E7%9A%84%E6%AC%A1%E6%95%B0.py" target="_blank" rel="external">从 1 到 n 中 1 出现的次数</a></h2><h3 id="题目描述-29"><a href="#题目描述-29" class="headerlink" title="题目描述"></a>题目描述</h3><p>原题:输入一个整数 n,求从 1 到 n 这 n 个整数的十进制表示中 1 出现的次数</p>
<p>扩展:改成 X 出现的次数,X ∈ [1, 9]</p>
<h3 id="最优题解-29"><a href="#最优题解-29" class="headerlink" title="最优题解"></a>最优题解</h3><p>链接:<a href="https://www.nowcoder.net/questionTerminal/bd7f978302044eee894445e244c7eee6" target="_blank" rel="external">https://www.nowcoder.net/questionTerminal/bd7f978302044eee894445e244c7eee6</a></p>
<p>来源:牛客网</p>
<p>参考博文:<a href="http://www.cnblogs.com/nailperry/p/4752987.html" target="_blank" rel="external">http://www.cnblogs.com/nailperry/p/4752987.html</a> ,主要就是从数字出发找规律。</p>
<p>一、1的数目</p>
<p>编程之美上给出的规律:</p>
<ol>
<li><p>如果第 i 位(自右至左,从 1 开始标号)上的数字为 0,则第 i 位可能出现 1 的次数由更高位决定</p>
<p> (若没有高位,视高位为 0),等于更高位数字 * 当前位数的权重 10 ^ (i - 1)</p>
</li>
<li><p>如果第 i 位上的数字为 1,则第 i 位上可能出现 1 的次数不仅受更高位影响,还受低位影响</p>
<p> (若没有低位,视低位为 0),等于更高位数字 * 当前位数的权重 10 ^ (i - 1) + (低位数字 + 1)</p>
</li>
<li><p>如果第 i 位上的数字大于 1,则第 i 位上可能出现 1 的次数仅由更高位决定(若没有高位,视高位为 0)</p>
<p>等于(更高位数字 + 1)* 当前位数的权重10 ^ (i - 1)</p>
</li>
</ol>
<p>二、X的数目</p>
<p>这里的 X ∈ [1, 9],因为 X = 0 不符合下列规律,需要单独计算</p>
<p>首先要知道以下的规律:</p>
<p>从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次</p>
<p>从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次</p>
<p>从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次</p>
<p>依此类推,从 1 至 10 ^ i,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10 ^ (i - 1)次</p>
<p>这个规律很容易验证,这里不再多做说明</p>
<p>接下来以 n = 2593, X = 5 为例来解释如何得到数学公式。从 1 至 2593 中,数字 5 总计出现了 813 次</p>
<p>其中有 259 次出现在个位,260 次出现在十位,294 次出现在百位,0 次出现在千位</p>
<p>现在依次分析这些数据,首先是个位。从 1 至 2590 中,包含了 259 个 10,因此任意的 X 都出现了 259 次</p>
<p>最后剩余的三个数 2591, 2592 和 2593,因为它们最大的个位数字 3 < X,因此不会包含任何 5</p>
<p>(也可以这么看,3 < X,则个位上可能出现的 X 的次数仅由更高位决定,等于更高位数字(259) * 10 ^ (1 - 1) = 259)</p>
<p>然后是十位。从 1 至 2500 中,包含了 25 个 100,因此任意的 X 都出现了 25 * 10 = 250 次</p>
<p>剩下的数字是从 2501 至 2593,它们最大的十位数字 9 > X,因此会包含全部 10 个 5</p>
<p>最后总计 250 + 10 = 260。(也可以这么看,9 > X,则十位上可能出现的 X 的次数仅由更高位决定</p>
<p>等于更高位数字(25 + 1) * 10 ^ (2 - 1) = 260)</p>
<p>接下来是百位。从 1 至 2000 中,包含了 2 个 1000,因此任意的 X 都出现了 2 * 100 = 200 次</p>
<p>剩下的数字是从 2001 至 2593,它们最大的百位数字 5 == X,这时情况就略微复杂</p>
<p>它们的百位肯定是包含 5 的,但不会包含全部 100 个。如果把百位是 5 的数字列出来</p>
<p>是从 2500 至 2593,数字的个数与百位和十位数字相关,是 93 + 1 = 94。最后总计 200 + 94 = 294</p>
<p>(也可以这么看,5 == X,则百位上可能出现X的次数不仅受更高位影响,还受低位影响,</p>
<p>等于更高位数字(2) * 10 ^ (3 - 1) + (93 + 1) = 294)</p>
<p>最后是千位。现在已经没有更高位,因此直接看最大的千位数字 2 < X,所以不会包含任何 5</p>
<p>(也可以这么看,2 < X,则千位上可能出现的 X 的次数仅由更高位决定,等于更高位数字 (0) * 10 ^ (4 - 1) = 0)</p>
<p>到此为止,已经计算出全部数字 5 的出现次数</p>
<p>总结一下以上的算法,可以看到,当计算右数第 i 位包含的 X 的个数时:</p>
<p>取第i位左边(所有高位)的数字,乘以 10 ^ (i - 1) ,得到基础值 a </p>
<p>取第i位数字,计算修正值:</p>
<p>如果小于 X,则结果为 a </p>
<p>如果大于 X,则结果为 a + 10 ^ (i - 1) </p>
<p>如果等于 X,则取第 i 位右边(所有低位)数字,设为 b ,最后结果为 a + b + 1 </p>
<p>相应的代码非常简单,效率也非常高,时间复杂度只有 O( log n) </p>
<h3 id="代码-29"><a href="#代码-29" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def NumberOf1Between1AndN(self, n):
count, weight = 0, 1
while weight <= n:
low = n % weight
high = n // weight
cur = high % 10
base = (high // 10) * weight
if cur < 1:
count += base
elif cur > 1:
count += base + weight
else:
count += base + low + 1
weight *= 10
return count
</code></pre>
<h2 id="把数组排成最小的数"><a href="#把数组排成最小的数" class="headerlink" title="把数组排成最小的数"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/33_%E6%8A%8A%E6%95%B0%E7%BB%84%E6%8E%92%E6%88%90%E6%9C%80%E5%B0%8F%E7%9A%84%E6%95%B0.py" target="_blank" rel="external">把数组排成最小的数</a></h2><h3 id="题目描述-30"><a href="#题目描述-30" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。<br>例如输入数组 [3, 32, 321],则打印出这三个数字能排成的最小数字为 321323。</p>
<h3 id="最优题解-30"><a href="#最优题解-30" class="headerlink" title="最优题解"></a>最优题解</h3><p>最直接的做法就是把数组中所有数字进行全排列,然后把每个排列拼接起来,最后求出拼起来的数字的最小值</p>
<p>易知算法复杂度高达 O(n!),所以不推荐。这里定义一个新的比较规则:</p>
<p>对于两个数字 m, n,可以拼接成 mn 和 nm</p>
<p>若 mn < nm,则定义 m < n;</p>
<p>若 mn > nm,则定义 m > n;</p>
<p>若 mn = nm,则定义 m = n</p>
<p>将数组中的数按照上述比较规则从小到大进行排序,最后将排序的数组进行拼接即为数组所能拼成的最小数</p>
<p>证明见书第一版P179~180</p>
<h3 id="代码-30"><a href="#代码-30" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">from functools import cmp_to_key
class Solution:
def PrintMinNumber(self, numbers):
return ''.join(sorted([str(num) for num in numbers], key = cmp_to_key(lambda x, y: int(x + y) - int(y + x))))
</code></pre>
<h2 id="丑数"><a href="#丑数" class="headerlink" title="丑数"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/34_%E4%B8%91%E6%95%B0.py" target="_blank" rel="external">丑数</a></h2><h3 id="题目描述-31"><a href="#题目描述-31" class="headerlink" title="题目描述"></a>题目描述</h3><p>把只包含质因子 2、3 和 5 的数称作丑数(Ugly Number)</p>
<p>例如 6、8 都是丑数,但 14 不是,因为它包含质因子7</p>
<p>习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。</p>
<h3 id="最优题解-31"><a href="#最优题解-31" class="headerlink" title="最优题解"></a>最优题解</h3><p>创建数组保存已经找到的丑数并排好序,关键在于如何生成下一个丑数</p>
<p>数组中最后一个丑数最大,记为M。设置 index2,标记该位置的数乘以 2 大于 M</p>
<p>同理设置 index3、index5,这样每次只需求<code>min(A[index2] * 2, A[index3] * 3, A[index5] * 5)</code></p>
<p>就可求出下一个丑数,然后更新三个标记</p>
<p>这样关键就在于如何更新这三个标记</p>
<p>仔细推敲可以发现其实只需让那些指向的数乘相应因子等于当前 M 的标记往后移一位即可</p>
<p>因为 <code>M = min(A[index2] * 2, A[index3] * 3, A[index5] * 5)</code>,则至少有个标记是要往后移的</p>
<p>且移一位即可,后面那个数乘以相应的因子一定大于M</p>
<p>那么其他指向的数乘相应因子不等于当前M的标记为什么没有必要移动呢?</p>
<p>还是因为 <code>M = min(A[index2] * 2, A[index3] * 3, A[index5] * 5)</code>, 既然M是其中最小的</p>
<p>那么其他的标记所指向的数乘以相应因子一定就比 M 大了,没有必要更新</p>
<p>这样就可以把三个并列的 while 简化成三个并列的if</p>
<p>这里谈谈为什么要使用这三个 index,且为什么这样做可以保证按顺序产生下一个丑数</p>
<p>按照正常的理解,后面的丑数都是由前面已经产生的某个丑数乘 2 或乘 3 或乘 5 得到</p>
<p>为了按照顺序,必须把前面每个丑数乘 2 或乘 3 或乘 5 得到的值中取大于当前最后一个丑数的最小值</p>
<p>那么问题来了,有必要把每个丑数都乘这三个因子然后取最小值?</p>
<p>我们发现每个丑数都要经历乘 2 乘 3 乘 5 的过程,但却没有必要在同一次竞争下一个丑数中乘</p>
<p>所以我们反过来,标记上那些需要乘 2 或乘 3 或乘 5 的数,使得 index2 指向的数就要乘 2</p>
<p>因为它在下一次竞争中可能会胜利,index3 和 index5 同理。为了满足以上规则</p>
<p>我们让这三个标记从左向右各自独立遍历,这样也就让每个数都会经历乘 2 或乘 3 或乘 5 的过程</p>
<p>且如果标记的数乘以相应因子后竞争胜利了,那么该标记就要往后挪 1 位</p>
<p>因为新的丑数是该标记因子乘以它指向的数竞争胜利而生成的</p>
<p>所以该数乘以该因子已经没有参与下一次竞争的机会了,相应的因子标记就该往后挪</p>
<p>使得下一个数参与新的竞争。而其他竞争失败的标记不用动,因为它们还有竞争胜利的机会</p>
<p>毕竟每次胜利的是那个乘积最小的</p>
<h3 id="代码-31"><a href="#代码-31" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def GetUglyNumber(self, index):
if not index: return 0
idx2 = idx3 = idx5 = 0
uglynums = [1]
for _ in range(index-1):
uglynums.append(min(uglynums[idx2]*2, uglynums[idx3]*3, uglynums[idx5]*5))
# 可能会有多个标记竞争胜利,即丑数恰好是前面标记所在值的公倍数
# 因此必须是并列的if,不能if...elif...else
if uglynums[-1] == uglynums[idx2]*2: idx2 += 1
if uglynums[-1] == uglynums[idx3]*3: idx3 += 1
if uglynums[-1] == uglynums[idx5]*5: idx5 += 1
return uglynums[-1]
</code></pre>
<h2 id="第一个只出现一次的字符"><a href="#第一个只出现一次的字符" class="headerlink" title="第一个只出现一次的字符"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/35_1_%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%8F%AA%E5%87%BA%E7%8E%B0%E4%B8%80%E6%AC%A1%E7%9A%84%E5%AD%97%E7%AC%A6.py" target="_blank" rel="external">第一个只出现一次的字符</a></h2><h3 id="题目描述-32"><a href="#题目描述-32" class="headerlink" title="题目描述"></a>题目描述</h3><p>在一个字符串(1 <= 字符串长度 <= 10000,全部由字母组成)中找到第一个只出现一次的字符</p>
<p>并返回它的位置,不存在就返回 -1</p>
<h3 id="最优题解-32"><a href="#最优题解-32" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间复杂度 O(n),空间复杂度 O(1)(不会超过 256 个键值对)</p>
<p>利用 python 的字典建立哈希表,键记录字母,值记录字母出现的次数</p>
<p>第一次遍历建立哈希表,第二次遍历找到第一个值为 1 的键(字母)</p>
<h3 id="代码-32"><a href="#代码-32" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">from collections import OrderedDict
class Solution:
def FirstNotRepeatingChar(self, s):
table = OrderedDict()
for ch in s:
table[ch] = table.setdefault(ch, 0) + 1
for ch, v in table.items():
if v == 1:
return s.index(ch)
return -1
</code></pre>
<h3 id="举一反三-8"><a href="#举一反三-8" class="headerlink" title="举一反三"></a>举一反三</h3><ul>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/35_2_%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E5%88%A0%E9%99%A4%E5%AD%97%E7%AC%A6.py" target="_blank" rel="external">字符串中删除字符</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/35_3_%E5%88%A0%E9%99%A4%E9%87%8D%E5%A4%8D%E7%9A%84%E5%AD%97%E7%AC%A6.py" target="_blank" rel="external">删除重复的字符</a></li>
<li><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/35_4_%E5%8F%98%E4%BD%8D%E8%AF%8D.py" target="_blank" rel="external">变位词</a></li>
</ul>
<h2 id="数组中的逆序对"><a href="#数组中的逆序对" class="headerlink" title="数组中的逆序对"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/36_%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E9%80%86%E5%BA%8F%E5%AF%B9.py" target="_blank" rel="external">数组中的逆序对</a></h2><h3 id="题目描述-33"><a href="#题目描述-33" class="headerlink" title="题目描述"></a>题目描述</h3><p>在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对</p>
<p>输入一个数组, 求出这个数组中逆序对的总数</p>
<h3 id="最优题解-33"><a href="#最优题解-33" class="headerlink" title="最优题解"></a>最优题解</h3><p>归并排序,在合并时统计逆序对个数</p>
<p>设合并的两个数组为 a1 和 a2,用两个指针 p1,p2,指向 a1 和 a2 的末尾元素,并每次比较两个指针指向的数字。</p>
<p>如果 p1 数字大于 p2,则构成逆序对,并且逆序对的数目等于 p2 之前的元素个数(包括 p2 指向的元素)</p>
<p>因为 a2 是已经排好序的,则 p2 之前的数都小于 p1 指向的数。</p>
<p>反之,如果 p1 数字小于等于 p2,则不构成逆序对</p>
<p>每一次比较都把较大的数从后往前复制到一个辅助数组里,确保辅助数组里的数字是递增排序的</p>
<p>然后把较大的数的指针往前移一位,进行下一轮比较</p>
<p>所以总的逆序对数就是先把数组分隔成子数组,先统计出两个子数组内部的逆序对数</p>
<p>然后统计出两个相邻子数组之间的逆序对数,三者之和</p>
<h3 id="代码-33"><a href="#代码-33" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class Solution:
def merge(data, copy, l, r):
if l == r: return 0
m = (l + r) // 2
# 注意这里的copy和data位置交换了,这样就能保证递归回来时,上一层拿到的data是下一层已经排好序的copy
left = merge(copy, data, l, m) # 左区域的逆序对个数
right = merge(copy, data, m + 1, r) # 右区域的逆序对个数
i, j = m, r # i初始化为前半段最后一个数字的下标,j初始化为后半段最后一个数字的下标
count, copyIdx = 0, r
while i >= l and j >= m + 1:
# 复制的时候统计两个子数组之间的逆序对数
if data[i] > data[j]:
copy[copyIdx] = data[i]
count += j - m # 逆序对的数目等于j之前的元素个数(包括j指向的元素)
i -= 1
else:
copy[copyIdx] = data[j]
j -= 1
copyIdx -= 1
# 将剩下复制到辅助数组里
while i >= l:
copy[copyIdx] = data[i]
i -= 1
copyIdx -= 1
while j >= m + 1:
copy[copyIdx] = data[j]
j -= 1
copyIdx -= 1
return left + right + count # 总逆序对个数 = 左区域 + 右区域 + 合并
if not data: return 0
return merge(data, data[:], 0, len(data) - 1)
</code></pre>
<h2 id="两个链表的第一个公共结点"><a href="#两个链表的第一个公共结点" class="headerlink" title="两个链表的第一个公共结点"></a><a href="https://github.com/Hk4Fun/algorithm_offer/blob/master/target_offer/37_%E4%B8%A4%E4%B8%AA%E9%93%BE%E8%A1%A8%E7%9A%84%E7%AC%AC%E4%B8%80%E4%B8%AA%E5%85%AC%E5%85%B1%E7%BB%93%E7%82%B9.py" target="_blank" rel="external">两个链表的第一个公共结点</a></h2><h3 id="题目描述-34"><a href="#题目描述-34" class="headerlink" title="题目描述"></a>题目描述</h3><p>输入两个单向无环链表,找出它们的第一个公共结点。</p>
<h3 id="最优题解-34"><a href="#最优题解-34" class="headerlink" title="最优题解"></a>最优题解</h3><p>时间复杂度 O(m + n),空间复杂度 O(1)</p>
<p>两个链表指针 p1,p2 从头开始一起遍历,但是 p1 来到末尾时指向另一个链表的头结点(不是指向原链表的头结点),p2 也一样</p>
<p>这样第一次遍历时,短的链表指针(假如为 p1)来到长链表(假如为 l2)的头结点,此时 p2 还在 l2 上,且 p2 距离末尾(l2 - l1)</p>
<p>p2 来到 l1 头结点时,p1 在 l2 上距离末尾(l2 - (l2 - l1))此时 p1 的位置正好就是长指针先出发来到二者长度差的位置,下面一起遍历一定能够相遇</p>
<p>即使没有公共结点,也会在 None 处一起返回(相当于相遇)</p>
<p>整个过程最坏情况下 p1、p2 各遍历两个链表一次,所以时间复杂度 O(m + n),空间复杂度 O(1)</p>
<h3 id="代码-34"><a href="#代码-34" class="headerlink" title="代码"></a>代码</h3><pre><code class="python">class ListNode: