归并排序(MergeSort)详解(附动画代码)

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/To_be_to_thought/article/details/83988767

归并排序算法思想:将数组不断二分得到子数组,知道子数组长度为1(自然是排序好的),对左子数组和右子数组分别排序,

子数组长度为1,2,4....,

子数组排序使用了一个辅助数组

def exchange(arr,i,j):
    temp=arr[i]
    arr[i]=arr[j]
    arr[j]=temp

#归并需要一个额外数组存放归并前的子数组排序状态
def merge(arr,start,mid,end):
    i=start
    j=mid+1
    aux[start:end+1]=arr[start:end+1]
    for idx in range(start,end+1):
        if i>mid:
            arr[idx] = aux[j]
            j += 1
        elif j > end:
            arr[idx] = aux[i]
            i += 1
        elif aux[j] < aux[i]:
            arr[idx] = aux[j]
            j += 1
        else:
            arr[idx] = aux[i]
            i += 1
        record.append(arr.copy())#动画代码

对于i,j下标指针的边界条件判断和他们对应的数组值的比较是这段代码的关键!

下面是递归二分(自顶向下然后再回溯)的过程:

先计算子数组中间元素索引,再对当前子数组的左子数组进行排序,再对当前子数组的右子数组进行排序,最后将两个排序子数组归并。

def sortUB(arr,start,end):
    if start>=end:
        return
    mid=start+(end-start)//2
    sortUB(arr,start,mid)
    sortUB(arr,mid+1,end)
    merge(arr,start,mid,end)

时间复杂度:O(nlogn)

空间复杂度:O(n)

下面看一个非常tricky的自底向上的归并排序,就是利用数组的索引的变化规律来重排序:

对于特定长度(size)的子数组,第一个元素和最后一个元素的索引为:idx,idx+2*size+1

def sortBU(arr):
    n = len(arr)
    size = 1
    while size < n:
        idx = 0
        while idx < n - size:
            start=idx
            mid=idx + size - 1
            end=min(idx + 2 * size - 1,n-1)
            merge(arr,start,mid,end)
            idx += 2*size
        size *= 2

索引变化如下图:

  

动画代码如下:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.path as path
import matplotlib.animation as animation

record=[]
n=100
data=np.random.randint(0,100,n)
record.append(data.copy())
aux=data.copy()
sortBU(data)
bins=np.arange(n+1)
left = np.array(bins[:-1])
right = np.array(bins[1:])
bottom = np.zeros(len(left))
top = bottom + data
nrects = len(left)

# here comes the tricky part -- we have to set up the vertex and path
# codes arrays using moveto, lineto and closepoly

# for each rect: 1 for the MOVETO, 3 for the LINETO, 1 for the
# CLOSEPOLY; the vert for the closepoly is ignored but we still need
# it to keep the codes aligned with the vertices
nverts = nrects*(1 + 3 + 1)
verts = np.zeros((nverts, 2))
codes = np.ones(nverts, int) * path.Path.LINETO
codes[0::5] = path.Path.MOVETO
codes[4::5] = path.Path.CLOSEPOLY
verts[0::5, 0] = left
verts[0::5, 1] = bottom
verts[1::5, 0] = left
verts[1::5, 1] = top
verts[2::5, 0] = right
verts[2::5, 1] = top
verts[3::5, 0] = right
verts[3::5, 1] = bottom

fig, ax = plt.subplots()
barpath = path.Path(verts, codes)
patch = patches.PathPatch(
    barpath, facecolor='green', edgecolor='yellow', alpha=0.5)
ax.add_patch(patch)
ax.set_xlim(left[0], right[-1])
ax.set_ylim(bottom.min(), top.max())

def animate(i):
    # simulate new data coming in
    top = bottom + record[i]
    verts[1::5, 1] = top
    verts[2::5, 1] = top
    return [patch, ]

ani = animation.FuncAnimation(fig, animate, len(record), interval=10,repeat=False, blit=True)
plt.show()

 

展开阅读全文

没有更多推荐了,返回首页