利用python实现插入排序、归并排序、快排和堆排序

data=[12,3,5,6,8,9,13,20,10,100,11,1000,23,999]

#############################

思路是两层循环,外层for处理每个元素(用j遍历,忽略第一个元素,用key指代),内层是while循环,负责为每个元素找到相应的位置,使得每加入一个新的元素后,前面的有序区间仍然有序。做法是当该元素比前面的最后一个元素(即最大值)小时,说明要把它插入到前面某个位置,用i往前找,如果某个i指向的元素仍然比key大,就把该元素往后移一个位置,同时继续往前找,直到找到正确的位置,就退出while循环,然后把key插入到该位置。过程中,j从左往右,i从右往左,且[0,j-1]这个区间是有序的,然后将[j,N-1]的每个数插入到正确的位置。如果查找改为二分查找,而不是从右往左依次查找,就变为二分插入排序了,性能会提高,代码会变稍复杂。此外还可以加入“哨兵”元素,来省略边界的判断。

def insert_sort(data):
    #对数组长度为0、1、2的都成立,不需要额外的边界判断
    for j in range(1, len(data)):
        key = data[j]
        i = j - 1
        while i >=0 and data[i] > key:
            data[i + 1] = data[i]
            i -= 1
        data[i+1]=key
    return data

#############################

Merge方法将A数组的两部分[p,q]、[q,r]合并为一个有序的数组,其中这两部分都是有序的,通过复制到L、R,然后对L、R从左到右遍历,每次取较小者,填回到A数组,实现整体有序。其中若L到达最后了,只需把R一次性复制到A,反之亦然。

def Merge(A,p,q,r):
    #n1=q-p+1 #A[p,q]
    #n2=r-q #A[q+1,r]
    L=A[p:q+1] #即A[p,q]
    R=A[q+1:r+1] #即A[q+1,r]
    i,j=0,0 #分别指向L、R的当前位置
    for k in range(p,r+1):
        if i==len(L):
            A[k:r+1]=R[j:] #L到底,直接把R的剩下给A
            break
        if j==len(R):
            A[k:r+1]=L[i:] #R到底,直接把L的剩下给A
            break
        if L[i]<=R[j]:
            A[k]=L[i]
            i+=1
        else:
            A[k]=R[j]
            j+=1

外层函数,将A划分为[p,q]、[q,r],然后分别调用自己实现各自有序,然后调用Merge实现整体有序;和快排有点类似,只不过快排的PARTITION在递归调用QuickSort之前。核心思想:“将原始序列看成 N 个只包含 1 个元素的有序子序列,然后每次迭代归并两个相邻的有序子序列,直到最后只剩下 1 个有序的序列”;这个方法是自顶向下的,原问题依赖子问题事先解决,划分下去,直到子问题可以直接求解,所以是“先进后出”的,所以是递归的。

def Merge_Sort(A,p,r):
    if p<r:
        q=(p+r)//2
        Merge_Sort(A,p,q)
        Merge_Sort(A,q+1,r)
        Merge(A,p,q,r)

自底向上版本,相对不是很常见;外层while循环控制width,而width始终是从1->2->4->8…不超过数组size;内层while循环每次从index=0开始,以区间长度为2*width遍历数组,对每个区间调用Merge实现有序,即当width为1时,使得每2个元素有序,然后width变为2,又使得每4个元素有序,直到整体有序。

def Merge_Sort_down2top(A):
    width=1
    size=len(A)
    while width<size:
        index=0
        while index<size-width:
            low=index
            high=index+(2*width-1)
            mid=(low+high)//2 # or index+(high-low)//2
            Merge(A,low,mid,high)#
            index += 2 * width
        width*=2

############################

PARTITION作用是将A数组的[p,r]这一段,以最后一个元素(即A[r])为枢纽,将它移动到中间某个位置,(该位置是动态调整一步一步确定的)使得它左边的元素都比它小(内部无序),右边都比它大(内部无序),然后返回这个元素最后所在的下标。整个过程中,i指向比枢纽小的部分的最后一个元素,j指向当前处理了的最后一个元素(同时也是比枢纽大的部分的最后元素)j到枢纽之间是是待处理的元素,所以过程中可把数组看成3或4部分(如果枢纽也单独看成一部分)。下面说具体做法,用j对数组进行遍历,从开头到最后(不包含最后枢纽),如果遇到比枢纽小(或等于),此时有两层含义,但代码一样,一种是i与j之间没有元素(即还没发现比枢纽大的元素),此时i加1后与j重合,(然后交换i与j的元素等于什么也没做)另一种是i与j之间有元素,此时i加1后指向第一个比枢纽大的元素,然后交换i与j的元素,就真正把一个大的和一个小的交换了位置。也就是说j每次都加一,而i只在发现一个比枢纽小的元素时加一,并且一旦发现,就要进行一次交换(无论交换是否有效)。外层函数QuickSort通过对整个数组进行递归(自顶向下)调用PARTITION,就能实现整个数组的排序。

def PARTITION(A,p,r):
    x=A[r]
    i=p-1
    for j in range(p,r):
        if A[j]<=x:
            i+=1
            A[i],A[j]=A[j],A[i]
    A[i+1],A[r]=A[r],A[i+1]
    return i+1

def QuickSort(A,p,r):
    if p<r:
        q=PARTITION(A,p,r)
        QuickSort(A,p,q-1)
        QuickSort(A,q+1,r)

############################

和算法导论书上不同,这里数组下标从0开始,所以left、right多加1,没有用到parent;max_heapify中heap_size后面需要多减1;build_max_heap的range需要多减1;heapsort的几个下标也要多减1;需要在至少两处添加global heap_size

def left(i):
    return 2*i+1

def right(i):
    return 2*i+2

largest记录i、l、r三者中值最大的那个下标;如果largest是i,无论l、r之间谁大,都不需要做任何事了,此时i满足‘最大堆’;(所以这个函数不是让i为根的整个树都满足‘最大堆’,而仅仅是让i这个节点满足;)如果largest不是i,就交换i和largest节点的值,然后对largest节点调用max_heapify自己;因为对largest来说,值变小了,可能会改变该节点的‘最大堆’性质。

def max_heapify(A,i):
    l=left(i)
    r=right(i)
    global heap_size
    if l<=heap_size-1 and A[l]>A[i]:
        largest=l
    else:
        largest = i
    if r<=heap_size-1 and A[r]>A[largest]:
        largest=r
    if largest!=i:
        A[i],A[largest]=A[largest],A[i]
        max_heapify(A,largest)

从非叶子节点往左遍历,对每个节点执行max_heapify,因为叶子节点一定满足‘最大堆’性质;这样就能使得每个节点都满足‘最大堆’性质,即整个树是一个‘最大堆’。

def build_max_heap(A):
    global heap_size
    heap_size = len(A)
    for i in range(len(A)//2-1, -1,-1):
        max_heapify(A,i)

首先调用build_max_heap构造‘最大堆’;然后对数组从后往前遍历,取每个元素A[j],与第一个元素交换,即把当前堆的最大值(根节点)赋值到A[j],把A[j]赋值到A[0],然后将全局变量heap_size减一,再对第一个元素做堆调整,调用max_heapify,最后A数组就被从小到大排序了;可以看到是原地的,且最大堆对应的是升序,当然如果每次取最大值不放到最后,而是直接打印就变成降序排序了。

def heapsort(A):
    build_max_heap(A)
    for j in range(len(A)-1,0,-1):
        A[0],A[j]=A[j],A[0]
        global heap_size
        heap_size=heap_size-1
        max_heapify(A,0)

############################

#data=insert_sort(data)

#Merge_Sort(data,0,len(data)-1)

#Merge_Sort_down2top(data)

#QuickSort(data,0,len(data)-1)

heapsort(data)

print(data)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值