任务描述
本关任务:编写代码实现归并排序。
相关知识
为了完成本关任务,你需要掌握: 1.如何实现归并排序; 2.归并排序算法的分析。
归并排序
分而治之的策略可以用来改进排序算法,归并排序就是分治策略在排序算法上的典型应用。归并排序是一种递归算法,其算法思路是将列表持续地拆分为两半,再对这两半分别进行归并排序。
图 1 展示了对一个含有 9 个数据项的列表进行归并排序的拆分过程。
图1 归并排序的列表拆分过程
如果列表里的数据项个数超过一个,就把列表拆分,然后再分别对拆分后的两个部分递归进行排序。图 1 中的列表首先被拆分为 [54,26,93,17] 和 [77,31,44,55,20] 左右两部分。然后对左右两部分分别再进行拆分,左部分继续被拆分为 [54,26] 和 [93,17]。[54,26] 再被拆分为 [54] 和 [26],当列表中的数据项只有一个时,就结束这个递归过程。
一旦拆分成的两个部分被排序好,就执行归并操作。图 2 展示了对以上列表进行归并排序时,已拆分的列表被排序好后再被重新归并回去的过程。
图2 归并排序的列表归并过程
归并的过程是把两个排好序的列表结合在一起,组合成一个单一的、有序的新列表。如图 2 最后一步归并是将已排好序的两个列表 [17,26,54,93] 和 [20,31,44,55,77] 组合成一个新的有序列表。归并的过程首先将第一个列表中最小的 17 和第二个列表中最小的 20 进行比较,17 小于 20,于是将 17 放置在一个新的结果列表中。然后将第一个列表中当前最小的 26 与第二个列表中当前最小的 20 进行比较,26 大于 20,于是将 20 放置在结果列表中。以此类推,直至两个列表的所有数据项都按从小到大的顺序放置在结果列表中。
归并排序的算法实现可总结为以下几个关键点:
-
递归的基本结束条件:列表中仅有 1 个数据项时,自然是排好序的;
-
缩小规模:将列表拆分为两半,规模减为原来的二分之一;
-
调用自身:对分为两半的列表分别调用自身来排序,然后将分别排好序的两半进行归并,得到最终排好序的整个列表。
归并排序的算法分析
我们可以将归并排序分为两个过程来分析:
-
拆分的过程,借鉴二分查找中的分析结果,是对数复杂度,其时间复杂度为
O(logn)
; -
归并的过程,对于拆分的每个部分,其所有数据项都会被比较和放置一次,所以是线性复杂度,其时间复杂度是
O(n)
。
综合考虑,每次拆分的部分都进行一次O(n)
的数据项归并,因此总的时间复杂度是O(nlogn)
。此外,归并排序算法使用了额外 1 倍的存储空间用于归并,因此空间复杂度为O(n)
。
编程要求
在右侧编辑器中的 Begin-End 区间补充代码,根据归并排序的算法思想完成mergeSort
方法,从而实现对无序表的排序。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确的数值,只有所有数据全部计算正确才能通过测试:
测试输入:
54,26,93,17,77,31,44,55,20
输入说明:输入为需要对其进行排序的无序表。
预期输出:
[17, 20, 26, 31, 44, 54, 55, 77, 93]
输出说明:输出的是对无序表进行归并排序后的结果,以列表的形式展现。
测试输入:
49,38,65,97,76,13,27
预期输出:
[13, 27, 38, 49, 65, 76, 97]
开始你的任务吧,祝你成功!
'''请在Begin-End之间补充代码, 完成mergeSort函数'''
def mergeSort(alist):
if len(alist)>1: # 基本结束条件
mid = len(alist)//2
lefthalf = alist[:mid]
righthalf = alist[mid:]
# 递归调用mergeSort来对切片得到左半部分和右半部分进行排序
# ********** Begin ********** #
mergeSort(lefthalf)
mergeSort(righthalf)
# ********** End ********** #
i=0
j=0
k=0
# 此时左半部分和右半部分都已排好序,接下来需要对排好序的左右两半进行合并
while i<len(lefthalf) and j<len(righthalf):
# 拉链式交错把左右半部分从小到大归并到结果列表中
if lefthalf[i]<righthalf[j]:
alist[k]=lefthalf[i]
i=i+1
else:
# ********** Begin ********** #
alist[k]=righthalf[j]
j+=1
# ********** End ********** #
k=k+1
# 归并左半部分的剩余项
while i<len(lefthalf):
alist[k]=lefthalf[i]
i=i+1
k=k+1
# 归并右半部分的剩余项
while j<len(righthalf):
# ********** Begin ********** #
alist[k]=righthalf[j]
j+=1
k+=1
# ********** End ********** #
第2关:归并排序的另一个实现
任务描述
本关任务:编写更具 Python 风格的归并排序代码。
相关知识
为了完成本关任务,你需要掌握:Pyhon 列表 List 中的一些方法。
为了让代码更简洁,具有更优异的可读性,可以利用 Python 中列表 List 的方法来实现更具 Python 风格的归并排序代码。
-
list.append():在列表末尾添加新的对象。 该方法的语法为:
list.append(obj) # obj 表示添加到列表末尾的对象
示例如下:
aList = [123, 'x', 'y', 'z']
aList.append(456)
print(aList)
输出:
[123, 'x', 'y', 'z', 456]
- list.extend():在列表末尾一次性追加另一个序列中的多个值(用新列表扩展原来的列表)。 该方法的语法为:
list.extend(seq) # seq 表示一个元素列表
示例如下:
aList = [123, 'x', 'y', 'z', 456]
bList = [789, 'a']
aList.extend(bList)
print(aList)
输出:
[123, 'x', 'y', 'z', 456, 789, 'a']
- list.pop():移除列表中的一个元素(默认最后一个元素),并且返回该元素的值。 该方法的语法为:
list.pop(index=-1) # index 为可选参数,表示要移除列表元素的索引值,默认为删除最后一个列表值
示例如下:
aList = [123, 'x', 'y', 'z', 456]
list_pop=aList.pop(0)
print("删除的项为 :", list_pop)
print("列表现在为 :", aList)
输出:
删除的项为 : 123
列表现在为 : ['x', 'y', 'z', 456]
编程要求
根据提示,在右侧编辑器中的 Begin-End 区间补充代码,根据归并排序的算法思想完成merge_sort
方法,从而实现对无序表的排序。
测试说明
平台会对你编写的代码进行测试,比对你输出的数值与实际正确的数值,只有所有数据全部计算正确才能通过测试:
测试输入:
54,26,93,17,77,31,44,55,20
输入说明:输入为需要对其进行排序的无序表。
预期输出:
[17, 20, 26, 31, 44, 54, 55, 77, 93]
输出说明:输出的是对无序表进行归并排序后的结果,以列表的形式展现。
测试输入:
49,38,65,97,76,13,27
预期输出:
[13, 27, 38, 49, 65, 76, 97]
提示:
i = [1, 2, 3]
j = []
print(i if i else j)
输出:
[1, 2, 3]
开始你的任务吧,祝你成功!
'''请在Begin-End之间补充代码, 完成merge_sort函数'''
# python风格的归并排序
def merge_sort(lst):
# 递归结束条件
if len(lst) <= 1:
return lst
# 分解问题,并递归调用
middle = len(lst) // 2
# 分别对左半部分和右半部分进行归并排序
# ********** Begin ********** #
left=merge_sort(lst[:middle])
right=merge_sort(lst[middle:])
# ********** End ********** #
# 合并左右半部,完成排序
merged = []
# 只要左右部分还有数据,就进行合并
# 将左半部分的首个数据(位置为0的数据)与右半部分的首个数据进行比较
# 把更小的数据添加到merged列表中,同时把它从原来所在列表中删除(下一次比较还是从0位置开始)
while left and right:
if left[0] <= right[0]:
# ********** Begin ********** #
merged.append(left.pop(0))
# ********** End ********** #
else:
merged.append(right.pop(0))
# 合并未合并的部分
# ********** Begin ********** #
merged.extend(right if right else left)
# ********** End ********** #
# 返回结果
return merged