【Python】归并排序极其在小和问题、逆序对问题中的应用


总结下本人目前对归并排序的理解,有不到位的地方麻烦大家批评指正。

  • 归并排序相对于暴力求解为什么能降低时间复杂度呢?

假设原问题的规模为n,归并排序把原问题划分为两个规模为n/2的子问题。减少时间复杂的度的关键在于在merge两个子数组的时候,子数组是有序的

举个例子:

原数组:[ 1, 4, 5, 8, 2, 3, 9, 11]

子数组left: [ 1, 4, 5, 8]

子数组right:[2, 3, 9, 11]

若用冒泡排序,时间复杂度O(n^2),对原数组进行排序时,怎么才能确定1是不是这个数组中最小的数呢,我们需要将整个数组遍历一遍;而如果对有序的子数组进行merge排序,只需要判断子数组left中的最小数1 < 子数组right中的最小数2,就可以确定1是整个数组中的最小数,减少了很多不必要的比较,从而降低了时间复杂度,为O(N*logN)。

  • 将归并排序的思路应用在求小和及逆序对问题中:

我走了很多的弯路,原先我对于归并排序的理解只是把问题不断的划分划分划分为子问题,只是关注用一个变量记录题目需要求解的内容:小和或者逆序对,忽略了更新数组的排序,忽略了子数组有序才能降低时间复杂度。

下面直接PO代码啦,之后我会整理一个Python的对数器模版方便调代码。

1、归并排序

#1、归并排序
def merge (a, b):
    help = []
    i = j = 0
    while i < len(a) and j < len(b):
        if a[i] < b[j]:
            help.append(a[i])
            i += 1
        else:
            help.append(b[j])
            j += 1
    if i == len(a):
        for num in b[j:]:
            help.append(num)
    else:
        for num in a[i:]:
            help.append(num)
    return help

def merge_sort(alist):
    if len(alist) <= 1:
        return alist
    mid_index = len(alist) // 2
    left = merge_sort(alist[: mid_index])
    right = merge_sort(alist[mid_index :])
    return(merge(left, right))

2、小和问题:

#小和问题:
#在一个数组中,每一个左边比当前小的数累加起来,叫做这个数组的小和。求一个数组的小和。
#example:[1, 3, 4, 2, 5]
def small_sum(alist):
    if len(alist) <= 1:
        return 0
    else:
        return(merge_count(alist, 0, len(alist)-1))

def merge_count(alist, left, right):
    if left == right:
        return 0
    mid = (left + right) // 2
    return merge_count(alist, left, mid) + merge_count(alist, mid + 1, right) + merge(alist, left, mid, right)
 
def merge(alist, left, mid, right):
    help = []
    p1 = left
    p2 = mid+1
    res = 0
    while p1 <= mid and p2 <= right:
        # res += (right - p2 + 1)* alist[p1] if alist[p1] < alist[p2] else 0
        if alist[p1] < alist[p2]:
            res += (right - p2 + 1)* alist[p1]
            help.append(alist[p1])
            p1 += 1
        else:
            help.append(alist[p2])
            p2 += 1
    while p1 <= mid:
        help.append(alist[p1])
        p1 += 1
    while p2 <= right:
        help.append(alist[p2])
        p2 += 1
    for i in range(len(help)):
        alist[left + i] = help[i]
    return res    

#暴力求解  (选择一个绝对正确但是低效的算法作为对照,稍后会整理一个对数器的模版)
def solution2(alist):
    res = 0
    for i in range(0, len(alist)):
        for j in range(0,i):
            if alist[j]< alist[i]:
                res += alist[j]

    return res

if __name__ == "__main__":
    alist1 = [1, 3, 4, 2, 5, 7, 9, 12, 2]
    alist2 = [1, 3, 4, 2, 5, 7, 9, 12, 2]
    print(small_sum(alist1))
    print(solution2(alist2))#small_sum 会更改alist的顺序

3、逆序对问题

##逆序对问题: 在一个数组中,左边的数如果比右边的数大,则这两个数构成一个逆序对,请打印所有逆序对
def small_print(alist):
    if len(alist) <= 1:
        return None
    else: 
        return merge_print(alist, 0, len(alist)-1)

def merge_print(alist, left, right):
    if left == right:
        return []
    mid = (left + right) // 2
    res1 = merge_print(alist, left, mid)
    res2 = merge_print(alist, mid+1, right)
    res3 = merge(alist, left, mid, right)
    return res1 + res2 + res3

def merge(alist, left, mid, right):
    help = []
    res = [] 
    i = left
    j = mid + 1

    while i <= mid and j <= right:
        if alist[i] > alist[j]:
            help.append(alist[i])
            for m in range(j, right+1):
                res.append((alist[i], alist[m]))
            i += 1
        else:
            help.append(alist[j])
            j += 1
        
    while i <= mid:
        help.append(alist[i])
        i += 1

    while j <= right:
        help.append(alist[j])
        j += 1
        
    for t in range(len(help)):
        alist[left + t] = help[t]
    return res


if __name__ == "__main__":
    alist1 = [7, 5, 6, 4]
    print(len(small_print(alist1)))

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值