Skip to content

Commit fbbd483

Browse files
committed
归并排序
1 parent 77289ef commit fbbd483

File tree

1 file changed

+25
-3
lines changed

1 file changed

+25
-3
lines changed

Algorithm/algorithm-ch.md

+25-3
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function selection(array) {
9595

9696
#### 归并排序
9797

98-
归并排序的原理如下。先通过中间数索引将数组分为左右两部分,然后先排序左边,再排序右边,再将左右排序一次。假设我有一组数组 `[3, 1, 2, 8, 9, 7, 6]`,中间数索引是 3,先排序数组 `[3, 1, 2, 8]` 。在这个左边数组上,继续拆分直到变成数组包含两个元素(如果数组长度是奇数的话,会有一个拆分数组只包含一个元素)。然后排序数组 `[3, 1]``[2, 8]` ,然后再排序数组 `[1, 3, 2, 8]` ,这样左边数组就排序完成,然后按照以上思路排序右边数组,最后将数组 `[1, 2, 3, 8]``[6, 7, 9]` 排序。
98+
归并排序的原理如下。递归的将数组两两分开直到最多包含两个元素,然后将数组排序合并,最终合并为排序好的数组。假设我有一组数组 `[3, 1, 2, 8, 9, 7, 6]`,中间数索引是 3,先排序数组 `[3, 1, 2, 8]` 。在这个左边数组上,继续拆分直到变成数组包含两个元素(如果数组长度是奇数的话,会有一个拆分数组只包含一个元素)。然后排序数组 `[3, 1]``[2, 8]` ,然后再排序数组 `[1, 3, 2, 8]` ,这样左边数组就排序完成,然后按照以上思路排序右边数组,最后将数组 `[1, 2, 3, 8]``[6, 7, 9]` 排序。
9999

100100
<div align="center"><img src="https://user-gold-cdn.xitu.io/2018/4/13/162be13c7e30bd86?w=896&h=1008&f=gif&s=937952" width=500 /></div>
101101

@@ -111,7 +111,10 @@ function sort(array) {
111111
function mergeSort(array, left, right) {
112112
// 左右索引相同说明已经只有一个数
113113
if (left === right) return;
114-
let mid = parseInt((left + right) / 2);
114+
// 等同于 `left + (right - left) / 2`
115+
// 相比 `(left + right) / 2` 来说更加安全,不会溢出
116+
// 使用位运算是因为位运算比四则运算快
117+
let mid = parseInt(left + ((right - left) >> 1));
115118
mergeSort(array, left, mid);
116119
mergeSort(array, mid + 1, right);
117120

@@ -135,6 +138,25 @@ function mergeSort(array, left, right) {
135138
}
136139
```
137140

138-
以上算法使用了递归的思想。递归的本质就是压栈,每递归执行一次函数,就将该函数的信息(比如参数,内部的变量,执行到的行数)压栈,直到遇到终止条件,然后出栈并继续执行函数。对于以上算法来说,第一个递归函数最终会在 `left``right` 都为 0 的情况下终止,然后执行下一个递归函数,传入的索引参数都是 1,所以递归都终止,这时 `left` 为 0 , `mid` 为 0,所以将 `[3, 1]` 排序,这时函数执行完毕并出栈,继续执行以上的操作。
141+
以上算法使用了递归的思想。递归的本质就是压栈,每递归执行一次函数,就将该函数的信息(比如参数,内部的变量,执行到的行数)压栈,直到遇到终止条件,然后出栈并继续执行函数。对于以上递归函数的调用轨迹如下
142+
143+
```js
144+
mergeSort(data, 0, 6) // mid = 3
145+
mergeSort(data, 0, 3) // mid = 1
146+
mergeSort(data, 0, 1) // mid = 0
147+
mergeSort(data, 0, 0) // 遇到终止,回退到上一步
148+
mergeSort(data, 1, 1) // 遇到终止,回退到上一步
149+
// 排序 p1 = 0, p2 = mid + 1 = 1
150+
// 回退到 `mergeSort(data, 0, 3)` 执行下一个递归
151+
mergeSort(2, 3) // mid = 2
152+
mergeSort(3, 3) // 遇到终止,回退到上一步
153+
// 排序 p1 = 2, p2 = mid + 1 = 3
154+
// 回退到 `mergeSort(data, 0, 3)` 执行合并逻辑
155+
// 排序 p1 = 0, p2 = mid + 1 = 2
156+
// 执行完毕回退
157+
// 左边数组排序完毕,右边也是如上轨迹
158+
```
159+
160+
139161

140162
该算法的操作次数是可以这样计算:递归了两次,每次数据量是数组的一半,并且最后把整个数组迭代了一次,所以得出表达式 `2T(N / 2) + T(N)` (T 代表时间,N 代表数据量)。根据该表达式可以套用 [该公式](https://www.wikiwand.com/zh-hans/%E4%B8%BB%E5%AE%9A%E7%90%86) 得出时间复杂度为 `O(N * logN)`

0 commit comments

Comments
 (0)