海量数据排序问题:文件包含1千万条电话号码记录(10**7次方),每条记录都是7位整数,没有重复的整数。要求对文件进行排序,注意大约只有1MB的内存空间可用,有充足的磁盘存储空间可用。请设计一个高效的算法。
(1)运用多趟算法:如果每个号码都使用32位整数来表示,则在1MB存储空间里大约可以存250000个号码。因此,可以使用遍历输入文件40趟的程序来完成排序。在第一趟中,将0至249999之间的任何整数读入内在,对其进行排序后写到输出文件中。第二趟遍历250000至499999之间的整数,依此类推,到第40趟遍历的时候对9750000至9999999之间的整数进行排序。
(2)运用位图数据结构:使用一个具有1000万个位的串(约1.25MB)来表示这个文件,其中当且仅当整数i在文件中存在时,第i位为1。首先将所有的位都置为0,从而将集合初始化为空。然后通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。最后扫描每一位,如果该位为1就输出对应的整数,由此产生有序的输出文件。
海量数据搜索问题:一个顺序文件包含40亿个随机排列的32位整数,找出一个不在文件中的32位整数。
(1)位图技术:如果有足够的内存,可以使用位图技术。使用536870912个8位字节形成的位图来表示文件的整数。通过扫描位图即可找到缺失的整数。
(2)排序技术:通过对文件进行快速排序,我们能够找到缺失的整数。这时总的运行时间正比于nlogn。
(1)运用多趟算法:如果每个号码都使用32位整数来表示,则在1MB存储空间里大约可以存250000个号码。因此,可以使用遍历输入文件40趟的程序来完成排序。在第一趟中,将0至249999之间的任何整数读入内在,对其进行排序后写到输出文件中。第二趟遍历250000至499999之间的整数,依此类推,到第40趟遍历的时候对9750000至9999999之间的整数进行排序。
(2)运用位图数据结构:使用一个具有1000万个位的串(约1.25MB)来表示这个文件,其中当且仅当整数i在文件中存在时,第i位为1。首先将所有的位都置为0,从而将集合初始化为空。然后通过读入文件中的每个整数来建立集合,将每个对应的位都置为1。最后扫描每一位,如果该位为1就输出对应的整数,由此产生有序的输出文件。
#include <stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1+N/BITSPERWORD]; /* 表示1000万个整数的位向量 */
/* 设置整数i所在的位 */
void set(int i){
/* a中每个元素能表示32个整数,因此表示整数i的位是元素a[i/32]中的某个位,
这个位在a[i/32]的左越第i & 5(i的末五位表示的整数)个位处
*/
a[i>>SHIFT] |= (1<<(i & MASK));
}
/* 清除整数i所在的位 */
void clr(int i){
a[i>>SHIFT] &= ~(1<<(i & MASK));
}
/* 测试位向量中是否有整数i */
int test(int i){
return a[i>>SHIFT] & (1<<(i & MASK));
}
int main(void){
int i;
for(i=0;i<N;++i)
clr(i);
while(scanf("%d",&i)!=EOF) //输入要排序的整数
set(i);
for(i=0;i<N;++i)
if(test(i))
printf("%d/n",i);
return 0;
}
(3)排序算法:用C标准库函数qsort,采用的是快速排序。
#include <stdio.h>
#include <stdlib.h>
int intcomp(int *x,int *y){
return *x-*y;
}
int a[10000000];
int main(void){
int i,n=0;
while(scanf("%d",&a[n])!=EOF) //输入要排序的整数
n++;
qsort(a,n,sizeof(int),intcomp);
for(i=0;i<n;++i)
printf("%d\n",a[i]);
return 0;
}
(4)基于集合的排序算法:用C++ STL中的set容器。
#include <cstdio>
#include <iostream>
#include <set>
using namespace std;
int main(void){
set<int> sint;
int i;
set<int>::iterator j;
while(cin>>i)
sint.insert(i); //会进行有序的插入
for(j=sint.begin();j!=sint.end();++j)
cout<<*j<<"\n";
return 0;
}
(1)位图技术:如果有足够的内存,可以使用位图技术。使用536870912个8位字节形成的位图来表示文件的整数。通过扫描位图即可找到缺失的整数。
(2)排序技术:通过对文件进行快速排序,我们能够找到缺失的整数。这时总的运行时间正比于nlogn。
(3)二分搜索技术:如果仅有几百个字节的内存和几个外部的临时顺序文件可用呢?采用二分搜索技术并结合多趟算法。第一遍通过多趟读取40亿个输入整数,并把起始位为0的整数写入一个顺序文件,把起始位为1的整数写入另一个顺序文件。这两个文件中有一个文件最多包含20亿个整数,接下来将该文件用作当前输入并重复探测过程,但这次探测的是第第二个位。如果原始的输入文件包含n元素,那么第一遍将读取n个整数,第二遍最多读取n/2个整数,第三遍最多读取n/4个整数,依此类推,最后我们可以找到缺失的整数。所以总的运行时间正比于n。
BitMap的定义:所谓的Bit-map就是用一个bit位来标记某个元素对应的Value, 而Key即是该元素。由于采用了Bit为单位来存储数据,因此在存储空间方面,可以大大节省。
使用bitmap实现8位电话号码的存储,能够实现电话号码的插入、删除、查找。
使用bitmap算法。8位电话号码总共有0-99999999个号码,每位代表一个电话号码,需要12500000个字节的内存来存储电话号码
实现过程如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define SZLONG_SIZE 5
#define SZLONG_MASK 31UL
#define __BIT_SET_N(n) (0x1<<(n&SZLONG_MASK))
void set_bit(void *base, unsigned long nr)
{
unsigned long *m;
unsigned long mask;
m = (unsigned long *)base + (nr >> SZLONG_SIZE);
mask = __BIT_SET_N(nr);
*m = *m |mask;
}
void clear_bit(void *base, unsigned long nr)
{
unsigned long *m;
unsigned long mask;
m = (unsigned long *)base + (nr >> SZLONG_SIZE);
mask = __BIT_SET_N(nr);
mask = ~mask;
*m = *m&mask;
}
int main()
{
unsigned long value = 0;
set_bit(&value, 0);
set_bit(&value, 30);
printf("0x%08x\n", value);
clear_bit(&value, 0);
clear_bit(&value, 30);
printf("0x%08x\n", value);
return 0;
}
另外一个例子:
bitmap.h
/*
*bitmap的c语言实现
*作者:bitileaf
*时间:2010-12-18 14:12
*/
#ifndef _BITMAP_H_
#define _BITMAP_H_
/*
*功能:初始化bitmap
*参数:
*size:bitmap的大小,即bit位的个数
*start:起始值
*返回值:0表示失败,1表示成功
*/
int bitmap_init(int size, int start);
/*
*功能:将值index的对应位设为1
*index:要设的值
*返回值:0表示失败,1表示成功
*/
int bitmap_set(int index);
/*
*功能:取bitmap第i位的值
*i:待取位
*返回值:-1表示失败,否则返回对应位的值
*/
int bitmap_get(int i);
/*
*功能:返回index位对应的值
*/
int bitmap_data(int index);
/*释放内存*/
int bitmap_free();
#endif
bitmap.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bitmap.h"
unsigned char *g_bitmap = NULL;
int g_size = 0;
int g_base = 0;
int bitmap_init(int size, int start)
{
g_bitmap = (char *)malloc((size/8+1)*sizeof(char));
if(g_bitmap == NULL)
return 0;
g_base = start;
g_size = size/8+1;
memset(g_bitmap, 0x0, g_size);
return 1;
}
int bitmap_set(int index)
{
int quo = (index-g_base)/8 ;
int remainder = (index-g_base)%8;
unsigned char x = (0x1<<remainder);
if( quo > g_size)
return 0;
g_bitmap[quo] |= x;
return 1;
}
int bitmap_get(int i)
{
int quo = (i)/8 ;
int remainder = (i)%8;
unsigned char x = (0x1<<remainder);
unsigned char res;
if( quo > g_size)
return -1;
res = g_bitmap[quo] & x;
return res > 0 ? 1 : 0;
}
int bitmap_data(int index)
{
return (index + g_base);
}
int bitmap_free()
{
free(g_bitmap);
}
测试程序bitmap_test.c:
#include <stdio.h>
#include "bitmap.h"
int main()
{
int a[] = {5,8,7,6,3,1,10,78,56,34,23,12,43,54,65,76,87,98,89,100};
int i;
bitmap_init(100, 0);
for(i=0; i<20; i++)
bitmap_set(a[i]);
for(i=0; i<100; i++)
{
if(bitmap_get(i) > 0 )
printf("%d ", bitmap_data(i));
}
printf("/n");
bitmap_free();
return 0;
}