1/ Что такое сортировка слиянием?
归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法,是一种内排序(在内存中完成)。
该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,
因为归并排序始终都是 O(nlogn) 的时间复杂度,代价是需要额外的内存空间。
也就是说:用空间换时间。
2/ Что такое разделяй и властвуй
顾名思义:分而治之。
**就是把一个大问题分解成相似的小问题,通过解决这些小问题,最后用小问题的解构造大问题的解。**
听起来是不是和之前讲递归的时候很像?
没错,**分治法基本都是可以用递归来实现的**。
在之前,我们没有加以区分递归和分治法,当然现在我也认为不需要加以区分,
但你如果非要问它们之间是什么区别,我的理解是:
- **递归是一种编程技巧**,一个函数自己调用自己就是递归;
- **分治法是一种解决问题的思想**:
- 把大的问题分解成小问题的这个过程就叫“分”,
- 解决小问题的过程就叫“治”,
- 解决小问题的方法往往是递归。
**所以分治法的三大步骤是:**
「分」:大问题分解成小问题;
「治」:用同样的方法解决这些小问题;
「合」:用小问题的解构造大问题的解。
**那回到我们的归并排序上来:**
分」:把一个数组拆成两个;
「治」:用归并排序去排这两个小数组;
「合」:把两个排好序的小数组合并成大数组。
这里还有个问题,就是什么时候能够解决小问题了?也就是什么时候拆分完毕了?
答:当只剩一个元素的时候,直接返回就好了,分解不了了。
这就是递归的 base case,是要直接给出答案的。
3/шаги алгоритма
归并排序使用**分而治之**的概念对给定的元素列表进行排序。
它将问题分解为较小的子问题,直到它们变得足够简单以至可以直接解决为止。
以下是归并排序的步骤:
1.将给定的列表分为两半(如果列表中的元素数为奇数,则使其大致相等)。
2.以相同的方式继续划分子数组,直到只剩下单个元素数组。
3.从单个元素数组开始,**合并**子数组,以便对每个合并的子数组进行排序。
4.重复第 3 步单元,直到最后得到一个排好序的数组。
4/пример
待排序列表为[5,2,1,0]
Step1:
先拆成两半,
分成两个数组:{5, 2} 和 {1, 0}
Step2.
没到 base case,所以继续把大问题分解成小问题
当然了,虽然左右两边的拆分我都叫它 Step2,但是它们并不是同时发生的
Step3.
这一层都是一个元素了,是 base case,可以返回并合并了。
Step4.
合并的过程就是按大小顺序来排好,这里借助`两个指针`来比较,以及一个**额外的数组**来辅助完成。
比如在最后一步时,数组已经变成了:{2, 5, 0, 1},
那么通过两个指针 i 和 j,比较指针所指向元素的大小,把小的那个放到一个新的数组?里,然后指针相应的向右移动。
5/код
import random
temp_list = []
for i in range(20):
temp_list.append(random.randint(1,100))
def merge_sort(temp_list):
n = len(temp_list)
if n <= 1:
return temp_list
# 使用二分法进行拆分
mid = n // 2
left = temp_list[:mid]
right = temp_list[mid:]
# 函数自己调用自己,属于递归
return f( merge_sort(left), merge_sort(right) )
def f(left,right):
# 排序合并两个数列
final_list = [] # 定义一个额外的列表,来存放最后的结果
# 两个数列都有值
while len(left) > 0 and len(right) > 0:
# 左右两个数列第一个最小放前面
if left[0] <= right[0]:
# 这里用的是pop()
# 因为不仅要把元素加入到final_list中,还要从left中删除掉
final_list.append( left.pop(0) )
else:
final_list.append( right.pop(0) )
# 只有一个数列中还有值,直接添加
# 把剩下的元素直接添加到result_list的末尾
final_list += left
final_list += right
return final_list
print(merge_sort(arr))