leetcode
leetcode 2151 ~ 2200
统计中位数为 K 的子数组

统计中位数为 K 的子数组

难度:

标签:

题目描述

You are given an array nums of size n consisting of distinct integers from 1 to n and a positive integer k.

Return the number of non-empty subarrays in nums that have a median equal to k.

Note:

  • The median of an array is the middle element after sorting the array in ascending order. If the array is of even length, the median is the left middle element.
    • For example, the median of [2,3,1,4] is 2, and the median of [8,4,3,5,1] is 4.
  • A subarray is a contiguous part of an array.

 

Example 1:

Input: nums = [3,2,1,4,5], k = 4
Output: 3
Explanation: The subarrays that have a median equal to 4 are: [4], [4,5] and [1,4,5].

Example 2:

Input: nums = [2,3,1], k = 3
Output: 1
Explanation: [3] is the only subarray that has a median equal to 3.

 

Constraints:

  • n == nums.length
  • 1 <= n <= 105
  • 1 <= nums[i], k <= n
  • The integers in nums are distinct.

代码结果

运行时间: 69 ms, 内存: 27.0 MB


/*
 * 思路:
 * 1. 使用Java Stream API处理子数组。
 * 2. 遍历所有可能的子数组,检查它们的中位数是否为k。
 */

import java.util.Arrays;
import java.util.stream.IntStream;

public class Solution {
    public int countSubarrays(int[] nums, int k) {
        return (int) IntStream.range(0, nums.length)
            .flatMap(start -> IntStream.range(start, nums.length)
                .map(end -> {
                    int[] subArray = Arrays.copyOfRange(nums, start, end + 1);
                    Arrays.sort(subArray);
                    return subArray[(end - start) / 2];
                })
                .filter(median -> median == k))
            .count();
    }
}

解释

方法:

这个题解使用了一种基于平衡计数的策略来高效地统计子数组。首先,找出中位数 k 在数组中的位置 p。然后,从 p 向左扫描,使用 d 来统计当前位置到 p 的平衡状态(大于 k 的元素增加 d,小于 k 的元素减少 d),并在 count 数组中记录每个 d 值出现的次数。接着,从位置 p+1 向右扫描,更新 d 的值,并利用之前记录在 count 数组中的平衡状态来统计符合条件的子数组数量,这是基于左侧的平衡状态可以与右侧的平衡状态结合,形成以 k 为中位数的子数组。最终,通过对所有可能的平衡状态进行统计,得到以 k 为中位数的所有子数组的数量。

时间复杂度:

O(n)

空间复杂度:

O(n)

代码细节讲解

🦆
算法中的平衡值`d`是如何确保子数组中位数是`k`的,请详细解释其逻辑和作用?
在这个算法中,平衡值`d`用于跟踪数组中大于和小于中位数`k`的元素数量之间的差异。具体来说,当遍历数组时,每遇到一个大于`k`的元素,`d`增加1;每遇到一个小于`k`的元素,`d`减少1。这样,`d`的值可以看作是一个累积差值,表示当前子数组中大于`k`的元素与小于`k`的元素的数量差。当`d`为0或接近0的值时(如1),表示子数组中大于和小于`k`的元素数量大致相等,从而使得`k`可能是该子数组的中位数。通过统计这些平衡值的出现频次,我们可以计算出所有可能的以`k`为中位数的子数组数量。
🦆
题解中提到,初始化`count[0]`为1表示没有元素时平衡值为0,这种设定是基于什么考虑?
初始化`count[0]`为1是基于考虑,当我们开始考察任何子数组时,最初没有任何元素包括在内,因此没有任何大于或小于`k`的元素,使得初始平衡值`d`为0。这相当于在统计中创建了一个基准点,即在任何元素被考虑之前,已经存在一个平衡值为0的状态。这是必要的,因为它允许算法正确地统计那些从`k`开始并且包含`k`的子数组。如果不将`count[0]`初始化为1,则这些情况将会被漏计。
🦆
在向左扫描时对平衡值`d`的更新规则是`nums[i] > k`则`d`增加1,否则减少1,为何不是相反的?
向左扫描时,我们实际上是在计算以`k`为结束位置的子数组的平衡状态。在这种情况下,当我们遇到一个大于`k`的元素时,增加`d`是因为这增加了子数组中大于`k`的元素的计数,使得平衡向大于`k`的方向倾斜。相反,当遇到一个小于`k`的元素时,我们减少`d`,因为这会使平衡向小于`k`的方向倾斜,从而纠正平衡值以反映当前子数组中元素的实际分布。这种计数方式是为了确保`d`正确反映了从当前位置到`k`的每个元素对平衡状态的影响。

相关问题