归并排序 —— 递归实现 + 非递归实现
归并排序
归并排序说的简单一点就是把一个大的序列分成多个子序列,再别对各个子序列进行排序,等所有子序列都排序完成之后,再逐步从所有的子序列里面抽出最小的元素放回到大序列里面。直至所有元素都放回大序列,从而完成排序。
是采用分治法的典型案例。
本例是 二路归并。
一个大的序列
分成两个子序列分别进行排序
如图所示,最终将两个排好序的子序列,合并成一个,完成排序。
一种是递归的方式:
该方法是递归的将序列分成两个部分,直至每个部分都只有一个元素,然后在返回,逐个的将相邻部分,排序、合并从而实现总的排序。
代码如下:
def merge(left, right): """合并左右两个子序列""" merged = [] # 设置一个空序列用以盛放每次从子序列中拿出的小的元素 i, j = 0, 0 #初始化 i j 两个游标,分别跟踪 左右两个子序列的最小元素,因为左右两个序列已经从小到大排序好了,所以i j 均为 0 left_len, right_len = len(left), len(right) while i < left_len and j < right_len: # i j 作为左右子序列的游标,他们是不能超过两个子序列的长度的。一旦超过,说明该子序列遍历完成 if left[i] <= right[j]: #分别比较两个子序列中最小的那个。即 i j分别指向的那两个元素 merged.append(left[i]) #小的加入到 临时序列,同时 游标向右移动 i += 1 else: merged.append(right[j]) j += 1 merged.extend(left[i:]) #当上个循环退出的时候,说明其中一个子序列已经遍历完成,于是将另一个子序列的剩余所有元素放入到临时序列中 merged.extend(right[j:]) #此中情况是 i 坐在的左序列提前遍历完了,于是右子序列的从j开始的所有剩余元素都放到临时序列中 return merged #返回临时序列 def merge_sort(lst): if len(lst) <= 1: #如果序列的长度为 1 了,那么就返回该序列 return lst middle = len(lst) // 2 left = merge_sort(lst[:middle]) #递归的对序列的左半部分进行分解 right = merge_sort(lst[middle:]) #递归的对序列的右半部分进行分解 return merge(left, right) #合并左右两个子序列。 a = [1,4,7,3,5,89,64,32,46] print(merge_sort(a))
二是 非递归的方式
递归的方式是从大序列开始,逐步分解,直到最小的只含有单个元素的序列。然后再返回合并。
而非递归的方式则恰恰相反,它是从单个元素的序列开始,逐步和相邻的部分排序、合并,最终形成一个大的序列。
代码如下:
def merger(A,left,mid,right): #取一个序列的三个关键索引,mid 为界,将整个序列分成左右两个部分。 '''每一组排序合并 放在本例中就是每两个相邻部分进行排序合并 ''' i = left #left 为做左半部分序列的头 ,左半部分序列的 尾为 mid j = mid +1 # mid+1 即为右半部分序列的 头,right 为右半部分序列的尾。 temp = [] # 设置一个临时序列,用以盛放 每一趟排好序的元素 while i<=mid and j<=right: # i, j 分别为左右两个部分的游标,不能越界 if A[i]<A[j]: #分别将左右序列小的部分加入临时序列。 temp.append(A[i]) i += 1 else: temp.append(A[j]) j += 1 while i<=mid: # j 先遍历完了,将左半部分从 i 以后的所有元素都放入到序列中 temp.append(A[i]) i += 1 while j <=right: # i 先遍历完了,将左半部分从 j 以后的所有元素都放入到序列中 temp.append(A[j]) j += 1 A[left:right+1]=temp # 将临时序列中排好序的元素放回到A 中 .注意为什么是right+1 ,是因为,python列表不包含右边界元素,而我们又需要包含右边界 return A def mergepass(A,length,n): '''每一趟排序合并,放在本例中就是对每一行排序 合并''' i = 0 while i+2*length-1<n: # 此处是指每一组的最右部分是否存在,如果存在进行,如果不存在就是5 那种情况,只有左部分,没有右部分 merger(A,i,i+length-1,i+2*length-1) #每一组排序合并的函数。 i = i+2*length # 每一趟排序合并之后,需要对已经合并好的序列的相邻一组元素进行排序合并 if i+length-1<n: # 这个是针对上图中 5 那种只有左部分没有右部分,无法排序合并 的情况 定义的。 merger(A,i,i+length-1,n-1) return A def mergerSort(A,n): '''归并排序''' length = 1 #length为步长,即每次排序合并的元素个数 while length<n: mergepass(A,length,n) #调用每一趟排序的函数 length = 2* length # 步长是成 2 倍 扩大的。 return A A = [1,4,7,3,5,89,64,32,46] n =len(A) print(mergerSort(A,n))