题解/算法 {3134. 找出唯一性数组的中位数}

文章讲述了如何利用二分查找和双指针技巧解决LeetCode中的一个问题,即在给定唯一性数组A中找到满足特定条件的子数组个数,从而确定中位数。关键点在于理解数组的单调性并避免常见的错误思维陷阱。
摘要由CSDN通过智能技术生成

题解/算法 {3134. 找出唯一性数组的中位数}

@LINK: https://leetcode.cn/problems/find-the-median-of-the-uniqueness-array/;

Count[l,r]A[l....r]里 不同元素的个数; 令答案数组是ANS[] (他的长度是(1+N)*N/2), 我们的目的是 求ANS的中位数;

看一个有点关联性的题目: @LINK: https://editor.csdn.net/md/?articleId=134148990, 这很容易给你带入到一个错误的思路里…
错误做法: 我们得到ANS, 或者说 得到x在ANS中出现的次数也可以, 总之就是得到ANS; 这是一个很自然(暴力)的做法;
假如说 以A[l]开头的 形如A[l, >=l]的子数组 他的Count值依次为: [1, 1, 2, 2, 3, 3, 4, 4, ...]; 然后我们要更新到 以A[l+1]开头的 形如A[l+1, >=(l+1)]的子数组 他的Count值 是什么呢? 假如说与A[l]相同的 且最靠左的是A[ll], 那么从A[l]变成A[l+1] 会导致 Count[l, l+1, l+2, ..., ll-1]这些值 都-=1, 其他的Count[>=ll]的值 不变;
. 于是你想到了线段树 维护区间修改, 可是还有一个问题, 你即使得到了Count[l, ...N-1]的值 你怎么记录到答案里面呢? 即怎么把他们放到ANS里面呢? 一个个扫描 肯定不行, 但又别无他法…

其实, 这个思路是行不通的…

正解是二分, 中位数 是满足单调性的;

长度为奇数: 排序后的A=[ M个数, x, M个数] (m为中位数) 则所有`<x`的数一定有`<=M`个;
长度为偶数: 排序后的A=[ M个数, x, M+1个数]  (m为中位数) 则所有`<x`的数一定有`<=M`个;

反证很容易, 如果`<x`的数 有`>M`个 那么他就占据了`中位数的位置了`, 因此此时x肯定不是中位数;

因此 问题转换为: 求Count值<x的 子数组 有多少个;
这可以用双指针, 比如说 对于L, 他最大的是R (此时Count[L...R] < xCount[L...(R+1)] >= x), 那么 他就贡献了R-L+1个子数组 (这些子数组的Count值是<x的);

看一个错误: 如果A[R] != A[R+1], 那么Count[L...R] != Count[L...(R+1)]; 很容易犯的直觉错误… (如果说A[R]==A[R+1] 此时确实有Count[L-R] == Count[L-(R+1)]);
. 比如[1,....,2, 1], 虽然2!=1, 但显然Count[1....2] == Count[1...2,1]; 因此你需要用map来维护A[L...R] 判断A[R+1]是否在map里面;

int medianOfUniquenessArray(vector<int>& A) {
    int N = A.size();
    auto all = (1 + N) *1LL* N / 2;
    int64_t M = all / 2;
    if( all % 2 == 0){ -- M;}

    { // ___BinarySearch
        auto Check = [&]( int _mid)->bool{ // `<mid` 有 `<= M`个;
            if( _mid == 1){ return 0 <= M;}

            unordered_map<int,int> Cont;
            int count = 0;
            auto Modify = [&]( int _k, bool _a)->void{
                if( _a){ ++ Cont[_k]; if( Cont[_k] == 1){ ++ count;}}
                else{ -- Cont[_k]; if( Cont[_k] == 0){ -- count;}}
            };
            int64_t sum = 0;
            Modify( A[0], 1); // `l==r==0`;
            for( int l = 0, r = l; l < N; ++l){
                while( r+1 < N){
                	//> 写成`count + (A[r]==A[r+1] ? 0:1)`是错的...
                    if( count + (Cont[A[r+1]]>0 ? 0:1) < _mid){ ++ r; Modify(A[r], 1);}
                    else{ break;}
                }
                sum += (r - l + 1);
                if( sum > M){ return 0;}

                Modify( A[l], 0);
            }
            return 1;
        };
        int l = 1, r = N;
        ASSERTsystem_( l <= r);
        while( l < r){
            auto mid = (l+r+1) >> 1;
            if( Check( mid)){ l = mid;}
            else{ r = mid - 1;}
        }

        if( Check(l)){
            return l;
        }
        else{
        }
    } // ___BinarySearch
    ASSERTcustom_(0);
    return -1;
}
  • 7
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值