算法——归并和归并排序

一、归并

  假设现在的列表分两段有序,如何将其合成为一个有序列表。这种操作称为一次归并。

1、归并过程图示

  当一个列表两段有序合并为一个有序列表的一次归并的过程如下:

  将列表分为两段,两个箭头分别指向每段的第一个:

  

  比较两段中最小的数2和1,将最小的那个值,箭头后移:

  

  接着比较两段中最小的数,将2取出,箭头后移,以此类推:

  

 

2、归并代码实现

def merge(li, low, mid, high):
    """
    归并过程
    :param li:列表
    :param low:第一段第一个元素
    :param mid:第一段最后一个元素
    :param high:第二段最后一个元素
    :return:
    """
    i = low
    j = mid + 1   # 第二段第一个元素
    ltmp = []  # 新列表
    while i <= mid and j<= high:    # 只要左右两边都有数
        if li[i] < li[j]:
            ltmp.append(li[i])
            i += 1
        else:
            ltmp.append(li[j])
            j += 1
    # while执行完,肯定会有一部分没数了
    while i<= mid :   # 如果是第一部分仍有数
        ltmp.append(li[i])
        i += 1
    while j <= high:   # 如果是第二部分仍有数
        ltmp.append(li[j])
        j += 1
    # 将ltmp的值写回到li
    li[low:high+1] = ltmp   # 切片往回写


li = [2, 4, 5, 7, 1, 3, 6, 8]
merge(li, 0, 3, 7)
print(li)
"""
[1, 2, 3, 4, 5, 6, 7, 8]
"""

二、归并排序——使用归并

  归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。

1、归并排序图示

  

  分解:将列表越分越小,直至分成一个元素。

  终止条件:一个元素是有序的。

  合并:将两个有序列表归并,列表越来越大。

2、归并排序代码实现

def merge(li, low, mid, high):
    """
    归并过程
    :param li:列表
    :param low:第一段第一个元素
    :param mid:第一段最后一个元素
    :param high:第二段最后一个元素
    :return:
    """
    i = low
    j = mid + 1   # 第二段第一个元素
    ltmp = []  # 新列表
    while i <= mid and j<= high:    # 只要左右两边都有数
        if li[i] < li[j]:
            ltmp.append(li[i])
            i += 1
        else:
            ltmp.append(li[j])
            j += 1
    # while执行完,肯定会有一部分没数了
    while i<= mid :   # 如果是第一部分仍有数
        ltmp.append(li[i])
        i += 1
    while j <= high:   # 如果是第二部分仍有数
        ltmp.append(li[j])
        j += 1
    # 将ltmp的值写回到li
    li[low:high+1] = ltmp   # 切片往回写


def _merge_sort(li, low, high):     # 递归函数
    """归并排序"""
    if low < high:    # 翟少有两个元素,递归
        mid = (low + high) // 2
        _merge_sort(li, low, mid)    # 把左边排好序
        _merge_sort(li, mid+1, high)   # 把右边排好序
        merge(li, low, mid, high)
        print(li[low: high+1])


def merge_sort(li):
    return _merge_sort(li, 0, len(li)-1)


li = list(range(10))
import random
random.shuffle(li)
print(li)
merge_sort(li)
print(li)
"""
[6, 2, 8, 3, 1, 9, 7, 5, 4, 0]
[2, 6]
[2, 6, 8]
[1, 3]
[1, 2, 3, 6, 8]
[7, 9]
[5, 7, 9]
[0, 4]
[0, 4, 5, 7, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
"""

3、归并和归并排序时间、空间复杂度  

  每一层的时间复杂度是O(n),层数是logn。因此总的时间复杂度是O(nlogn)

  由于merge函数创建了一个ltmp的临时空间,到最大的时候长度是n,空间复杂度是O(n)。不再是原地排序。

 

转载于:https://www.cnblogs.com/xiugeng/p/9672639.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MPI(Message Passing Interface)是一种用于并行计算的编程模型和库。归并排序是一种经典的排序算法,适合并行计算。 在MPI中,可以通过发送和接收消息来实现进程间的通信。下面是一个基于MPI的归并排序的伪代码: ```python def parallel_merge_sort(data): # 获取进程总和当前进程编号 size = MPI.COMM_WORLD.Get_size() rank = MPI.COMM_WORLD.Get_rank() # 计算每个进程要处理的据量 chunk_size = len(data) // size remainder = len(data) % size # 将据分发到各个进程 if rank == 0: for i in range(size): if i < remainder: chunk = data[i * (chunk_size + 1):(i + 1) * (chunk_size + 1)] else: chunk = data[remainder + i * chunk_size:remainder + (i + 1) * chunk_size] MPI.COMM_WORLD.send(chunk, dest=i, tag=0) # 接收据 chunk = MPI.COMM_WORLD.recv(source=0, tag=0) # 对本地据进行排序 chunk.sort() # 归并排序 for step in range(size): # 计算要交换据的进程编号 partner = (rank + step) % size # 发送和接收据 sendbuf = chunk recvbuf = MPI.COMM_WORLD.recv(source=partner, tag=step) if rank < partner: sendtag = step recvtag = step + size else: sendtag = step + size recvtag = step MPI.COMM_WORLD.send(sendbuf, dest=partner, tag=sendtag) chunk = merge(chunk, recvbuf) # 将排序好的据返回 if rank == 0: result = [] for i in range(size): chunk = MPI.COMM_WORLD.recv(source=i, tag=size) result.extend(chunk) return result else: MPI.COMM_WORLD.send(chunk, dest=0, tag=size) ``` 在这个算法中,首先将原始据分发到各个进程,然后每个进程对本地据进行排序,接着对每个步骤进行归并排序,并且使用MPI的send和recv进行交换据。最后将排序好的据返回到主进程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值