2026-04-19:固定长度子数组中的最小逆序对数目。用go语言,给你一个整数数组 nums(长度为 n)和一个整数 k。所谓“逆序对”,指的是在数组中下标满足 i < j 且 nums[i] >

张开发
2026/4/19 11:44:33 15 分钟阅读

分享文章

2026-04-19:固定长度子数组中的最小逆序对数目。用go语言,给你一个整数数组 nums(长度为 n)和一个整数 k。所谓“逆序对”,指的是在数组中下标满足 i < j 且 nums[i] >
2026-04-19固定长度子数组中的最小逆序对数目。用go语言给你一个整数数组 nums长度为 n和一个整数 k。所谓“逆序对”指的是在数组中下标满足 i j 且 nums[i] nums[j] 的任意一对位置 (i, j)。对某个连续片段即子数组而言统计它内部所有满足上述条件的逆序对数量这个数量就是该子数组的“逆序对数量”。现在考虑 nums 中所有长度恰好为 k 的连续子数组要求找出其中逆序对数量的最小值并返回。1 n nums.length 100000。1 nums[i] 1000000000。1 k n。输入nums [5,3,2,1], k 4。输出6。解释只有一个长度为 k 4 的子数组[5, 3, 2, 1]。在该子数组中逆序对为(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), 和 (2, 3)。逆序对总数为 6因此最小逆序对数量是 6。题目来自力扣3768。大体步骤如下一、核心前置知识铺垫逆序对子数组内满足ij 且 nums[i]nums[j]的数对我们要统计每个长度为k的连续子数组的逆序对总数找最小值。滑动窗口数组中所有长度为k的连续子数组本质是一个固定大小为k的窗口从左向右滑动每次移动一位。树状数组高效完成单点更新和前缀和查询的数据结构用来快速统计逆序对避免暴力枚举超时。离散化因为数组元素值最大可达1e9无法直接用树状数组存储所以把原数组的值映射为连续的小整数1,2,3…。二、分步骤详细执行过程步骤1离散化处理压缩数值范围原数组[5, 3, 2, 1]克隆原数组并排序、去重得到排序去重数组[1,2,3,5]把原数组的每个值替换为它在排序数组中的位置1树状数组下标从1开始5 → 43 → 32 → 21 → 1离散化后的数组[4, 3, 2, 1]✅ 作用把超大数值变成1~4的连续整数适配树状数组。步骤2初始化核心变量初始化树状数组长度为离散化后最大值1 5初始值全0。逆序对计数器inv 0记录当前窗口的逆序对总数。答案变量ans 最大值存储所有窗口的最小逆序对。固定窗口大小k4。步骤3滑动窗口遍历数组逐个元素处理我们遍历离散化后的数组[4,3,2,1]每一步分为元素入窗口 → 计算逆序对 → 窗口成型则更新答案 → 元素出窗口。第1次遍历处理第一个元素4元素入窗口把数字4加入树状数组树状数组标记4的位置计数1。累加逆序对当前窗口内比4大的数字个数为0逆序对总数inv 0。窗口判断当前窗口长度1 4未形成有效窗口不执行后续操作。第2次遍历处理第二个元素3元素入窗口把数字3加入树状数组树状数组标记3的位置计数1。累加逆序对当前窗口内比3大的数字只有4逆序对总数inv 1。窗口判断当前窗口长度2 4未形成有效窗口不执行后续操作。第3次遍历处理第三个元素2元素入窗口把数字2加入树状数组树状数组标记2的位置计数1。累加逆序对当前窗口内比2大的数字有4、3逆序对总数inv 123。窗口判断当前窗口长度3 4未形成有效窗口不执行后续操作。第4次遍历处理第四个元素1元素入窗口把数字1加入树状数组树状数组标记1的位置计数1。累加逆序对当前窗口内比1大的数字有4、3、2逆序对总数inv 336。窗口判断窗口长度4刚好形成第一个有效窗口。更新最小答案当前逆序对总数6是最小值ans6。元素出窗口窗口左侧第一个元素4移出树状数组树状数组标记4的位置计数-1。修正逆序对减去移出元素4带来的逆序对数量逆序对总数更新。步骤4遍历结束输出结果整个数组遍历完成唯一的有效窗口逆序对总数为6最终返回6。三、关键逻辑补充说明元素入窗口时用树状数组快速查询当前窗口中比新元素大的数字个数直接累加到逆序对总数。窗口成型后用当前逆序对总数更新最小值。元素出窗口时用树状数组快速查询比移出元素小的数字个数从逆序对总数中减去因为这个元素离开了窗口它贡献的逆序对也要删掉。提前终止如果逆序对总数变为0理论最小值直接结束计算优化效率。四、时间复杂度分析离散化排序去重映射时间复杂度为O(n log n)。滑动窗口遍历遍历n个元素每个元素执行2次树状数组操作更新查询树状数组单次操作复杂度为O(log m)m是离散化后的数值范围m≤n。总遍历复杂度O(n log n)。✅总的时间复杂度O(n log n)这是处理n≤1e5数据的最优复杂度能完美通过题目限制五、额外空间复杂度分析额外空间指除输入数组外代码主动开辟的空间离散化用的排序数组O(n)。树状数组O(n)。其他临时变量O(1)。✅总的额外空间复杂度O(n)总结执行流程离散化压缩数值 → 滑动窗口遍历元素 → 树状数组高效统计逆序对 → 维护最小逆序对数量时间复杂度O(n log n)适配1e5规模数据额外空间复杂度O(n)。Go完整代码如下packagemainimport(fmtmathslicessort)typefenwick[]intfunc(t fenwick)update(i,valint){for;ilen(t);ii-i{t[i]val}}func(t fenwick)pre(iint)(resint){for;i0;ii-1{rest[i]}return}funcminInversionCount(nums[]int,kint)int64{// 离散化sorted:slices.Clone(nums)slices.Sort(sorted)sortedslices.Compact(sorted)fori,x:rangenums{nums[i]sort.SearchInts(sorted,x)1// 树状数组下标从 1 开始}t:make(fenwick,len(sorted)1)inv:0ans:math.MaxIntfori,in:rangenums{// 1. 入t.update(in,1)invmin(i1,k)-t.pre(in)// 窗口大小 - (x 的元素个数) (x 的元素个数)left:i1-kifleft0{// 尚未形成第一个窗口continue}// 2. 更新答案ansmin(ans,inv)ifans0{// 已经最小了无需再计算break}// 3. 出out:nums[left]inv-t.pre(out-1)// out 的元素个数t.update(out,-1)}returnint64(ans)}funcmain(){nums:[]int{5,3,2,1}k:4result:minInversionCount(nums,k)fmt.Println(result)}Python完整代码如下# -*-coding:utf-8-*-fromtypingimportListclassFenwick:def__init__(self,n:int):self.nn self.bit[0]*(n1)defupdate(self,i:int,val:int)-None:将位置i增加vali从1开始whileiself.n:self.bit[i]val ii-idefpre(self,i:int)-int:前缀和1到i的和res0whilei0:resself.bit[i]ii-1returnresdefmin_inversion_count(nums:List[int],k:int)-int: 计算所有长度为k的子数组中逆序对的最小值 # 离散化sorted_numssorted(set(nums))# 映射到1,2,3,...树状数组下标从1开始num_to_idx{val:idx1foridx,valinenumerate(sorted_nums)}nums[num_to_idx[x]forxinnums]# 树状数组bitFenwick(len(sorted_nums))inv0# 当前窗口的逆序对数ansfloat(inf)fori,numinenumerate(nums):# 1. 入将当前元素加入窗口bit.update(num,1)# 窗口大小 min(i1, k)window_sizemin(i1,k)# 大于num的元素个数 窗口大小 - 小于等于num的元素个数invwindow_size-bit.pre(num)lefti1-kifleft0:# 尚未形成第一个窗口continue# 2. 更新答案ansmin(ans,inv)ifans0:# 已经是最小值无需继续计算break# 3. 出移除窗口最左边的元素out_numnums[left]# 小于out_num的元素个数这些元素与out_num构成逆序对inv-bit.pre(out_num-1)bit.update(out_num,-1)returnint(ans)defmain():nums[5,3,2,1]k4resultmin_inversion_count(nums,k)print(result)if__name____main__:main()C完整代码如下#includeiostream#includevector#includealgorithm#includeclimitsusingnamespacestd;classFenwick{private:vectorintbit;public:Fenwick(intn):bit(n1,0){}voidupdate(inti,intval){for(;ibit.size();ii-i){bit[i]val;}}intpre(inti){intres0;for(;i0;ii-1){resbit[i];}returnres;}};longlongminInversionCount(vectorintnums,intk){// 离散化vectorintsortednums;sort(sorted.begin(),sorted.end());sorted.erase(unique(sorted.begin(),sorted.end()),sorted.end());for(inti0;inums.size();i){nums[i]lower_bound(sorted.begin(),sorted.end(),nums[i])-sorted.begin()1;// 树状数组下标从1开始}Fenwickbit(sorted.size());intinv0;intansINT_MAX;for(inti0;inums.size();i){intinnums[i];// 1. 入bit.update(in,1);invmin(i1,k)-bit.pre(in);// 窗口大小 - (x的元素个数) (x的元素个数)intlefti1-k;if(left0){// 尚未形成第一个窗口continue;}// 2. 更新答案ansmin(ans,inv);if(ans0){// 已经最小了无需再计算break;}// 3. 出intoutnums[left];inv-bit.pre(out-1);// out的元素个数bit.update(out,-1);}return(longlong)ans;}intmain(){vectorintnums{5,3,2,1};intk4;longlongresultminInversionCount(nums,k);coutresultendl;return0;}

更多文章