叽叽喳喳,嘻嘻哈哈

导航

归并排序 —— 递归实现 + 非递归实现

归并排序

  归并排序说的简单一点就是把一个大的序列分成多个子序列,再别对各个子序列进行排序,等所有子序列都排序完成之后,再逐步从所有的子序列里面抽出最小的元素放回到大序列里面。直至所有元素都放回大序列,从而完成排序。

  是采用分治法的典型案例。

  本例是 二路归并。

 

               一个大的序列

 

 

              分成两个子序列分别进行排序                                            

 

 

  如图所示,最终将两个排好序的子序列,合并成一个,完成排序。

 

 

一种是递归的方式:

  该方法是递归的将序列分成两个部分,直至每个部分都只有一个元素,然后在返回,逐个的将相邻部分,排序、合并从而实现总的排序。

 

 代码如下:

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))
View Code

 

 

 

二是 非递归的方式

   递归的方式是从大序列开始,逐步分解,直到最小的只含有单个元素的序列。然后再返回合并。

  而非递归的方式则恰恰相反,它是从单个元素的序列开始,逐步和相邻的部分排序、合并,最终形成一个大的序列。

 

代码如下:

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))
View Code

 

posted on 2016-12-02 20:36  叽叽喳喳,嘻嘻哈哈  阅读(691)  评论(0编辑  收藏  举报