Skip to content

Commit

Permalink
归并排序内容优化
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Aug 16, 2023
1 parent 2465a35 commit 3fca4de
Showing 1 changed file with 32 additions and 35 deletions.
67 changes: 32 additions & 35 deletions Contents/01.Array/02.Array-Sort/05.Array-Merge-Sort.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,52 +2,44 @@

> **归并排序(Merge Sort)基本思想**
>
> 采用经典的分治策略,先递归地将当前序列平均分成两半。然后将有序序列两两合并,最终合并成一个有序序列
> 采用经典的分治策略,先递归地将当前数组平均分成两半。然后将有序数组两两合并,最终合并成一个有序数组
## 2. 归并排序算法步骤

1. **分割过程**先递归地将当前序列平均分成两半,直到子序列长度为 `1`
1. 找到序列中心位置 `mid`,从中心位置将序列分成左右两个子序列 `left_arr``right_arr`
2. 对左右两个子序列 `left_arr``right_arr` 分别进行递归分割。
3. 最终将数组分割为 `n` 个长度均为 `1` 的有序子序列
2. **归并过程**:从长度为 `1` 的有序子序列开始,依次进行两两归并,直到合并成一个长度为 `n` 的有序序列
1. 使用数组变量 `arr` 存放归并后的有序数组。
2. 使用两个指针 `left_i``right_i` 分别指向两个有序子序列 `left_arr``right_arr` 的开始位置。
3. 比较两个指针指向的元素,将两个有序子序列中较小元素依次存入到结果数组 `arr` 中,并将指针移动到下一位置。
4. 重复步骤 `3`,直到某一指针到达子序列末尾
5. 将另一个子序列中的剩余元素存入到结果数组 `arr` 中。
6. 返回归并后的有序数组 `arr`
1. **分割过程**先递归地将当前数组平均分成两半,直到子数组长度为 $1$
1. 找到数组中心位置 $mid$,从中心位置将数组分成左右两个子数组 $left\underline{}arr$、$right\underline{}arr$
2. 对左右两个子数组 $left\underline{}arr$、$right\underline{}arr$ 分别进行递归分割。
3. 最终将数组分割为 $n$ 个长度均为 $1$ 的有序子数组
2. **归并过程**:从长度为 $1$ 的有序子数组开始,依次进行两两归并,直到合并成一个长度为 $n$ 的有序数组
1. 使用数组变量 $arr$ 存放归并后的有序数组。
2. 使用两个指针 $left\underline{}i$、$right\underline{}i$ 分别指向两个有序子数组 $left\underline{}arr$、$right\underline{}arr$ 的开始位置。
3. 比较两个指针指向的元素,将两个有序子数组中较小元素依次存入到结果数组 $arr$ 中,并将指针移动到下一位置。
4. 重复步骤 $3$,直到某一指针到达子数组末尾
5. 将另一个子数组中的剩余元素存入到结果数组 $arr$ 中。
6. 返回归并后的有序数组 $arr$

## 3. 归并排序动画演示

![](https://qcdn.itcharge.cn/images/20220816161220.gif)

1. 初始序列为 `[6, 2, 1, 3, 7, 5, 4, 8]`
2. 将序列分解为 `[6, 2, 1, 3]``[7, 5, 4, 8]`
3. 将序列分解为 `[6, 2]``[1, 3]``[7, 5]``[4, 8]`
4. 将序列分为为 `[6]``[2]``[1]``[3]``[7]``[5]``[4]``[8]`
5. 将序列看做是 `8` 个长度为 `1` 的子序列,即 `[6]``[2]``[1]``[3]``[7]``[5]``[4]``[8]`
6.`1` 趟排序:将子序列中的有序子序列两两归并,归并后的子序列为`[2, 6]``[1, 3]``[5, 7]``[4, 8]`
7.`2` 趟排序:将子序列中的有序子序列两两归并,归并后的子序列为`[1, 2, 3, 6]``[4, 5, 7, 8]`
8.`3` 趟排序:将子序列中的有序子序列两两归并,归并后的子序列为`[1, 2, 3, 4, 5, 6, 7, 8]`。得到长度为 `n` 的有序序列,排序结束。
1. 初始数组为 `[6, 2, 1, 3, 7, 5, 4, 8]`
2. 将数组分解为 `[6, 2, 1, 3]``[7, 5, 4, 8]`
3. 将数组分解为 `[6, 2]``[1, 3]``[7, 5]``[4, 8]`
4. 将数组分为为 `[6]``[2]``[1]``[3]``[7]``[5]``[4]``[8]`
5. 将数组看做是 `8` 个长度为 `1` 的子数组,即 `[6]``[2]``[1]``[3]``[7]``[5]``[4]``[8]`
6.`1` 趟排序:将子数组中的有序子数组两两归并,归并后的子数组为`[2, 6]``[1, 3]``[5, 7]``[4, 8]`
7.`2` 趟排序:将子数组中的有序子数组两两归并,归并后的子数组为`[1, 2, 3, 6]``[4, 5, 7, 8]`
8.`3` 趟排序:将子数组中的有序子数组两两归并,归并后的子数组为`[1, 2, 3, 4, 5, 6, 7, 8]`。得到长度为 `n` 的有序数组,排序结束。

## 4. 归并排序算法分析

- **时间复杂度**:$O(n \times \log_2n)$。归并排序算法的时间复杂度等于归并趟数与每一趟归并的时间复杂度乘积。子算法 `merge(left_arr, right_arr):` 的时间复杂度是 $O(n)$,因此,归并排序算法总的时间复杂度为 $O(n \times \log_2 n)$。
- **空间复杂度**:$O(n)$。归并排序方法需要用到与参加排序的序列同样大小的辅助空间。因此算法的空间复杂度为 $O(n)$。
- **排序稳定性**:归并排序算法是一种 **稳定排序算法**
- 因为在两个有序子序列的归并过程中,如果两个有序序列中出现相同元素,`merge(left_arr, right_arr):` 算法能够使前一个序列中那个相同元素先被复制,从而确保这两个元素的相对次序不发生改变。


## 5. 归并排序代码实现
## 4. 归并排序代码实现

```python
class Solution:
def merge(self, left_arr, right_arr): # 归并过程
arr = []
left_i, right_i = 0, 0
while left_i < len(left_arr) and right_i < len(right_arr):
# 将两个有序子序列中较小元素依次插入到结果数组中
# 将两个有序子数组中较小元素依次插入到结果数组中
if left_arr[left_i] < right_arr[right_i]:
arr.append(left_arr[left_i])
left_i += 1
Expand All @@ -56,12 +48,12 @@ class Solution:
right_i += 1

while left_i < len(left_arr):
# 如果左子序列有剩余元素,则将其插入到结果数组中
# 如果左子数组有剩余元素,则将其插入到结果数组中
arr.append(left_arr[left_i])
left_i += 1

while right_i < len(right_arr):
# 如果右子序列有剩余元素,则将其插入到结果数组中
# 如果右子数组有剩余元素,则将其插入到结果数组中
arr.append(right_arr[right_i])
right_i += 1

Expand All @@ -72,11 +64,16 @@ class Solution:
return arr

mid = len(arr) // 2 # 将数组从中间位置分为左右两个数组。
left_arr = self.mergeSort(arr[0: mid]) # 递归将左子序列进行分割和排序
right_arr = self.mergeSort(arr[mid:]) # 递归将右子序列进行分割和排序
return self.merge(left_arr, right_arr) # 把当前序列组中有序子序列逐层向上,进行两两合并。
left_arr = self.mergeSort(arr[0: mid]) # 递归将左子数组进行分割和排序
right_arr = self.mergeSort(arr[mid:]) # 递归将右子数组进行分割和排序
return self.merge(left_arr, right_arr) # 把当前数组组中有序子数组逐层向上,进行两两合并。

def sortArray(self, nums: List[int]) -> List[int]:
return self.mergeSort(nums)
```

## 5. 归并排序算法分析

- **时间复杂度**:$O(n \times \log n)$。归并排序算法的时间复杂度等于归并趟数与每一趟归并的时间复杂度乘积。子算法 `merge(left_arr, right_arr):` 的时间复杂度是 $O(n)$,因此,归并排序算法总的时间复杂度为 $O(n \times \log n)$。
- **空间复杂度**:$O(n)$。归并排序方法需要用到与参加排序的数组同样大小的辅助空间。因此,算法的空间复杂度为 $O(n)$。
- **排序稳定性**:因为在两个有序子数组的归并过程中,如果两个有序数组中出现相同元素,`merge(left_arr, right_arr):` 算法能够使前一个数组中那个相同元素先被复制,从而确保这两个元素的相对次序不发生改变。因此,归并排序算法是一种 **稳定排序算法**

0 comments on commit 3fca4de

Please sign in to comment.