LeetCode-2488. 统计中位数为 K 的子数组【哈希表,前缀和】

题目描述:

给你一个长度为 n 的数组 nums ,该数组由从 1 到 n 的 不同 整数组成。另给你一个正整数 k 。

统计并返回 nums 中的 中位数 等于 k 的非空子数组的数目。

注意:

数组的中位数是按 递增 顺序排列后位于 中间 的那个元素,如果数组长度为偶数,则中位数是位于中间靠 左 的那个元素。
例如,[2,3,1,4] 的中位数是 2 ,[8,4,3,5,1] 的中位数是 4 。
子数组是数组中的一个连续部分。

示例 1:

输入:nums = [3,2,1,4,5], k = 4
输出:3
解释:中位数等于 4 的子数组有:[4]、[4,5] 和 [1,4,5] 。

示例 2:

输入:nums = [2,3,1], k = 3
输出:1
解释:[3] 是唯一一个中位数等于 3 的子数组。

提示:

n == nums.length
1 <= n <= 105
1 <= nums[i], k <= n
nums 中的整数互不相同
https://leetcode.cn/problems/count-subarrays-with-median-k/description/

解题思路一:分类讨论,首先看奇数子数组情况:由于题目保证 nums 中的整数互不相同,「k是长为奇数的子数组的中位数」等价于「子数组中小于 k 的数的个数 = 大于 k 的数的个数」。这相当于「左侧小于 + 右侧小于 = 左侧大于 + 右侧大于」。变形得到「左侧小于 − 左侧大于 = 右侧大于 − 右侧小于」。

为了方便计算,把这四类数字等价转换:

  • 左侧小于:在 k 左侧且比 k 小的视作 1;
  • 左侧大于:在 k 左侧且比 k 大的视作 −1;
  • 右侧大于:在 k 右侧且比 k 大的视作 1;
  • 右侧小于:在 k 右侧且比 k 小的视作 −1。
  • 此外,把 k 视作 0。

主要思路是首先遍历一遍k左边的数组,将cnt遍历出来,然后在遍历右边数组的时候计算当前的值x,只需将左边cnt[x](值为x的子数组的个数加入答案即可)

在偶数的情况下:对于子数组长为偶数的情况,「k 是长为偶数的子数组的中位数」等价于「左侧小于 + 右侧小于 = 左侧大于 + 右侧大于 −1」,即「左侧小于 − 左侧大于 = 右侧大于 − 右侧小于 −1」。相比奇数的情况,等号右侧多了个 −1,那么接着上面的「右侧大于 − 右侧小于」的值 x 来说,cnt[x−1] 就是该右端点对应的符合题目要求的长为偶数的子数组个数。累加这些 cnt[x−1],就是子数组长为偶数时的答案。

即这里可以在上面的基础上操作,当遍历到右边数组的值为x的时候,在奇数的情况下对应的是左边数组的cnt[x];但偶数情况下此时右边减了1那么对应的是cnt[x-1]
最终答案就是在遍历右边数组时的cnt[x]+cnt[x-1]的一个不断累加。

class Solution:
    def countSubarrays(self, nums: List[int], k: int) -> int:
        pos=nums.index(k)#取其中值为k的地址
        cnt,x=Counter({
    
    0:1}),0
        # i=pos 的时候 x 是 0,直接记到 cnt 中,这样下面不是大于 k 就是小于 k
        for i in range(pos-1,-1,-1):#从pos-1到0步长为-1反向遍历
            x+=1 if nums[i]<k else -1
            cnt[x]+=1
        ans,x=cnt[0]+cnt[-1],0
        for i in range(pos+1,len(nums)):
            x+=1 if nums[i]>k else -1
            ans+=cnt[x]+cnt[x-1]
        return ans

时间复杂度:O(n)
空间复杂度:O(n)
也可以将哈希表替换为数组,加快速度。

解题思路二:0


解题思路三:0


猜你喜欢

转载自blog.csdn.net/qq_45934285/article/details/129579133