zoukankan      html  css  js  c++  java
  • 归并排序 —— 递归实现 + 非递归实现

    归并排序

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

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

      本例是 二路归并。

                   一个大的序列

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

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

    一种是递归的方式:

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

     代码如下:

    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
  • 相关阅读:
    街边的泥人张
    没事试试50mm1.4
    WWOOF介绍一下,但貌似我们利用不了
    护照和签证
    071205还是晴photo
    忘年旅行
    12月6日大雪
    12月5日
    周六
    071204 晴
  • 原文地址:https://www.cnblogs.com/jijizhazha/p/6127274.html
Copyright © 2011-2022 走看看