-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
479 lines (387 loc) · 122 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>ZhiXingHeYi</title>
<link href="/atom.xml" rel="self"/>
<link href="http://www.depu.online/"/>
<updated>2016-09-02T15:12:28.000Z</updated>
<id>http://www.depu.online/</id>
<author>
<name>Depu Lai</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>排序专题(七)——算法总结</title>
<link href="http://www.depu.online/2016/09/02/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E4%B8%83-%E2%80%94%E2%80%94%E7%AE%97%E6%B3%95%E6%80%BB%E7%BB%93/"/>
<id>http://www.depu.online/2016/09/02/排序专题-七-——算法总结/</id>
<published>2016-09-02T11:55:21.000Z</published>
<updated>2016-09-02T15:12:28.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
<entry>
<title>排序专题(六)——计数排序、桶排序和基数排序</title>
<link href="http://www.depu.online/2016/09/02/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E5%85%AD-%E2%80%94%E2%80%94%E8%AE%A1%E6%95%B0%E6%8E%92%E5%BA%8F%E3%80%81%E6%A1%B6%E6%8E%92%E5%BA%8F%E5%92%8C%E5%9F%BA%E6%95%B0%E6%8E%92%E5%BA%8F/"/>
<id>http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序/</id>
<published>2016-09-02T11:53:51.000Z</published>
<updated>2016-09-03T08:54:33.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
<entry>
<title>排序专题(五)——堆排序</title>
<link href="http://www.depu.online/2016/09/02/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E4%BA%94-%E2%80%94%E2%80%94%E5%A0%86%E6%8E%92%E5%BA%8F/"/>
<id>http://www.depu.online/2016/09/02/排序专题-五-——堆排序/</id>
<published>2016-09-02T11:50:46.000Z</published>
<updated>2016-09-03T10:23:26.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>生活的秘密在于······用一个烦恼代替另一个烦恼。&emsp; &emsp;&emsp; &emsp;——查尔斯·M.舒尔茨</strong></p>
</blockquote>
<p>本节主要和大家讨论一下堆排序,尽管大家对于堆这种数据结构和它在排序上的应用已经非常熟悉,但是用变治策略的新眼光来观察堆排序,我们还是仍有所收获的。<br>变治法主要分为两个阶段工作的。首先,在”变”的截断,处于某种原因,把问题的实例变得容易求解,然后在”治”的阶段,对实例进行求解。根据对问题实例的变换方式,变治思想有3种主要类型:</p>
<ul>
<li>(1) 变换为同样问题的一个更简单或者更方便的实例——称之为<strong>实例化简</strong></li>
<li>(2) 变换为同样实例的不同表现——称之为<strong>改变表现</strong></li>
<li>(3) 变换为另一个问题的实例,这种问题的算法是已知的——称之为<strong>问题化简</strong></li>
</ul>
<h2 id="堆排序"><a href="#堆排序" class="headerlink" title="堆排序"></a>堆排序</h2><p><strong>特性</strong>:<font color="red">In-place sort</font>、<font color="red">unstable sort</font></p>
<p><img class="aligncenter size-full wp-image-51210" src="http://www.36dsj.com/wp-content/uploads/2016/05/300.gif" alt="堆排序" width="280" height="214" data-tag="bdshare" data-bd-imgshare-binded="1"></p>
<p>堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。<br>1.堆在内存中的表现形式是以数组的形式存储<br>2.堆是一个完全二叉树<br>建堆:建堆的过程就是一个反复筛选的过程<br>1.每次都插到数组的末端,形成一个完全二叉树;<br>2.从最后一个非终端节点开始,直到第一个节点为止(只和子节点比较);<br>删除堆:<br>1.每次都是删除第一个;<br>2.然后最后一个数字补进来;<br>3.然后调整堆<br>调整堆:<br>1.每次都是与最小的儿子进行交换;<br>2.每次都是从倒数第一个非终端节点开始调整;</p>
<p><strong>算法步骤</strong>:</p>
<p>1)创建一个堆H[0..n-1]</p>
<p>2)把堆首(最大值)和堆尾互换</p>
<p>3)把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置</p>
<p>4) 重复步骤2,直到堆的尺寸为1</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line">//1.下标计算[为与程序对应,下标从0开始]</div><div class="line">Parent(i):</div><div class="line"> return i/2</div><div class="line">Left(i):</div><div class="line"> return 2*i+1</div><div class="line">Right(i):</div><div class="line"> return 2*i+2</div><div class="line">//2.使下标i元素为根的的子树成为最大堆</div><div class="line">MAX_HEAPIFY(A,i):</div><div class="line">l&lt;——Left(i)</div><div class="line">r&lt;——Right(i)</div><div class="line">if l&lt;length(A) and A[l]&gt;A[i]</div><div class="line"> then largest&lt;——l</div><div class="line"> else largest&lt;——i</div><div class="line">if r&lt;length(A) and A[r]&gt;A[largest]</div><div class="line"> then largest&lt;——r</div><div class="line">if largest != i</div><div class="line"> then exchange A[i] &lt;——&gt; A[largest]</div><div class="line"> MAX_HEAPIFY(A,largest)</div><div class="line">//3.最大堆的建立,将数组A编译成一个最大堆</div><div class="line">BUILD_MAX_HEAP(A):</div><div class="line"> heapsize[A]&lt;——length[A]</div><div class="line">for i &lt;—— length[A]/2+1 to 0</div><div class="line"> MAX_HEAPIFY(A,i)</div><div class="line">//4.堆排序</div><div class="line">HEAP_SORT(A):</div><div class="line"> BUILD_MAX_HEAP(A)</div><div class="line"> for i&lt;——length[A]-1 to 1</div><div class="line"> do exchange A[1] &lt;——&gt; A[i]</div><div class="line"> length[A]&lt;—— length[A]-1</div><div class="line"> MAX_HEAPIFY(A,0)</div></pre></td></tr></table></figure>
<p><strong>算法复杂度</strong>:<br>时间复杂度:<br>堆排序的时间,主要由<strong>建立初始堆</strong>和<strong>反复堆调整</strong>这两部分的时间开销构成,堆排序的时间复杂度不依赖于特定的输入,在任何情况下,时间复杂度为<strong>$$Ο(nlogn)$$</strong> 。<br>重新调整堆的时间复杂度为O(logN),共N – 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN),二者相加还是O(N <em> logN)。故堆排序的时间复杂度为O(N </em> logN)。<br>如果从底部最后的父节点开始建堆,那么我们可以大概算一下: 假如有N个节点,那么高度为H=logN,最后一层每个父节点最多只需要下调1次,倒数第二层最多只需要下调2次,顶点最多需要下调H次,而最后一层父节点共有2^(H-1)个,倒数第二层公有2^(H-2),顶点只有1(2^0)个,所以总共的时间复杂度为s = 1 <em> 2^(H-1) + 2 </em> 2^(H-2) + … + (H-1) <em> 2^1 + H </em> 2^0 将H代入后s= 2N - 2 - log2(N),近似的最坏建堆时间复杂度就是O(N) 注意不是O(logN),重新调整堆的时间复杂度才是O(logN)。重新进行保持堆特性为O(logN),因此O(N)+O(NlogN)</p>
<p>空间复杂度:</p>
<h2 id="堆排序是一种内部排序-不需要额外的辅助空间-空间复杂度为-O-1"><a href="#堆排序是一种内部排序-不需要额外的辅助空间-空间复杂度为-O-1" class="headerlink" title="堆排序是一种内部排序,不需要额外的辅助空间,空间复杂度为$$O(1)$$"></a>堆排序是一种内部排序,不需要额外的辅助空间,空间复杂度为<strong>$$O(1)$$</strong></h2><p>堆排序非常适合于求解TOP K问题,如对于一个网络游戏,从海量用户中实时获取前十名分数最高的玩家。这个时候可以考虑堆排序,因为每次排序的结果就是找到当前堆中的最大/最小值。因此完成需求的时间复杂度为m*O(logN)。当我们需要找到常数级的最大/最小值时,往往堆排序是我们应该最先考虑的</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul>
<li>堆是一颗近似完全二叉树,它的键都满足父母优势要求(大顶堆或小顶堆)。虽然定义为二叉树,但一般用数组来实现堆。堆结构是堆排序和优先队列高效实现的基础。</li>
<li>堆排序在理论上是一种重要的排序算法,其基本思路是,在排列好好堆中的数组元素后,再从剩余的堆中连续删除最大(或最小)的元素。在任何情况下,算法的时间复杂度为O(nlogn)。其和归并排序的时间效率属于同一类,而与后者不同的是,堆排序是一种内部排序(在位排序),不需要额外的辅助空间。</li>
</ul>
<p><a href="https://github.com/ZhiXingHeYiApple/Sort_Topics/blob/master/SortTopics/" target="_blank" rel="external"><font color="#FF6100">查看堆排序的具体实现代码</font></a></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
<entry>
<title>排序专题(四)——快速排序</title>
<link href="http://www.depu.online/2016/09/02/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E5%9B%9B-%E2%80%94%E2%80%94%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F/"/>
<id>http://www.depu.online/2016/09/02/排序专题-四-——快速排序/</id>
<published>2016-09-02T11:50:26.000Z</published>
<updated>2016-09-03T09:12:23.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>缘夫天下之大,非一人之所能治,而分治之以群工。&emsp; &emsp;&emsp; &emsp;——黄宗羲</strong></p>
<p><strong>一个人不论在祈祷什么,他总是祈祷着一个奇迹的降临。任何祷辞都不外是这样的意思:“伟大的上帝呵,请使二乘二不等于四吧。&emsp; &emsp;&emsp; &emsp;——伊万·屠格涅夫</strong></p>
</blockquote>
<p>上一节我们介绍了归并排序,本节我们将重点讲解以下快速排序。其也是采用分治策略的算法,但它不像归并排序那样是按照元素数组在数组中的位置对它进行划分,快速排序是按照元素的值对其进行划分操作的。快速排序通过划分操作使得基准元左侧的元素都小于等于它,而右侧的元素都大于等于它(基准元)。</p>
<h2 id="快速排序"><a href="#快速排序" class="headerlink" title="快速排序"></a>快速排序</h2><p><strong>特性</strong>:<font color="red">Out-place sort</font>、<font color="red">Unstable sort</font><br><img class="aligncenter size-full wp-image-51209" src="http://www.36dsj.com/wp-content/uploads/2016/05/299.gif" alt="快速排序" width="280" height="214" data-tag="bdshare" data-bd-imgshare-binded="1"></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
<entry>
<title>排序专题(三)——归并排序</title>
<link href="http://www.depu.online/2016/09/02/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E4%B8%89-%E2%80%94%E2%80%94%E5%BD%92%E5%B9%B6%E6%8E%92%E5%BA%8F/"/>
<id>http://www.depu.online/2016/09/02/排序专题-三-——归并排序/</id>
<published>2016-09-02T11:50:09.000Z</published>
<updated>2016-09-03T09:23:22.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>缘夫天下之大,非一人之所能治,而分治之以群工。&emsp; &emsp;&emsp; &emsp;——黄宗羲</strong></p>
<p><strong>一个人不论在祈祷什么,他总是祈祷着一个奇迹的降临。任何祷辞都不外是这样的意思:“伟大的上帝呵,请使二乘二不等于四吧。。&emsp; &emsp;&emsp; &emsp;——伊万·屠格涅夫</strong></p>
</blockquote>
<p>分治法可能是最著名的通用算法设计技术了。很多非常有效的算法实际上就是这个通用算法的特殊实现。分治法是按照以下方案工作的:</p>
<ul>
<li>(1) 将一个问题划分成同一类型的若干个子问题,子问题最好规模相同。</li>
<li>(2) 对这些子问题求解(通常采用递归方式,但当问题规模小到一定程度后,有时会利用另一算法)</li>
<li>(3) 如果有必要的话,合并这些子问题的解,以得到原始问题的答案。</li>
</ul>
<p>本节要介绍的归并排序,就是成功应用分治策略的一个完美例子。对于一个需要排序的数组A[0..n-1],归并排序把它一分为二,得到两个子数组并对子数组递归排序,然后把这两个排好序的子数组合并为一个有序数组。</p>
<hr>
<h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><p><strong>特性</strong>:<font color="red">Out-place sort</font>,<font color="red">stable sort</font>。<br><img class="aligncenter size-full wp-image-51208" src="http://www.36dsj.com/wp-content/uploads/2016/05/298.gif" alt="归并排序" width="280" height="237" data-tag="bdshare" data-bd-imgshare-binded="1">
</p>
<p><strong>算法思想</strong>:</p>
<p>归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。用分治策略解决问题分为三步:<font color="red"><strong>分解、解决、合并</strong></font>。也即:将原问题划分成n个规模较小而结构与原问题相似的子问题; 递归地解决这些子问题,然后再合并其结果,得到原问题的解。此处n=2<br>基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。</p>
<p><strong>算法步骤</strong>:</p>
<ol>
<li><p>申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列</p>
</li>
<li><p>设定两个指针,最初位置分别为两个已经排序序列的起始位置</p>
</li>
<li><p>比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置</p>
</li>
<li><p>重复步骤3直到某一指针达到序列尾</p>
</li>
<li><p>将另一序列剩下的所有元素直接复制到合并序列尾</p>
<a id="more"></a>
</li>
</ol>
<img src="/2016/09/02/排序专题-三-——归并排序/sort_algorithm03_1.png" alt="sort_algorithm03_1.png" title="">
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//合并排序伪代码(使用哨兵):</span></div><div class="line">merge(A,p,q,r):</div><div class="line"> n1 &lt;—— q-p+<span class="number">1</span></div><div class="line"> n2 &lt;—— r-q</div><div class="line"> create <span class="built_in">array</span> L[<span class="number">0</span>,n1] and R[<span class="number">0</span>,n2]</div><div class="line"> <span class="keyword">for</span> i &lt;—— <span class="number">0</span> to n1<span class="number">-1</span></div><div class="line"> <span class="keyword">do</span> L[i] &lt;—— A[p+i]</div><div class="line"> <span class="keyword">for</span> j &lt;—— <span class="number">0</span> to n2<span class="number">-1</span></div><div class="line"> <span class="keyword">do</span> R[j] &lt;—— A[q+j+<span class="number">1</span>]</div><div class="line"> L[n1] &lt;—— +∞</div><div class="line"> R[n2] &lt;—— +∞</div><div class="line"> i &lt;—— <span class="number">0</span></div><div class="line"> j &lt;—— <span class="number">0</span></div><div class="line"> <span class="keyword">for</span> k i &lt;—— p to r</div><div class="line"> <span class="keyword">do</span> <span class="keyword">if</span> L[i]&lt;=R[j]</div><div class="line"> then A[k] &lt;—— L[i]</div><div class="line"> i &lt;—— i+<span class="number">1</span></div><div class="line"> <span class="keyword">else</span> A[k] &lt;—— R[j]</div><div class="line"> j &lt;—— j+<span class="number">1</span></div><div class="line"></div><div class="line"><span class="comment">//通过调用merge完成排序:</span></div><div class="line">merge_sort(A,p,r):</div><div class="line"> <span class="keyword">if</span> p&lt;r then</div><div class="line"> q &lt;—— [(p+r)/<span class="number">2</span>] <span class="comment">//向下取整</span></div><div class="line"> merge_sort(A,p,q) <span class="comment">//分治</span></div><div class="line"> merge_sort(A,q+<span class="number">1</span>,r)</div><div class="line"> merge(A,p,q,r) <span class="comment">//合并结果</span></div></pre></td></tr></table></figure>
<p><strong>算法复杂度</strong>:</p>
<p>时间复杂度:<br>假设Divide需要f(n)时间,Conquer分解为b个子问题,且子问题大小为a,Combine需要g(n)时间,则递归式为:<br>T(n)=bT(n/a)+f(n)+g(n)<br>如归并排序,Divide的步骤为m=(p+q)/2,因此为O(1),Combine步骤为merge()函数,Conquer步骤为分解为2个子问题,子问题大小为n/2,因此:<br>归并排序的递归式:T(n)=2T(n/2)+O(n)<br>归并排序的效率是比较高的,且不依赖于特定的输入,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N<em>logN)。因为归并排序每次都是在相邻的数据中进行操作,尽管归并排序在O(N</em>logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)平均渐进复杂度是一样的,但是归并排序的系数比快排大,所以归并排序的效率较差一些,其显著优点在于稳定性。</p>
<p>空间复杂度:<br>当待排序的是一个数组(顺序存储结构)时,归并排序是一种外排序(Out-place sort),需要很多额外的空间,空间复杂度为<strong>$$O(n)$$</strong>。而当待排序的数据是一个链表时,此时并不需要额外的存储空间(关键在于链表结点指针的重新组织)。具体实现代码如下(此时空间复杂度为<strong>O(1)</strong>):<br><figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">class</span> Solution &#123;<span class="comment">/* 链表归并排序 */</span></div><div class="line"><span class="keyword">public</span>:</div><div class="line"> <span class="function"><span class="keyword">void</span> <span class="title">mergeLists</span><span class="params">( ListNode * &amp;head1, ListNode *head2 )</span></span>&#123;</div><div class="line"> <span class="comment">/* 链表1的头指针应引用传递(合并后的新链表会赋值给它) */</span></div><div class="line"> <span class="keyword">if</span> ( head1 == <span class="literal">NULL</span> )</div><div class="line"> &#123;</div><div class="line"> head1 = head2;</div><div class="line"> <span class="keyword">return</span>;</div><div class="line"> &#125;</div><div class="line"> ListNode *HEAD = <span class="keyword">new</span> ListNode( <span class="number">0</span> );</div><div class="line"></div><div class="line"></div><div class="line"> HEAD-&gt;next = head1;</div><div class="line"> ListNode *cur1 = head1, *pre1 = HEAD, *cur2 = head2;</div><div class="line"> <span class="keyword">while</span> ( cur1 &amp;&amp; cur2 )</div><div class="line"> &#123;</div><div class="line"> <span class="keyword">if</span> ( cur2-&gt;val &lt; cur1-&gt;val )</div><div class="line"> &#123;</div><div class="line"> pre1-&gt;next = cur2;</div><div class="line"> pre1 = pre1-&gt;next;</div><div class="line"> cur2 = cur2-&gt;next; <span class="comment">/* 这句要放在下面这行代码之前 */</span></div><div class="line"> pre1-&gt;next = cur1;</div><div class="line"> &#125;<span class="keyword">else</span>&#123;</div><div class="line"> pre1 = cur1;</div><div class="line"> cur1 = cur1-&gt;next;</div><div class="line"> &#125;</div><div class="line"> &#125;</div><div class="line"> <span class="comment">/* 此时pre1指向链表一尾部 */</span></div><div class="line"> <span class="keyword">if</span> ( cur2 )</div><div class="line"> &#123;</div><div class="line"> pre1-&gt;next = cur2;</div><div class="line"> &#125;</div><div class="line"> head1 = HEAD-&gt;next;</div><div class="line"> &#125;</div><div class="line"></div><div class="line"></div><div class="line"> <span class="comment">/* 归并排序(分治策略) */</span></div><div class="line"> <span class="function">ListNode* <span class="title">mergeSort</span><span class="params">( ListNode*head )</span> <span class="comment">/* 区间左闭右开 */</span></span></div><div class="line"> &#123;</div><div class="line"> <span class="keyword">if</span> ( head-&gt;next == <span class="literal">NULL</span> )</div><div class="line"> &#123;</div><div class="line"> <span class="keyword">return</span>(head);</div><div class="line"> &#125;</div><div class="line"> <span class="comment">/* 利用快慢指针确定中间位置,并断开链表进行分治 */</span></div><div class="line"> ListNode *fast = head, *slow = head, *pre = <span class="literal">NULL</span>, *list1, *list2;</div><div class="line"> <span class="keyword">while</span> ( fast )</div><div class="line"> &#123;</div><div class="line"> pre = slow;</div><div class="line"> slow = slow-&gt;next;</div><div class="line"> <span class="keyword">if</span> ( fast-&gt;next )</div><div class="line"> &#123;</div><div class="line"> fast = fast-&gt;next-&gt;next;</div><div class="line"> &#125;<span class="keyword">else</span>&#123;</div><div class="line"> fast = <span class="literal">NULL</span>;</div><div class="line"> &#125;</div><div class="line"> &#125;</div><div class="line"> <span class="keyword">if</span> ( pre )</div><div class="line"> &#123;</div><div class="line"> pre-&gt;next = <span class="literal">NULL</span>;</div><div class="line"> &#125;</div><div class="line"> list1 = mergeSort( head );</div><div class="line"> list2 = mergeSort( slow );</div><div class="line"> <span class="comment">/* 合并操作 */</span></div><div class="line"> mergeLists( list1, list2 );</div><div class="line"> <span class="keyword">return</span>(list1);</div><div class="line"> &#125;</div><div class="line"></div><div class="line"></div><div class="line"> <span class="function">ListNode *<span class="title">sortList</span><span class="params">( ListNode *head )</span></span></div><div class="line"> &#123;</div><div class="line"> <span class="comment">/*</span></div><div class="line"> * in O(n log n) time using constant space complexity.</div><div class="line"> * 由于需要O(nlogn)时间复杂度,所以只能考虑归并排序和堆排序,快速排序最好情况下才能达到O(nlogn),最坏情况下为O(n^2)</div><div class="line"> *</div><div class="line"> *</div><div class="line"> * 由于需要常数空间进行排序,所以只能考虑插入排序,选择排序,(冒泡排序,快速排序),堆排序,归并排序(链表时,无须辅助存储空间,数组时,需要额外空间)</div><div class="line"> * 综上所述,由于链表不容易建堆进行排序,所以只能选择归并排序了</div><div class="line"> */</div><div class="line"> <span class="keyword">if</span> ( head == <span class="literal">NULL</span> || head-&gt;next == <span class="literal">NULL</span> ) <span class="keyword">return</span>(head);</div><div class="line"> <span class="keyword">return</span>(mergeSort( head ) );</div><div class="line"> &#125;</div><div class="line">&#125;;</div></pre></td></tr></table></figure></p>
<hr>
<h2 id="归并算法的改进思路"><a href="#归并算法的改进思路" class="headerlink" title="归并算法的改进思路"></a>归并算法的改进思路</h2><p>当我们采用递归方式实现归并算法时,对子问题没有必要分解的太细,这样会很大程度上增加递归函数调用的深度,严重影响算法效率。为了改进算法效率,我们可以把上一节中讲解过的插入排序考虑进来,将二者结合,可以有效改进算法性能。我们可以在递归分解数组使其长度变为k时,用插入排序解决子数组排序,因为插入排序适合对小数组排序。此时算法复杂度为O(nk+nlg(n/k)) ,当k=O(lgn)时,复杂度为O(nlgn)。</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul>
<li>归并排序是一种分治排序算法。在任何情况下,算法的时间复杂度都为O(NlogN),其键值比较次数非常接近于理论上的最小值(基于比较操作的排序算法的理论下界为O(NlogN))。</li>
<li>归并排序的主要缺点是对于数组排序,需要相当大的额外存储空间,其显著优点在于稳定性。归并排序又称为多路合并排序,非常适合对存放在二级存储空间的文件进行排序。</li>
</ul>
<p><a href="https://github.com/ZhiXingHeYiApple/Sort_Topics/blob/master/SortTopics/" target="_blank" rel="external"><font color="#FF6100">查看归并排序的具体实现代码</font></a></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>缘夫天下之大,非一人之所能治,而分治之以群工。&emsp; &emsp;&emsp; &emsp;——黄宗羲</strong></p>
<p><strong>一个人不论在祈祷什么,他总是祈祷着一个奇迹的降临。任何祷辞都不外是这样的意思:“伟大的上帝呵,请使二乘二不等于四吧。。&emsp; &emsp;&emsp; &emsp;——伊万·屠格涅夫</strong></p>
</blockquote>
<p>分治法可能是最著名的通用算法设计技术了。很多非常有效的算法实际上就是这个通用算法的特殊实现。分治法是按照以下方案工作的:</p>
<ul>
<li>(1) 将一个问题划分成同一类型的若干个子问题,子问题最好规模相同。</li>
<li>(2) 对这些子问题求解(通常采用递归方式,但当问题规模小到一定程度后,有时会利用另一算法)</li>
<li>(3) 如果有必要的话,合并这些子问题的解,以得到原始问题的答案。</li>
</ul>
<p>本节要介绍的归并排序,就是成功应用分治策略的一个完美例子。对于一个需要排序的数组A[0..n-1],归并排序把它一分为二,得到两个子数组并对子数组递归排序,然后把这两个排好序的子数组合并为一个有序数组。</p>
<hr>
<h2 id="归并排序"><a href="#归并排序" class="headerlink" title="归并排序"></a>归并排序</h2><p><strong>特性</strong>:<font color=red>Out-place sort</font>,<font color=red>stable sort</font>。<br><img class="aligncenter size-full wp-image-51208" src="http://www.36dsj.com/wp-content/uploads/2016/05/298.gif" alt="归并排序" width="280" height="237" data-tag="bdshare" data-bd-imgshare-binded="1">
</p>
<p><strong>算法思想</strong>:</p>
<p>归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。用分治策略解决问题分为三步:<font color=red><strong>分解、解决、合并</strong></font>。也即:将原问题划分成n个规模较小而结构与原问题相似的子问题; 递归地解决这些子问题,然后再合并其结果,得到原问题的解。此处n=2<br>基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。</p>
<p><strong>算法步骤</strong>:</p>
<ol>
<li><p>申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列</p>
</li>
<li><p>设定两个指针,最初位置分别为两个已经排序序列的起始位置</p>
</li>
<li><p>比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置</p>
</li>
<li><p>重复步骤3直到某一指针达到序列尾</p>
</li>
<li><p>将另一序列剩下的所有元素直接复制到合并序列尾</p>
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
<entry>
<title>排序专题(二)——插入排序和希尔排序</title>
<link href="http://www.depu.online/2016/09/02/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E4%BA%8C-%E2%80%94%E2%80%94%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F%E5%92%8C%E5%B8%8C%E5%B0%94%E6%8E%92%E5%BA%8F/"/>
<id>http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序/</id>
<published>2016-09-02T11:46:40.000Z</published>
<updated>2016-09-03T09:06:32.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>普卢塔克说,萨特斯为了告诉他的士兵坚忍和智慧比蛮力更重要的道理,把两匹马带到他们面前,然后让两个人拔光马的尾毛。一个人是魁梧的大力士,他用力地拔了又拔,但一点效果也没有;另一个人是一个精美的、长相矫捷的裁缝,他微笑着,每次拔掉一根毛,很快就把尾巴拔得光秃秃的。!&emsp; &emsp;&emsp; &emsp;——科巴姆·布鲁尔</strong></p>
</blockquote>
<img src="/2016/09/02/排序专题-二-——插入排序和希尔排序/sort_algorithm02_1.jpg" alt="sort_algorithm02_1.jpg" title="">
<h2 id="蛮力法"><a href="#蛮力法" class="headerlink" title="蛮力法"></a>蛮力法</h2><p>减治技术利用了一个问题给定实例的解和同样问题较小实例的解之间的某种关系。一旦建立这种关系,我们既可以从顶至下,也可以从底至上地来利用该关系。其中自顶向下会自然导致出递归算法,而自底向上版本往往是迭代实现(该方法又叫增量法)。<br>减治法主要表现为3种主要变化形式:</p>
<ul>
<li>减去一个常量</li>
<li>减去一个常量因子</li>
<li>减去可变的规模</li>
</ul>
<p>本节要讲述的直接插入排序和希尔排序,本质上都是”插入排序”。插入排序算法在一定程度上体现了减治技术在算法中的应用。</p>
<hr>
<h2 id="直接插入排序"><a href="#直接插入排序" class="headerlink" title="直接插入排序"></a>直接插入排序</h2><p><strong>特性</strong>:<font color="red">In-place sort</font>、<font color="red">stable sort</font>、<font color="red">适合于小数组排序</font><br><img class="alignnone" src="http://cricode.qiniudn.com/insert-sort.gif" alt="" width="300" height="180"><br>思考这样一个问题,假设当我们对较小数组A[0..n-2]排序的问题已经解决了,得到一个大小为n-1的有序数组: A[0]&lt;=…&lt;=A[n-2]。如何利用这个较小规模的解,并将元素A[n-1]考虑进来,来得到原问题的解呢?<br>很显然,就像我们平时玩扑克牌时对手里的扑克牌进行排序整理的方式相同,我们要做的就是在这些有序的元素中为A[n-1]找到一个合适的位置,然后把它插入到那里。一般来说,我们从左往右扫描这个有序的子数组,直到遇到第一个小于等于A[n-1]的元素,然后把A[n-1]插在该元素后面。这也是插入法名字的由来。</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//非递归版本插入排序(自底向上)</span></div><div class="line">InsertionSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用插入排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> <span class="keyword">for</span> i&lt;- <span class="number">1</span> to n<span class="number">-1</span> <span class="keyword">do</span></div><div class="line"> key&lt;- A[i]</div><div class="line"> j&lt;- i<span class="number">-1</span></div><div class="line"> <span class="keyword">while</span> j&gt;=<span class="number">0</span> and key&lt;A[j] <span class="keyword">do</span></div><div class="line"> A[j+<span class="number">1</span>]&lt;- A[j];</div><div class="line"> j&lt;- j<span class="number">-1</span></div><div class="line"> A[j+<span class="number">1</span>]&lt;- key;</div></pre></td></tr></table></figure>
<figure class="highlight"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">//递归版本插入排序(自顶向下)</div><div class="line">Recursive_InsertionSort(A[0..n-1],p,q)</div><div class="line"> if p&lt;q then</div><div class="line"> Recursive_InsertionSort(A,p,q-1) //递归将A[p..q-1]进行排序</div><div class="line"> Insert(A,p,q-1)</div><div class="line"> </div><div class="line">Insert(A[0..n-1],p,q)</div><div class="line"> key&lt;- A[q+1]</div><div class="line"> j&lt;- q</div><div class="line"> while j&gt;=0 and key&lt;A[j] do</div><div class="line"> A[j+1]&lt;- A[j]</div><div class="line"> j&lt;- j-1</div><div class="line"> A[j+1]&lt;- key</div></pre></td></tr></table></figure>
<a id="more"></a>
<p><strong>算法复杂度</strong>:<br>时间复杂度:<br>插入排序是基于比较操作的排序算法,所以算法分析时,该算法的基本操作是键值比较。<br>最优复杂度:当输入数组就是排好序的时候,需要n-1次比较操作,复杂度为<strong>$$O(n)$$</strong>,而快速排序在这种情况下会产生O(n^2)的复杂度。<br>最差复杂度:其实插入排序的复杂度和逆序对的个数一样,当数组倒序时,逆序对的个数为n(n-1)/2,因此插入排序最坏复杂度为<strong>$$O(n^2)$$</strong>。<br>平均复杂度: 对于随机序列的数组,插入排序的平均比较次数是降序数组的一半,也就是说需要约(n^2)/4次比较操作,复杂度为<strong>$$O(n^2)$$</strong><br>直接插入排序的平均性能比最差性能快一倍,以及遇到基本有序的数组时表现出的优异性能,使得插入排序领先于它在基本排序算法领域的主要竞争对手 —— 选择排序和冒泡排序。</p>
<p>空间复杂度:<br>直接插入排序是一种内部排序(In-place sort),所以不需要额外的辅助空间,其空间复杂度为<strong>$$O(1)$$</strong></p>
<hr>
<h2 id="希尔排序"><a href="#希尔排序" class="headerlink" title="希尔排序"></a>希尔排序</h2><p><strong>特性</strong>:<font color="red">In-place sort</font>、<font color="red">unstable sort</font>、<font color="red">适合于较大的数组排序</font><br><img class="aligncenter" src="http://cricode.qiniudn.com/shellsort_anim.gif" alt="" width="277" height="344"><br>希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。</p>
<p>希尔排序是基于插入排序的以下两点性质而提出改进方法的:</p>
<ul>
<li>(1)插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率</li>
<li>(2)但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位</li>
</ul>
<p>希尔排序的基本思想是:先将整个待排序的记录序列<font color="red">分割成为若干子序列分别进行直接插入排序</font>,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。</p>
<p>直接插入排序也适用于链式存储结构;<font color="red">希尔排序不适用于链式结构</font>。</p>
<p>对于希尔排序来说,步长为<font color="red"> 1, 4, 13, 40, 121</font>的效率是最高的</p>
<p><strong>算法步骤</strong>:</p>
<p>1)选择一个增量序列t1,t2,…,tk,其中ti&gt;tj,tk=1;</p>
<p>2)按增量序列个数k,对序列进行k 趟排序; 增量序列gap的取法必须满足:<font color="red"><em>最后一个步长必须是</em> 1</font></p>
<p>3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。</p>
<p><strong>算法复杂度</strong>:<br>时间复杂度:<br>希尔排序的算法时间性能依赖于特定的输入。最好的情况下,数组已经按照升序排列了,此时时间复杂度为<strong>$$O(n)$$</strong>,而当输入数组为一个严格递减的数组时,对其升序排序,达到最差时间复杂度为<strong>$$O(n^2)$$</strong>。除了这两种极端情况外,其它大部分数组排序的平均时间复杂度约为<strong>O(n^1.3)</strong><br>空间复杂度:<br>希尔排序是对直接插入排序的扩展可改进,也是一种内部排序(In-place sort),所以不需要额外的辅助空间,其空间复杂度为<strong>$$O(1)$$</strong></p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul>
<li>减治法是一种一般性的算法设计技术,该方法有3种主要变化形式:(1)减一个常量(例如插入排序);(2)减一个常因子(例如二分查找);(3)减可变规模</li>
<li>直接插入排序是减一技术在排序问题上的运用。无论在平均情况下还是在最差情况下,它都是一个O(n^2)的算法,但在平均情况下的效率大约要比最差情况快一倍。该算法很适合于小规模的且基本有序的数组。</li>
<li>希尔排序是插入排序的扩展优化版本,适用于较大规模的数组,尽管其和插入排序一样都是内部排序,不需要额外的存储控件,但它却牺牲了稳定性。</li>
</ul>
<p><a href="https://github.com/ZhiXingHeYiApple/Sort_Topics/blob/master/SortTopics/" target="_blank" rel="external"><font color="#FF6100">查看插入排序和希尔排序的具体实现代码</font></a></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>普卢塔克说,萨特斯为了告诉他的士兵坚忍和智慧比蛮力更重要的道理,把两匹马带到他们面前,然后让两个人拔光马的尾毛。一个人是魁梧的大力士,他用力地拔了又拔,但一点效果也没有;另一个人是一个精美的、长相矫捷的裁缝,他微笑着,每次拔掉一根毛,很快就把尾巴拔得光秃秃的。!&emsp; &emsp;&emsp; &emsp;——科巴姆·布鲁尔</strong></p>
</blockquote>
<img src="/2016/09/02/排序专题-二-——插入排序和希尔排序/sort_algorithm02_1.jpg" alt="sort_algorithm02_1.jpg" title="">
<h2 id="蛮力法"><a href="#蛮力法" class="headerlink" title="蛮力法"></a>蛮力法</h2><p>减治技术利用了一个问题给定实例的解和同样问题较小实例的解之间的某种关系。一旦建立这种关系,我们既可以从顶至下,也可以从底至上地来利用该关系。其中自顶向下会自然导致出递归算法,而自底向上版本往往是迭代实现(该方法又叫增量法)。<br>减治法主要表现为3种主要变化形式:</p>
<ul>
<li>减去一个常量</li>
<li>减去一个常量因子</li>
<li>减去可变的规模</li>
</ul>
<p>本节要讲述的直接插入排序和希尔排序,本质上都是”插入排序”。插入排序算法在一定程度上体现了减治技术在算法中的应用。</p>
<hr>
<h2 id="直接插入排序"><a href="#直接插入排序" class="headerlink" title="直接插入排序"></a>直接插入排序</h2><p><strong>特性</strong>:<font color=red>In-place sort</font>、<font color=red>stable sort</font>、<font color=red>适合于小数组排序</font><br><img class="alignnone" src="http://cricode.qiniudn.com/insert-sort.gif" alt="" width="300" height="180"><br>思考这样一个问题,假设当我们对较小数组A[0..n-2]排序的问题已经解决了,得到一个大小为n-1的有序数组: A[0]&lt;=…&lt;=A[n-2]。如何利用这个较小规模的解,并将元素A[n-1]考虑进来,来得到原问题的解呢?<br>很显然,就像我们平时玩扑克牌时对手里的扑克牌进行排序整理的方式相同,我们要做的就是在这些有序的元素中为A[n-1]找到一个合适的位置,然后把它插入到那里。一般来说,我们从左往右扫描这个有序的子数组,直到遇到第一个小于等于A[n-1]的元素,然后把A[n-1]插在该元素后面。这也是插入法名字的由来。</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"><span class="comment">//非递归版本插入排序(自底向上)</span></div><div class="line">InsertionSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用插入排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> <span class="keyword">for</span> i&lt;- <span class="number">1</span> to n<span class="number">-1</span> <span class="keyword">do</span></div><div class="line"> key&lt;- A[i]</div><div class="line"> j&lt;- i<span class="number">-1</span></div><div class="line"> <span class="keyword">while</span> j&gt;=<span class="number">0</span> and key&lt;A[j] <span class="keyword">do</span></div><div class="line"> A[j+<span class="number">1</span>]&lt;- A[j];</div><div class="line"> j&lt;- j<span class="number">-1</span></div><div class="line"> A[j+<span class="number">1</span>]&lt;- key;</div></pre></td></tr></table></figure>
<figure class="highlight"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">//递归版本插入排序(自顶向下)</div><div class="line">Recursive_InsertionSort(A[0..n-1],p,q)</div><div class="line"> if p&lt;q then</div><div class="line"> Recursive_InsertionSort(A,p,q-1) //递归将A[p..q-1]进行排序</div><div class="line"> Insert(A,p,q-1)</div><div class="line"> </div><div class="line">Insert(A[0..n-1],p,q)</div><div class="line"> key&lt;- A[q+1]</div><div class="line"> j&lt;- q</div><div class="line"> while j&gt;=0 and key&lt;A[j] do</div><div class="line"> A[j+1]&lt;- A[j]</div><div class="line"> j&lt;- j-1</div><div class="line"> A[j+1]&lt;- key</div></pre></td></tr></table></figure>
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
<entry>
<title>如何写伪代码</title>
<link href="http://www.depu.online/2016/07/19/%E5%A6%82%E4%BD%95%E5%86%99%E4%BC%AA%E4%BB%A3%E7%A0%81/"/>
<id>http://www.depu.online/2016/07/19/如何写伪代码/</id>
<published>2016-07-19T08:57:19.000Z</published>
<updated>2016-07-19T09:39:35.000Z</updated>
<content type="html"><p>看到这个标题,你肯定会很不屑!在真实世界中,你可能掌握了多门编程语言,如swfit,python,java,c++,javascript等等。直接用这些语言来写代码,实现逻辑不就行了嘛!伪代码感觉就没啥用武之地,平时也不怎么接触和使用。但是在IT企业求职或面试过程中,在短短的一二十分钟内,HR想要考察你的专业技能,肯定不可能给你提供真实的编程环境,让你编写代码。通常情况下,是给你纸和笔,让你用伪代码来写出代码的逻辑。这个时候,你可能和我一样,就会有疑问了,具体的伪代码格式该怎么写?这个时候,你可以用你最擅长的一门编程语言的格式来写,但个人认为,除非特殊情况,否则最好用伪代码来写,一方面简洁明了,另一方面HR未必就懂你写的那个语言(通常HR也都是搞技术的,不会有太大问题)。所以最好还是了解一些伪代码的格式。<br>通常伪代码是介于自然语言和计算机编程语言之间的一种算法描述语言。其具有简洁明了易懂的特点。但具体的格式并没有非常严格的标准和规范。所以以下总结的伪代码格式,是参考个人认为比较权威的《算法导论》这本书。</p>
<p>(1)赋值用箭头“←” 或”:=”<br><a id="more"></a></p>
<p>(2)<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">for</span> i←<span class="number">0</span> to <span class="number">10</span> <span class="comment">//for、while、if 后面的条件语句都不用加括号</span></div><div class="line"> <span class="keyword">do</span> XXXXX <span class="comment">//for后面必定要紧跟缩进的do</span></div><div class="line"> XXXXX</div></pre></td></tr></table></figure></p>
<p>(3)<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">while</span> time&lt;<span class="number">10</span></div><div class="line"> <span class="keyword">do</span> xxxxx <span class="comment">//while后面必定要紧跟缩进的do</span></div><div class="line"> xxxxx</div></pre></td></tr></table></figure></p>
<p>(4)<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> i=<span class="number">10</span></div><div class="line"> then xxxx</div><div class="line"> <span class="keyword">else</span> xxxx <span class="comment">//else 和 then 要在对齐</span></div></pre></td></tr></table></figure></p>
<p>(5)<br><figure class="highlight cpp"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">if</span> i=<span class="number">10</span></div><div class="line"> then xxxx <span class="comment">//if 后面必定跟上then,else后面不用跟then</span></div><div class="line">elseif i=<span class="number">9</span></div><div class="line"> then xxxx</div><div class="line"> yyyy</div><div class="line"><span class="keyword">else</span> xxxx <span class="comment">//else 跟在elseif 的 then 对齐</span></div><div class="line"></div><div class="line"><span class="comment">//也可以用如下方式:</span></div><div class="line"></div><div class="line"> <span class="keyword">if</span> i=<span class="number">10</span> Then&#123;</div><div class="line"></div><div class="line"> XXXXXX</div><div class="line"></div><div class="line"> &#125;<span class="keyword">else</span>&#123;</div><div class="line"></div><div class="line"> XXXXXX</div><div class="line"> &#125;</div></pre></td></tr></table></figure></p>
<p>(6)elseif 要合并。</p>
<p>(7)<u>同一嵌套等级的语句要对齐</u>。</p>
<p>(8)定义变量的语句不用写出来,但必须在注释中给出</p>
<p>(9)函数的伪代码格式例子为:search(A,name) <strong>//参数类型可以不给出,但必须在注释中说明</strong></p>
<p>(10)写完的伪代码最后必须在每行伪代码前加上序号</p>
<p>(10)对于常用的一些操作(不是该算法的核心部分),可以利用单词意思来描述就行了,如交换操作,可以使用swap XX and XX(或者exchange)。</p>
</content>
<summary type="html">
<p>看到这个标题,你肯定会很不屑!在真实世界中,你可能掌握了多门编程语言,如swfit,python,java,c++,javascript等等。直接用这些语言来写代码,实现逻辑不就行了嘛!伪代码感觉就没啥用武之地,平时也不怎么接触和使用。但是在IT企业求职或面试过程中,在短短的一二十分钟内,HR想要考察你的专业技能,肯定不可能给你提供真实的编程环境,让你编写代码。通常情况下,是给你纸和笔,让你用伪代码来写出代码的逻辑。这个时候,你可能和我一样,就会有疑问了,具体的伪代码格式该怎么写?这个时候,你可以用你最擅长的一门编程语言的格式来写,但个人认为,除非特殊情况,否则最好用伪代码来写,一方面简洁明了,另一方面HR未必就懂你写的那个语言(通常HR也都是搞技术的,不会有太大问题)。所以最好还是了解一些伪代码的格式。<br>通常伪代码是介于自然语言和计算机编程语言之间的一种算法描述语言。其具有简洁明了易懂的特点。但具体的格式并没有非常严格的标准和规范。所以以下总结的伪代码格式,是参考个人认为比较权威的《算法导论》这本书。</p>
<p>(1)赋值用箭头“←” 或”:=”<br>
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
</entry>
<entry>
<title>排序专题(一)——选择排序与冒泡排序</title>
<link href="http://www.depu.online/2016/07/18/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98-%E4%B8%80-%E2%80%94%E2%80%94%E9%80%89%E6%8B%A9%E6%8E%92%E5%BA%8F%E5%92%8C%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F/"/>
<id>http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序/</id>
<published>2016-07-18T06:22:45.000Z</published>
<updated>2016-09-02T15:09:46.000Z</updated>
<content type="html"><p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>把事情做好,常常是浪费时间!&emsp; &emsp;&emsp; &emsp;——罗伯特·伯恩</strong></p>
<p><strong>剪不断,理还乱,是离愁,别是一番滋味在心头。&emsp; &emsp;&emsp; &emsp;——李清照</strong></p>
</blockquote>
<img src="/2016/07/18/排序专题-一-——选择排序和冒泡排序/sort_algorithm01.png" alt="sort_algorithm01.png" title="">
<p>现实世界中,纷繁凌乱的表象常常掩盖事物的规律和本质。为了去掉这层表象,我们不得不对事物重新组织,让其变的有序。在探索的过程中,人们开发出了几十种排序算法,广泛应用于人们的生活和生产实践中。大家过去或多或少都接触或了解过其中的一些算法,如果是这样,请你暂时忘记它们,以初学者的姿态问自己一个问题:“在只看结果,不考虑其他约束的情况下,解决排序问题最直接了当的方法是什么?” 对于这个问题的答案恐怕是智者见智,仁者见仁。也许你并不同意我的观点,但选择排序和冒泡排序绝对是两个有力的候选者。</p>
<hr>
<h2 id="蛮力法"><a href="#蛮力法" class="headerlink" title="蛮力法"></a>蛮力法</h2><p>蛮力法,经常又被称呼为穷举法或暴力拆解法。是一种简单直接地解决问题的方法,常直接基于问题的描述和所涉及的概念定义。<br>尽管“巧妙”与“高效”这两个高大上的词汇与“蛮力法”这个穷屌丝往往搭不上边。但是我们不应该忽略这个穷屌丝(屌丝也有完美逆袭的一天)的存在,其作为一种重要的算法设计策略的地位是无法撼动的,理由如下:</p>
<ul>
<li><strong>对于解决的问题类型更具一般性和普适性。</strong></li>
<li><strong>虽然效率低,但仍可以解决一些小规模的问题实例。</strong></li>
<li><strong>当要解决的问题实例不多或解空间较小时,蛮力法仍可以用能够接受的速度对问题进行求解,而寻找和设计一个更高效算法所花费的代价可能是不值得的。</strong></li>
<li><strong>蛮力法经常被其他一些“高富帅”的算法作为参照标杆。</strong></li>
</ul>
<p>个人认为在排序算法中,选择排序和冒泡排序俨然就是蛮力法的最佳代言人。这也正是我为什么会选择这两个排序算法作为讲解排序专题的切入点。<br><a id="more"></a></p>
<hr>
<h2 id="选择排序"><a href="#选择排序" class="headerlink" title="选择排序"></a>选择排序</h2><p><strong>特性</strong>:<font color="red">In-place sort</font>(无需额外辅助空间),<font color="red">unstable sort</font>(非稳定)。<br><img class="aligncenter size-full wp-image-51206" src="http://www.36dsj.com/wp-content/uploads/2016/05/296.gif" alt="选择排序" width="288" height="288" data-tag="bdshare" data-bd-imgshare-binded="1"></p>
<p><strong>算法步骤</strong>:<br>1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置</p>
<p>2)再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。</p>
<p>3)重复第二步,直到所有元素均排序完毕。</p>
<p>举例(红色标记的数为每轮迭代找到的最小数)<br><strong><font color="blue">|</font></strong> &emsp;89&emsp;&emsp;46&emsp;&emsp;67&emsp;&emsp;92&emsp;&emsp;29&emsp;&emsp;34&emsp;&emsp;<strong><font color="red">17</font></strong></p>
<p> &emsp;&emsp;17<strong><font color="blue">|</font></strong> &emsp;46&emsp;&emsp;67&emsp;&emsp;92&emsp;&emsp;<strong><font color="red">29</font></strong>&emsp;&emsp;34&emsp;&emsp;89</p>
<p> &emsp;&emsp;17&emsp;&emsp;29<strong><font color="blue">|</font></strong> &emsp;67&emsp;&emsp;92&emsp;&emsp;46&emsp;&emsp;<strong><font color="red">34</font></strong>&emsp;&emsp;89</p>
<p> &emsp;&emsp;17&emsp;&emsp;29&emsp;&emsp;34<strong><font color="blue">|</font></strong> &emsp;92&emsp;&emsp;<strong><font color="red">46</font></strong>&emsp;&emsp;67&emsp;&emsp;89</p>
<p> &emsp;&emsp;17&emsp;&emsp;29&emsp;&emsp;34&emsp;&emsp;46<strong><font color="blue">|</font></strong> &emsp;&emsp;92&emsp;&emsp;<strong><font color="red">67</font></strong>&emsp;&emsp;89</p>
<p> &emsp;&emsp;17&emsp;&emsp;29&emsp;&emsp;34&emsp;&emsp;46&emsp;&emsp;67<strong><font color="blue">|</font></strong> &emsp;92&emsp;&emsp;<strong><font color="red">89</font></strong></p>
<p> &emsp;&emsp;17&emsp;&emsp;29&emsp;&emsp;34&emsp;&emsp;46&emsp;&emsp;67&emsp;&emsp;89&emsp;&emsp;92</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">SelectionSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用选择排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> <span class="keyword">for</span> i&lt;- <span class="number">0</span> to n<span class="number">-2</span> <span class="keyword">do</span></div><div class="line"> min&lt;- i</div><div class="line"> <span class="keyword">for</span> j&lt;- i+<span class="number">1</span> to n<span class="number">-1</span> <span class="keyword">do</span></div><div class="line"> <span class="keyword">if</span> A[j] &lt; A[min] then min&lt;- j</div><div class="line"> swap A[i] and A[min]</div></pre></td></tr></table></figure>
<p>注:最外层循环只需要进行n-1轮迭代。显而易见,n-1轮迭代后A[1…n-1]包含了A中最小的i-1个元素,且已排序,因此A[n]中的元素是最大的,因此A[1…n]已排序。<br><strong>算法复杂度</strong>:<br>时间复杂度:<br>在选择排序算法中的基本操作是比较操作<code>A[j] &lt; A[min]</code>,其执行次数仅仅取决于数组的规模,并不依赖于特定的输入。所以其时间复杂度始终为<strong>$$O(n^2)$$</strong><br>空间复杂度:<strong>$$O(1)$$</strong><br>选择排序算法键的的交换次数为<code><font color="red">n-1</font></code>次(每轮迭代执行一次交换),这个特性使得选择排序优于其它许多时间复杂度也是O(n^2)的排序算法。</p>
<hr>
<h2 id="冒泡排序"><a href="#冒泡排序" class="headerlink" title="冒泡排序"></a>冒泡排序</h2><p><strong>特性</strong>:<font color="red">In-place sort</font>、<font color="red">stable sort</font></p>
<p><img class="aligncenter size-full wp-image-51207" src="http://www.36dsj.com/wp-content/uploads/2016/05/297.gif" alt="冒泡排序" width="280" height="237" data-tag="bdshare" data-bd-imgshare-binded="1"></p>
<p>冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。之所以叫冒泡排序,是因为在相邻元素不断两两交换的过程中,较小的元素会慢慢浮到数列的前端,而每轮迭代,都会将所有未排序元素中最大的元素会沉到数列的尾部,这一过程非常类似于水中的气泡。</p>
<p><strong>算法步骤</strong>:</p>
<p>1)比较相邻的元素。如果需要调整,就交换它们。</p>
<p>2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。</p>
<p>3)针对所有的元素重复以上的步骤,<font color="red">除了最后一个</font>。</p>
<p>4)持续每次对越来越少的元素重复上面的步骤,<font color="red">直到没有任何一对数字需要比较</font>。</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">BubbleSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用冒泡排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> <span class="keyword">for</span> i&lt;- n<span class="number">-2</span> downto <span class="number">0</span> <span class="keyword">do</span></div><div class="line"> <span class="keyword">for</span> j&lt;- <span class="number">0</span> to i <span class="keyword">do</span></div><div class="line"> <span class="keyword">if</span> A[j]&gt;A[j+<span class="number">1</span>]</div><div class="line"> then swap A[j] and A[j+<span class="number">1</span>]</div></pre></td></tr></table></figure>
<p><strong>算法复杂度</strong>:<br>时间复杂度:<br>未改进版本的冒泡排序键值比较次数对于任何输入规模为n的数组都是相同的,但键的交换次数却取决于特定的输入数组。最坏情况下,遇到降序的数组,交换次数与键值比较次数相同。因此我们应该把键值比较作为冒泡排序的基本操作。算法复杂度为<strong>$$O(n^2)$$</strong><br>空间复杂度:<br>和选择排序一样,冒泡排序也不需要额外的辅助空间,其空间复杂度为<strong>$$O(1)$$</strong></p>
<h3 id="冒泡排序法的改进"><a href="#冒泡排序法的改进" class="headerlink" title="冒泡排序法的改进"></a>冒泡排序法的改进</h3><p>在应用蛮力法时常常会遇到这种情况,即经过适度的努力后,我们能够对算法的第一个版本进行一定的改良。让我们回顾前面讲过的选择排序和冒泡排序,分析伪代码,你会发现这两个算法的实现都需要两层循环嵌套,而且它们在每轮迭代中都是彼此独立的,正如一个头脑简单,空有蛮力的人一个劲儿地往终点冲刺,而忽略了沿途的风景,同时也忽略了捷径。简单的冒泡排序正是犯了这样的错误,而没有充分激发出自己的潜能。具体来说吧!在冒泡排序过程中,在某轮迭代中,如果对列表比较一遍后发现没有进行元素交换时,说明列表已经是有序的了,可以终止这个算法了。<br>具体的方式主要有3种(尽管都大同小异,本质上就一个策略,及时终止,不做无用功)。</p>
<p>(1). 加一个标志位flag,当某一趟冒泡排序没有元素交换时,则冒泡结束,元素已经有序,可以有效的减少冒泡次数。</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">BubbleSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用改进冒泡排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> <span class="keyword">for</span> i&lt;- n<span class="number">-2</span> downto <span class="number">0</span> <span class="keyword">do</span></div><div class="line"> <span class="keyword">if</span> flag==<span class="literal">false</span> then <span class="keyword">return</span></div><div class="line"> flag&lt;- <span class="literal">false</span></div><div class="line"> <span class="keyword">for</span> j&lt;- <span class="number">0</span> to i <span class="keyword">do</span></div><div class="line"> <span class="keyword">if</span> A[j]&gt;A[j+<span class="number">1</span>]</div><div class="line"> then swap A[j] and A[j+<span class="number">1</span>]</div><div class="line"> flag&lt;- <span class="literal">true</span></div></pre></td></tr></table></figure>
<p>(2). 保留“犯罪现场”,记录每轮迭代过程中,最后进行交换的元素的下标(<font color="red">该元素之后的所有元素已经是有序的了</font>)。</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">BubbleSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用改进的冒泡排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> lastSwap&lt;- n<span class="number">-2</span></div><div class="line"> <span class="keyword">while</span> lastSwap &gt; <span class="number">0</span> <span class="keyword">do</span></div><div class="line"> <span class="keyword">for</span> j&lt;- <span class="number">0</span> to lastSwap <span class="keyword">do</span></div><div class="line"> <span class="keyword">if</span> A[j]&gt;A[j+<span class="number">1</span>]</div><div class="line"> then swap A[j] and A[j+<span class="number">1</span>]</div><div class="line"> lastSwap&lt;- j<span class="number">-1</span> <span class="comment">//记录最后一次交换的位置</span></div></pre></td></tr></table></figure>
<p>(3). 双向冒泡排序(又称鸡尾酒排序)。该方式从低到高然后从高到低去比较序列里的每个元素。这种方式可以得到比基本冒泡排序稍微好一点的效能(通过分别保存每轮迭代过程,高位置最后一次交换的位置,以及低位置最后一次交换发生的位置)。</p>
<figure class="highlight cpp"><figcaption><span>伪代码</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">BubbleSort(A[<span class="number">0.</span>.n<span class="number">-1</span>])</div><div class="line"> <span class="comment">//功能:用双向冒泡排序对给定的数组进行排序</span></div><div class="line"> <span class="comment">//输入:一个可排序数组A[0..n-1]</span></div><div class="line"> <span class="comment">//输出:升序排列的数组A[0..n-1]</span></div><div class="line"> up&lt;- n<span class="number">-1</span> low&lt;- <span class="number">0</span></div><div class="line"> <span class="keyword">while</span> low&lt;up <span class="keyword">do</span></div><div class="line"> <span class="keyword">for</span> i&lt;- low to up<span class="number">-1</span> <span class="keyword">do</span> <span class="comment">//从低到高扫描</span></div><div class="line"> <span class="keyword">if</span> A[i]&gt;A[i+<span class="number">1</span>]</div><div class="line"> then swap A[i] and A[i+<span class="number">1</span>]</div><div class="line"> up&lt;- i <span class="comment">//记录最后一个交换的位置</span></div><div class="line"></div><div class="line"> <span class="keyword">for</span> i&lt;- up downto low+<span class="number">1</span> <span class="keyword">do</span> <span class="comment">//从高到低扫描</span></div><div class="line"> <span class="keyword">if</span> A[i]&gt;A[i<span class="number">-1</span>]</div><div class="line"> then swap A[i] and A[i<span class="number">-1</span>]</div><div class="line"> low&lt;- i <span class="comment">//记录最后一个交换的位置</span></div></pre></td></tr></table></figure>
<p>以上的这些改进使得在最好情况下(数组已经有序)复杂度可以降到<strong>O(n)</strong>。虽然对于某些输入,可能运行的比较快,但在最坏情况和平均情况下,这些算法仍然属于<strong>O(n^2)</strong>。实际上,即使在初等排序中,冒泡排序法也不是一个好的选择。如果不是它有一个形象而又容易记住的名字。我们可能不会对它有太多了解。但不管怎样,我们刚刚学到的内容是非常重要的!这两种基本的排序算法常常被调侃为算法界的Hello World!在IT企业的面试环节中,经常被HR拿出来考查你是否具备基本的算法基础,为了不被HR鄙视,你懂的!好好理解和掌握它们吧!</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><ul>
<li>蛮力法可以快速地得到一个算法,此算法在一些情况下可以通过适度的努力来提升它的性能。</li>
<li>选择排序和冒泡排序,归结起来就6个字“循环,比较,交换”</li>
<li>选择排序和冒泡排序<u>都属于内部排序</u>,无需额外存储空间;而<u>冒泡排序是稳定排序,选择排序是不稳定的</u>。</li>
</ul>
<p><a href="https://github.com/ZhiXingHeYiApple/Sort_Topics/blob/master/SortTopics/" target="_blank" rel="external"><font color="#FF6100">查看冒泡和选择排序的具体实现代码</font></a></p>
</content>
<summary type="html">
<p><strong><br><a href="http://www.depu.online/2016/07/18/排序专题-一-——选择排序和冒泡排序"><font color="#FF6100">排序专题(一)——选择排序和冒泡排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-二-——插入排序和希尔排序"><font color="#FF6100">排序专题(二)——插入排序和希尔排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-三-——归并排序"><font color="#FF6100">排序专题(三)——归并排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-四-——快速排序"><font color="#FF6100">排序专题(四)——快速排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-五-——堆排序"><font color="#FF6100">排序专题(三)——堆排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-六-——计数排序、桶排序和基数排序"><font color="#FF6100">排序专题(三)——计数排序、桶排序和基数排序</font></a><br><a href="http://www.depu.online/2016/09/02/排序专题-七-——算法总结"><font color="#FF6100">排序专题(七)——算法总结</font></a>
</strong></p>
<blockquote>
<p><strong>把事情做好,常常是浪费时间!&emsp; &emsp;&emsp; &emsp;——罗伯特·伯恩</strong></p>
<p><strong>剪不断,理还乱,是离愁,别是一番滋味在心头。&emsp; &emsp;&emsp; &emsp;——李清照</strong></p>
</blockquote>
<img src="/2016/07/18/排序专题-一-——选择排序和冒泡排序/sort_algorithm01.png" alt="sort_algorithm01.png" title="">
<p>现实世界中,纷繁凌乱的表象常常掩盖事物的规律和本质。为了去掉这层表象,我们不得不对事物重新组织,让其变的有序。在探索的过程中,人们开发出了几十种排序算法,广泛应用于人们的生活和生产实践中。大家过去或多或少都接触或了解过其中的一些算法,如果是这样,请你暂时忘记它们,以初学者的姿态问自己一个问题:“在只看结果,不考虑其他约束的情况下,解决排序问题最直接了当的方法是什么?” 对于这个问题的答案恐怕是智者见智,仁者见仁。也许你并不同意我的观点,但选择排序和冒泡排序绝对是两个有力的候选者。</p>
<hr>
<h2 id="蛮力法"><a href="#蛮力法" class="headerlink" title="蛮力法"></a>蛮力法</h2><p>蛮力法,经常又被称呼为穷举法或暴力拆解法。是一种简单直接地解决问题的方法,常直接基于问题的描述和所涉及的概念定义。<br>尽管“巧妙”与“高效”这两个高大上的词汇与“蛮力法”这个穷屌丝往往搭不上边。但是我们不应该忽略这个穷屌丝(屌丝也有完美逆袭的一天)的存在,其作为一种重要的算法设计策略的地位是无法撼动的,理由如下:</p>
<ul>
<li><strong>对于解决的问题类型更具一般性和普适性。</strong></li>
<li><strong>虽然效率低,但仍可以解决一些小规模的问题实例。</strong></li>
<li><strong>当要解决的问题实例不多或解空间较小时,蛮力法仍可以用能够接受的速度对问题进行求解,而寻找和设计一个更高效算法所花费的代价可能是不值得的。</strong></li>
<li><strong>蛮力法经常被其他一些“高富帅”的算法作为参照标杆。</strong></li>
</ul>
<p>个人认为在排序算法中,选择排序和冒泡排序俨然就是蛮力法的最佳代言人。这也正是我为什么会选择这两个排序算法作为讲解排序专题的切入点。<br>
</summary>
<category term="传统算法" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/"/>
<category term="排序专题" scheme="http://www.depu.online/categories/%E4%BC%A0%E7%BB%9F%E7%AE%97%E6%B3%95/%E6%8E%92%E5%BA%8F%E4%B8%93%E9%A2%98/"/>
<category term="Algorithm" scheme="http://www.depu.online/tags/Algorithm/"/>
<category term="Sort" scheme="http://www.depu.online/tags/Sort/"/>
</entry>
</feed>