P1168 中位数 vector/priority_queue/权值线段树
代码1注释与解题思路
解题思路
这段代码使用了一个动态维护的有序数组来求解中位数。对于每个新输入的数字,使用二分查找将其插入到正确的位置以保持数组有序。当处理到奇数个元素时,直接输出数组中间位置的元素作为中位数。
代码注释
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; vector<int> a; // 用于存储有序序列的vector int n,x; // n表示数字个数,x用于临时存储输入的数字 int main() { // 读取第一个数字并直接输出(第一个中位数就是它自己) cin >> n >> x; a.push_back(x); cout << x << endl; // 处理后续数字 for(int i = 2; i <= n; i++) { cin >> x; // 使用upper_bound找到插入位置,保持数组有序 a.insert(upper_bound(a.begin(),a.end(),x),x); // 如果是奇数个数字,输出当前中位数 if(i % 2 == 1){ // 输出中间位置的元素 cout << a[i / 2] << endl; } } return 0; }
代码2注释与解题思路
解题思路
这段代码使用了两个堆(优先队列)来高效地维护中位数。一个大根堆存储较小的一半数字,一个小根堆存储较大的一半数字。通过平衡两个堆的大小,可以快速获取当前的中位数。这种方法的时间复杂度比第一种更优。
代码注释
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 10; priority_queue<int,vector<int>,greater<int> > mx; // 小根堆,存储较大的一半数字 priority_queue<int> mi; // 大根堆,存储较小的一半数字 int n,x,ans; // n表示数字个数,x用于临时存储输入的数字 int main() { // 读取第一个数字并直接输出 cin >> n >> x; mx.push(x); // 初始放入小根堆 cout << mx.top() << endl; // 第一个中位数 // 处理后续数字 for(int i = 2; i <= n; i++) { cin >> x; // 根据数字大小决定放入哪个堆 if(mx.top() <= x) mx.push(x); if(mx.top() > x) mi.push(x); // 平衡两个堆的大小,保持小根堆大小等于或比大根堆大1 if(mi.size() + 1 < mx.size()){ mi.push(mx.top()); mx.pop(); } else if(mi.size() > mx.size()){ mx.push(mi.top()); mi.pop(); } // 如果是奇数个数字,输出小根堆的堆顶(当前中位数) if(i % 2 == 1) { cout << mx.top() << endl; } } return 0; }
代码3注释与解题思路
解题思路
这段代码使用了线段树来统计数字的出现频率,并通过查询线段树来找到中位数。首先对输入数字进行离散化处理,然后使用线段树维护数字的频率。对于奇数个数字,查询线段树找到第k小的数字作为中位数。
代码注释
#include<bits/stdc++.h> #define lc rt << 1 #define rc rt << 1 | 1 #define lson lc,l,mid #define rson rc,mid + 1,r #define ll long long using namespace std; const int N = 1e5 + 10; struct node{ int sum; // 线段树节点,记录区间内数字的总数 }; node t[N << 2]; // 线段树数组 int n,a[N],b[N]; // a存储原始数字,b用于离散化 // 线段树更新函数 void pushup(int rt) { t[rt].sum = t[lc].sum + t[rc].sum; } // 线段树单点修改函数 void change(int rt,int l,int r,int x) { if(x < l || r < x) return; // 超出范围直接返回 if(l == r){ t[rt].sum++; // 找到对应位置,计数+1 return; } int mid = (l + r) >> 1; change(lson,x); // 递归修改左子树 change(rson,x); // 递归修改右子树 pushup(rt); // 更新当前节点 } // 线段树查询第k小的数 int query(int rt,int l,int r,int k) { if(l == r) return l; // 找到叶子节点,返回位置 int mid = (l + r) >> 1; if(t[lc].sum >= k) return query(lson,k); // 左子树数量足够,查询左子树 else return query(rson,k - t[lc].sum); // 否则查询右子树 } int main() { cin >> n; // 读取输入并离散化 for(int i = 1; i <= n; i++) { cin >> a[i]; b[i] = a[i]; // 复制到b数组用于离散化 } // 排序并去重(离散化) sort(b + 1,b + 1 + n); unique(b + 1,b + 1 + n); // 将原始数字映射为离散化后的值 for(int i = 1; i <= n; i++) { int x = lower_bound(b + 1,b + 1 + n,a[i]) - b; a[i] = x; } // 处理每个数字 for(int i = 1; i <= n; i++) { change(1,1,N,a[i]); // 更新线段树 if(i % 2 == 1) // 查询中位数并输出原始值 printf("%d\n",b[query(1,1,N,i / 2 + 1)]); } return 0; }

浙公网安备 33010602011771号