python归并排序--递归实现
归并排序:
归并排序(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
分治法:
字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。
分治法的设计思想是:
将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。
分治法的基本步骤
分治法在每一层递归上都有三个步骤:
step1 分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;
step2 解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题
step3 合并:将各个子问题的解合并为原问题的解。
依据分治法设计程序时的思维过程
实际上就是类似于数学归纳法,找到解决本问题的求解方程公式,然后根据方程公式设计递归程序。
1. 一定是先找到最小问题规模时的求解方法
2. 然后考虑随着问题规模增大时的求解方法
3. 找到求解的递归函数式后(各种规模或因子),设计递归程序即可
现在我们来看一下归并排序:
seq = [5,3,2,0,1,4]
- 首先我们来找最小规模时的求解方法:
当列表中只有一个元素的时候,就是最小规模。
如:
left = [5], right = [3]
将两个列表合并成一个有序的列表:
result = []
if left[0] <= right[0]:
result.append(left[0])
else:
result.append(right[0])
result += left
此时 合并后的列表 result = [3,5]就是排序好的列表。 - 现在我们将问题的规模扩大。
left = [0,3,5] right = [1,4]
将两个有序列表,合并成一个有序列表:
比较二个列表的第一个数,谁小就先取谁,取了后就在对应的列表中跳过这个数。然后再进行比较,如果有如果为空,那直接将另一个列表的数据依次取出即可:
result = []
i = 0 #left列表的下标
j = 0 #right列表的下标
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result = left[i:] #将剩余的元素合并到新的列表中
result = right[j:] #将剩余的元素合并到新的列表中。
此时 合并后的列表 result = [0,1,3,4,5]就是排序好的列表。 - 设计递归,将复杂的问题分解为最小规模子问题。
- 将列表分解为 两个更小的列表。
- 递归分解,将更小的列表继续分解,直到达到最小规模,也就是只有一个元素的时候。
- 对已经排序好的列表 进行合并。单个元素的列表,认为是已经排序好的。
最小规模,列表只有一个元素的时候。
if len(seq) <= 1:
return seq
mid = len(seq)/2 #将列表分成更小的两个列表
分别对左右两个列表进行递归分解
left = mergesort(seq[:mid])
right = mergesort(seq[mid:])
对排序好的两个列表合并,产生一个新的排序好的列表
return merge(left,right)
完整代码如下:
def mergesort(seq):
"""归并排序"""
if len(seq) <= 1:
return seq
mid = len(seq) / 2 # 将列表分成更小的两个列表
# 分别对左右两个列表进行处理,分别返回两个排序好的列表
left = mergesort(seq[:mid])
right = mergesort(seq[mid:])
# 对排序好的两个列表合并,产生一个新的排序好的列表
return merge(left, right)
def merge(left, right):
"""合并两个已排序好的列表,产生一个新的已排序好的列表"""
result = [] # 新的已排序好的列表
i = 0 # 下标
j = 0
# 对两个列表中的元素 两两对比。
# 将最小的元素,放到result中,并对当前列表下标加1
while i < len(left) and j < len(right):
if left[i] <= right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result += left[i:]
result += right[j:]
return result
seq = [5,3,0,6,1,4]
print '排序前:',seq
result = mergesort(seq)
print '排序后:',result
结果如下:
耗时:
import random
start_time = time.time()
seq = random.sample(range(10000),10000)
result = mergesort(seq)
print '耗时---',time.time()-start_time```
结果如下:
![image.png](http://upload-images.jianshu.io/upload_images/4131789-c9ba875ae4bf1e78.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
和 冒泡,插入,选择排序比起来 是相当快的。
最后编辑于
:2017-12-05 18:57:48
©著作权归作者所有,转载或内容合作请联系作者