Sicily 找中位数(Memory Limit:2MB)

探讨了在有限内存条件下,如何高效地从大规模无序数组中找出中位数的方法。通过对快速排序思想的改进及堆的应用,实现了既节省内存又能在合理时间内完成任务的解决方案。
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
 Copy sample input to clipboard
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,可以计算一次存起来,多次使用,结果中注意保留一位小数!(鄙人的代码写的比较急,简洁性差了一些,见谅)





评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值