Description
给出一个含有N个数的无序数组,找出数列的中位数。当N是偶数,则中位数是数列排序后中间两个数的平均数;若N是奇数,则中位数就是数列排序后中间的那个数假设数组下标从0开始,我们很容易知道,若N是偶数,则中位数就是第(N/2-1)和第(N/2)个数的平均数;若N是奇数,则中位数就是第(N/2)个数。
对于这个问题,大部分人都能够一瞬间想到一种最简单有效的解决办法,并鄙视出题人出的这种脑残题目。因此,作为出题人,有必要对这个问题进行一些条件约束。
你所写的这个程序要在内存容量很小的环境上运行。即使现在PC内存的价格比白菜还白菜,但在某些特殊的环境下总内存容量并非是我们想象的那么充裕。就算环境是一般的家用PC平台,一个软件所占用的系统资源也应该尽可能少,而一个软件同一时间所执行的程序段可以很多,因此有必要将每个程序段的占用资源降到最少。
Input
输入只有两行
第一行是一个正整数N
接下来一行包含N个数,第i个数表示数组里面的元素Ai。
1 <= N <= 500000
Ai是int类型的正整数(0 <= Ai <= 2^31 -1)
Output
输出一个M,数组的中位数,保留一位小数
Sample Input
6 5 8 7 9 6 4
Sample Output
6.5
Hint
注意题目中内存大小的限制
遇到了这样一道题,他的内存限制是2MB , 时限2s, 当然本身对于寻找无序数组中位数,如果内存没有太大限制的话,选择类似快排思想的partition函数,不用排序可以找到中位数,在时间上有很大优势,这个我先是尝试了一下,结果超内存了,我的代码如下:
#include <iostream>
using namespace std;
int partition(int* temp, int l, int h) {
int x = temp[l];
int i = l;
int j = h;
while (i < j) {
while (i < j && temp[j] >= x) j--;
temp[i] = temp[j];
while (i < j && temp[i] <= x) i++;
temp[j] = temp[i];
}
temp[i] = x;
return i;
}
int findmedian(int* temp, int n) {
int pos;
int k, w;
if (n % 2 == 0) {
k = n/2;
w = n/2-1;
pos = partition(temp, 0, n-1);
int p = 0;
int q = n-1;
while (pos != k && pos != w) {
if (pos > k) {
q = pos-1;
pos = partition(temp, p, pos-1);
} else {
p = pos+1;
pos = partition(temp, pos+1, q);
}
}
if (pos == k) {
int a = 0;
for (int j = p; j < k; j++) {
if (temp[j] > a) a = temp[j];
}
float tt;
if ((a+temp[k])%2 != 0) tt = (a+temp[k])/2+0.5;
else tt = (a+temp[k])/2;
cout << tt << endl;
} else {
int a = 0;
for (int j = pos+1; j <= q; j++) {
if (a == 0) {
a = temp[j];
} else {
if (temp[j] < a) a = temp[j];
}
}
float tt;
if ((a+temp[w])%2 != 0) tt = (a+temp[w])/2+0.5;
else tt = (a+temp[w])/2;
cout << tt << endl;
}
} else {
k = n/2;
pos = partition(temp, 0, n-1);
int p = 0;
int q = n-1;
while (pos != k) {
if (pos > k) {
q = pos-1;
pos = partition(temp, p, pos-1);
} else {
p = pos+1;
pos = partition(temp, pos+1, q);
}
}
cout << temp[pos] << endl;
}
}
int main() {
int n;
cin >> n;
int temp[500000];
for (int i = 0; i < n; i++) cin >> temp[i];
findmedian(temp, n);
return 0;
}那么我开始理解出题人的目的,给出的数组上限是500000,而开一个这么大的数组刚好超内存了,也就意味着我们必须以时间换空间,开比这个小的数组,可以开一个一半的数组,然后先将2/n+1个数以最大堆存在数组中,对于剩下的数,依次读入,如果这个数比堆的顶点大,直接忽略,如果比顶点小,那么取代顶点,这时再次维护堆的结构,当所有的数读完,如果总数n为奇数,那么此时顶点就是中位数了,如果为偶数,那么顶点和顶点孩子的最大值的平均数就是中位数,代码如下:
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<iostream>
using namespace std;
int left(int pa) {
return pa*2;
}
int right(int pa) {
return pa*2+1;
}
void tiaozheng(int* temp, int root, int n) {
int largest;
int l = left(root);
int r = right(root);
if (l <= n && temp[l] > temp[root]) largest = l;
else largest = root;
if (r <= n && temp[r] > temp[largest]) largest = r;
if (largest != root) {
int k = temp[root];
temp[root] = temp[largest];
temp[largest] = k;
tiaozheng(temp, largest, n);
}
}
void build(int* temp, int n) {
for (int i = n/2; i > 0; i--) tiaozheng(temp, i, n);
}
int main() {
int a[250002];
int n;
scanf("%d", &n);
if (n % 2 == 0) {
int aa = n/2;
a[0] = 0;
for (int i = 1; i <= (aa+1); i++) scanf("%d", &a[i]);
build(a, aa+1);
int hello;
for (int j = 0; j < aa-1; j++) {
scanf("%d", &hello);
if (hello < a[1]) {
a[1] = hello;
tiaozheng(a, 1, aa+1);
}
}
printf( "%.1f\n", ((float)a[1] + max(a[3], a[2]))/2);
} else {
int aa = n/2;
a[0] = 0;
for (int i = 1; i <= (aa+1); i++) scanf("%d", &a[i]);
build(a, aa+1);
int hello;
for (int j = 0; j < aa; j++) {
scanf("%d", &hello);
if (hello < a[1]) {
a[1] = hello;
tiaozheng(a, 1, aa+1);
}
}
printf( "%.1f\n", (float)a[1]);
}
} 本来是用C++写的,然而超时,于是在输出输入处使用了C语言,也用了一些技巧可以节省时间,比如避免重复计算n/2,可以计算一次存起来,多次使用,结果中注意保留一位小数!(鄙人的代码写的比较急,简洁性差了一些,见谅)
探讨了在有限内存条件下,如何高效地从大规模无序数组中找出中位数的方法。通过对快速排序思想的改进及堆的应用,实现了既节省内存又能在合理时间内完成任务的解决方案。
Copy sample input to clipboard
1万+

被折叠的 条评论
为什么被折叠?



