找出唯一性数组的中位数_leetcode_0827每日一题思路

3134. 找出唯一性数组的中位数找出唯一性数组的中位数

给你一个整数数组 nums 。数组 nums 的 唯一性数组 是一个按元素从小到大排序的数组,包含了 nums 的所有非空 

子数组

 中不同元素的个数。

换句话说,这是由所有 0 <= i <= j < nums.length 的 distinct(nums[i..j]) 组成的递增数组。

其中,distinct(nums[i..j]) 表示从下标 i 到下标 j 的子数组中不同元素的数量。

返回 nums 唯一性数组 的 中位数 

注意,数组的 中位数 定义为有序数组的中间元素。如果有两个中间元素,则取值较小的那个。

理解唯一性数组:唯一性数组由数组 nums 中所有可能的非空子数组包含的不同元素个数组成,并且这些不同元素个数是递增排序的。

排序:首先对输入数组 nums 进行排序。这有助于我们识别不同元素并计算它们的个数。

计算不同元素个数:使用双指针或滑动窗口的方法,我们可以计算从每个元素开始到数组末尾的所有子数组的不同元素个数。

构建唯一性数组:虽然我们不需要显式构建唯一性数组,但我们需要理解它的形成。唯一性数组的每个元素是数组 nums 中每个不同元素后面的子数组的不同元素数量。

计算唯一性数组的中位数:

唯一性数组的长度是所有子数组不同元素数量的总和加一(加一是因为包含单个元素的子数组)。
中位数是唯一性数组排序后位于中间位置的数。如果唯一性数组的长度是奇数,中位数是中间的数;如果是偶数,中位数是中间两个数中较小的那一个。
避免显式构建:由于唯一性数组可能非常大,显式构建它将非常低效。我们的目标是避免这样做,而是通过数学计算或二分查找来确定中位数。

二分查找:我们可以使用二分查找来确定中位数。首先,确定一个左闭右开的区间 [0, len(nums)),然后找到满足条件的最长子数组,即该子数组的末尾元素是当前扫描到的元素,并且该子数组的不同元素数量位于我们的中位数区间内。

更新中位数:根据找到的最长子数组,我们可以更新中位数的估计。如果唯一性数组的长度大于中位数,我们增加中位数;否则,我们减少中位数的估计。

终止条件:当二分查找的区间缩小到一个点时,我们就找到了唯一性数组的中位数。
  • 计算每个子数组的不同元素个数

    • 对于给定的数组 nums,我们需要计算每个可能的子数组 nums[i..j] 的不同元素个数。这意味着我们需要计算出从 ij 的所有子数组的 distinct(nums[i..j])
  • 生成唯一性数组

    • 唯一性数组即包含了所有子数组中不同元素个数的一个数组。我们可以通过滑动窗口来有效地计算这些不同元素个数。
  • 排序唯一性数组

    • 一旦我们得到了所有子数组的不同元素个数,我们需要对这些个数进行排序,以便找到中位数。
  • 找到中位数

    • 对于一个长度为 n 的排序数组,中位数的位置是 (n-1) / 2,我们可以直接返回该位置的值,
  • 这里可以通过730个测试用例
    新的方案是:
    这个题,可以首先先得到原始数组的长度n,之后找到其所有子数组n*(n+1)/2的中间的那一个元素的位置n*(n+1)/4,同时,检查这个元素的左右两边的所有子数组是否存在重复元素,即当子数组里面粗存在数组长度和其实际不同的元素个数不一致的个数,计算左右的总数的差值,之后判断,如果左面多,则向左移动对应个数后,返回当前子数组里面不同元素的数量

    ### 优化思路:

    1. **计算子数组的总数**:
       - 子数组的总数为 `n*(n+1)/2`,其中 `n` 是数组的长度。

    2. **确定中位数位置**:
       - 中位数位置为 `n*(n+1)/4`。这个位置可以通过滑动窗口或其他高效方法找到。

    3. **滑动窗口查找唯一元素的数量**:
       - 使用滑动窗口来计算子数组中不同元素的数量,这可以高效计算每个子数组的唯一性。

    4. **调整中位数位置**:
       - 如果子数组中有重复的元素,调整左边和右边的窗口,找到正确的中位数。

    ### Python 实现:

    
    from typing import List
    
    class Solution:
        def medianOfUniquenessArray(self, nums: List[int]) -> int:
            n = len(nums)
            total_subarrays = n * (n + 1) // 2
            target_position = total_subarrays // 4
    
            def count_distinct_subarrays(nums: List[int], n: int) -> List[int]:
                unique_counts = []
                for i in range(n):
                    freq = {}
                    unique_count = 0
                    for j in range(i, n):
                        if nums[j] not in freq:
                            unique_count += 1
                        freq[nums[j]] = freq.get(nums[j], 0) + 1
                        unique_counts.append(unique_count)
                return unique_counts
    
            # 得到所有子数组中不同元素的数量
            unique_counts = count_distinct_subarrays(nums, n)
            unique_counts.sort()
    
            # 寻找左右边界
            left_counts = sum(1 for count in unique_counts if count < unique_counts[target_position])
            right_counts = sum(1 for count in unique_counts if count > unique_counts[target_position])
    
            # 判断是否需要调整中位数的位置
            if left_counts > right_counts:
                return unique_counts[target_position - (left_counts - right_counts)]
            else:
                return unique_counts[target_position]
    
    

    ### 解释:

    1. **count_distinct_subarrays** 函数: 
       - 用于计算所有子数组中不同元素的数量。

    2. **target_position**:
       - 用于确定中位数的目标位置。

    3. **调整中位数位置**:
       - 如果左边的子数组计数多于右边,则向左调整位置,反之向右调整。

    ### 性能优化:

    虽然这个方法通过计算中位数位置并使用滑动窗口提高了效率,但在极端情况下仍然会有性能瓶颈。你可以考虑进一步优化,例如:
    - **哈希表**:实时跟踪子数组中不同元素的数量。
    - **二分查找**:在有序的唯一性数组中进行二分查找。

    以上实现结合了你提供的方法,进一步优化了计算和调整中位数的过程,保证了较高的运行效率。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值