剑指Offer 数组中的逆序对 归并排序
难度困难686收藏分享切换为英文接收动态反馈
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
思路:
这道题题目可以说是“题狠话不多”了。只要有前面一个数字比后面数字大的情况,就可以算作是一个逆序对。第一时间想到的方法当然就是暴力遍历了:从第一个数开始,一直遍历到它之后的数;再从第二个数开始,再向后遍历……可以去试一下,这样子的复杂度为O(n的平方),直接超时。毕竟是个困难题,直接暴力也太不给面子了~
然后,似乎就想不出什么别的办法了……
直接说套(思)路:这道题我们直接用归并排序的模板去做,只需要再加一行代码就做完了。因为这道题的情境非常符合归并排序的条件,通过归并排序,可以顺带着把这道题解决。
归并排序的基本思路应该还记得吧:将一个数组分成左右两半,并对左右两半也进行归并排序,最后将排序好的左右两边进行“有序数组的合并”。我们正好可以利用最后的合并阶段——比如我们现在要合并两个数组:[5,8,14]和[4,6,7],我们首先从两个数组的第一位开始看,我们发现5>4,这样我们就发现了(5,4)可以组成一个逆序对——紧接着,因为第一个有序数组中5后面的数肯定都是比5大的,因此它后面的所有数又都可以和4组成逆序对!它后面的所有数的个数我们通过此时遍历到的位置也很好求,直接加进结果即可。同理,当合并考虑到(8,6)的时候,紧接着(14,6)也会被计算到……
就这样在正常的归并排序中添加一步普通的计算操作,我们的结果就一步步计算出来了。
代码:
class Solution(object):
def reversePairs(self, nums):
def merge_sort(nums):#归并排序
lenth=len(nums)
if lenth<=1:return nums
n = lenth//2#拿到数组一半的位置
left = merge_sort(nums[:n])#对左半侧进行排序
right=merge_sort(nums[n:])#对右半侧进行排序
return merge(left,right)#合并两个有序数组
def merge(nums1,nums2):
len1 = len(nums1) #分别拿到两个数组的长度
len2 = len(nums2)
res=[]
l=0#l是在第一个数组上游走的指针
r=0#r是在第二个数组上游走的指针
while l<len1 and r<len2 :#当l和r各自都没有走到头
n1 = nums1[l]#拿到他们对应的元素
n2 = nums2[r]
if n1>n2:#若n1的值大于n2,则说明n1及之后的值都可以和n2组成逆序对
#n1及之后的值的个数为len1-l,注意这里l是字母,不是数字1
total_res[0]+=len1-l#就在整个归并排序的基础上加这么一句话!!!
res.append(n2)#谁小就先放谁,正常归并排序
r+=1#n2小,r向右移动
else:
res.append(n1)#谁小就先放谁,正常归并排序
l+=1#n1小,l向右移动
#此时l或r走到了头,为了将没走到头的数组的剩余部分加入结果
#直接无差别进行下面操作,反正剩下的这些都是最大的
res+=nums1[l:]
res+=nums2[r:]
return res
#正式开始
total_res=[0]#用列表定义全局结果变量,初始化为0
merge_sort(nums)#调用一遍归并排序 结果变量就更新完成了
return total_res[0]#返回这个结果
代码注释很详细了。虽然是个困难题,但是难的也只是知道思路,知道思路后写起来就不是那么困难了~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了