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/
个性签名:时间会解决一切