leetcode1157 子数组中占绝大多数的元素

题意:就是查询区间内的绝对众数(保证次数超过一半)

解法一:随机+二分

对于一个查询[left, right, threshold],从arr[left...right]中随机取一个,它有一个预处理好的”出现点“数组,求出[left, right]包含的“出现点”的个数,再与threshold比较。

class MajorityChecker {
public:
    #define random(a,b) ((a)+rand()%((b)-(a)+1))    
    unordered_map<int, vector<int>>mp;
    vector<int>arr;
    MajorityChecker(vector<int>& _arr) {
        for(int i = 0;i < _arr.size();i++)  mp[_arr[i]].push_back(i);
        arr = _arr;
    }
    
    int query(int left, int right, int threshold) {
        int cnt = 0;
        while(cnt < 10)
        {
            cnt++;
            int major = arr[random(left, right)];
            // cout << random(left, right) << endl;
            vector<int>& vec = mp[major];  // 写成引用才不会超时
            auto it1 = lower_bound(vec.begin(), vec.end(), left);
            // if(it1 == mp[major].end())  return -1;
            auto it2 = upper_bound(vec.begin(), vec.end(), right);
            if(it2 - it1 >= threshold)  return major;
        }
        return -1;
    }
};

解法二:用线段树维护绝对众数

总所周知,线段树不能维护众数,但是在这里,可以维护绝对众数。

要使用线段树,需要元线段可累加,对这道题目,可以使用摩尔投票法对元线段进行累加。
令元素的值为val,元素的个数为count,
(val1, count1) + (val2, count2) =
(val1, count1+count2) cond val1 == val2
(val1, count1-count2) cond val1 != val2 && count1 > count2
(val2, count2-count1) cond val1 != val2 && count2 > count1
这样建立完树后,就可以使用线段树的查找操作,直接找到区间内的众数的值。

 

Query查询时,得到某个被包含的子区间,也是将当前区间和之前区间做同样的合并。

class MajorityChecker {
public:

    struct SegTree {
        #define maxn 20010  //元素总个数
        #define ls rt<<1
        #define rs rt<<1|1
        int Val[maxn<<2],Count[maxn<<2]; //Val绝对众数,Count众数个数
        // int A[maxn],n;//存原数组数据下标[1,n] 
        vector<int>A;

        void init(vector<int>& _A){
            A = _A;
        }

        //PushUp函数更新节点信息 ,这里是求和
        void PushUp(int rt){
            // Sum[rt]=Sum[rt<<1]+Sum[rt<<1|1];
            // Max[rt] = max(Max[rt<<1], Max[rt<<1|1]); // 标记不需要向上维护
            if(Val[ls] == Val[rs]) {
                Val[rt] = Val[ls];
                Count[rt] = Count[ls] + Count[rs];
            } else {
                if(Count[ls] < Count[rs]) {
                    Val[rt] = Val[rs];
                    Count[rt] = Count[rs] - Count[ls];
                } else {
                    Val[rt] = Val[ls];
                    Count[rt] = Count[ls] - Count[rs];
                }
            }
        }
        //Build函数建树 
        void Build(int l,int r,int rt){ //l,r表示当前节点区间,rt表示当前节点编号
            // cout << "build: " << l << " " << r << endl;
            if(l==r) {//若到达叶节点 
                // Sum[rt]=A[l-1];//储存数组值 
                Val[rt]=A[l];
                Count[rt]=1;
                return;
            }
            int m=(l+r)>>1;
            //左右递归 
            Build(l,m,ls);
            Build(m+1,r,rs);
            //更新信息 
            PushUp(rt);
        }


        void Query(int L,int R,int l,int r,int rt, int& val, int& counts){//L,R表示操作区间,l,r表示当前节点区间,rt表示当前节点编号
            // cout << "Query: " << l << " " << r << endl;
            if(L <= l && r <= R){
                //在区间内,直接返回 
                // return Max[rt];
                if(Val[rt] == val)  counts += Val[rt];
                else {
                    if(Count[rt] > counts) {
                        val = Val[rt];
                        counts = Count[rt] - counts;
                    } else {
                        counts -= Count[rt];
                    }
                }
                return;
            }
            int m=(l+r)>>1;
            //下推标记,否则Sum可能不正确
            // PushDown(rt,m-l+1,r-m); 
            
            //累计答案
            if(L <= m) Query(L,R,l,m,ls,val,counts);
            if(R >  m) Query(L,R,m+1,r,rs,val,counts);
        } 
    }segTree;
    int n;
    unordered_map<int, vector<int>>mp;

    MajorityChecker(vector<int>& arr) {
        n = arr.size();
        segTree.init(arr);
        segTree.Build(0, n-1,1);

        for(int i = 0;i < n;i++)
            mp[arr[i]].push_back(i);
    }
    
    int query(int left, int right, int threshold) {
        int znum = -1, counts = 0;
        segTree.Query(left, right, 0, n-1, 1, znum, counts);
        auto it1 = lower_bound(mp[znum].begin(), mp[znum].end(), left);
        auto it2 = upper_bound(mp[znum].begin(), mp[znum].end(), right);
        if(it2 - it1 >= threshold)  return znum;
        return -1;
    }
};

注意,区间可以写成[0, n-1]也可以是[1, n]在Build赋值和调用Build, Query时做个区分就行。

其次,没有Update,是静态的数组。

 

参考链接:https://leetcode-cn.com/problems/online-majority-element-in-subarray/solution/er-fen-fa-mo-er-tou-piao-fa-xian-duan-shu-by-xiaoy/

posted @ 2021-03-15 12:27  Rogn  阅读(336)  评论(0编辑  收藏  举报