数据空间与索引

在这个内存GB,硬盘TB级的时代,跟新生代程序员谈节约数据空间似乎有些可笑。因为所面对问题规模不大,程序的性能区别往往难以发现。我们总是只关注小数据正确性

前段时间做了下编程之美的比赛,题目不算难,但是每道题分为大数据和小数据的测试。大数据即参数上万的规模,由于OJ(以及实际情况)的限制,代码运行必须在一定的时间和内存范围内,这就导致了程序可以AC小数据而大数据出现RE。程序的算法可以多样,但是数据的存储和索引却是不变的技术部分。

在当今“大数据”的新环境下,这一方法甚至显得比以前更重要。要知道庞大的互联网增长速度超过指数级,假设我们做的是一个拥有百万用户的系统,程序处理还能像平时练手这样简单随意?

说了很多废话,来看看一个常见的问题:一个平面地图系统,有2000个用户(0...1999),零散分布在平面坐标上(X,Y范围都是0...199)。没有两个用户点坐标相同。需求很简单,指定一个坐标位置,返回该处的用户编号(-1表示没有)


没有什么高级的算法,关键的是数据的处理,怎样建立这个数据结构空间?

我们开始往往会不假思索,上一个二维数组array[200][200],对有用户的坐标赋值其编号,没有则-1,一行代码搞定:

input:x y
output: array[x][y]

算一下空间需求:设一个数字用32位4B表示,总共需要200*200*4B= 160KB,也许这不算大。可是地球区域范围可不是200这么简单。20000呢?将达到1.6GB,足够爆了小型电脑的内存,即使采用16位表示也难救火。

对于数据应该是尽量存有用的,于是可以想到用平行链表来解决


表中colhead表示列号,后面的指针指向的是该列上的第一个用户坐标点,依次链接下去,最后赋NULL

于是查询算法就成为了

for(p=colhead[x];p!=NULL;p=p->next)
  if p->row = y
     return p->pointnum
return -1
这似乎是个比较完美的算法,最坏情况可能访问200个结点得到,而平均需要访问10个结点。开销只需要大约2000*12B+200*4B= 24.8KB
进阶:链表访问比直接访问慢很多,最快的仍是顺序访问

如何只存储必要的数据点,同时又可以只用数组访问?(实际上,malloc的效率并不高,它只是提供了一个回收机制而已,空间和时间开销都比常规要多)

编程珠玑的大神给出了这样一种结构,可以看做是3个数组


初看起来比较难理解:对firstcol数组,下标i代表对应的列号,纪录的值是该列第一个元素出现在row数组中的下标。而row数组则是按列-行优先的顺序依次纪录所有用户点的行号。与之对应的是一个平行数组纪录纪录用户点编号

for k = [firstincol[x],firstincol[x+1])
  if point[row[k]] == y
    return pointnum[k]
return -1

对给定坐标x y ,我们先根据x找到这一列上的用户点的范围,即[firstincol[x],firstincol[x+1]),然后比较这些点的行号是否有对应即可

比如查找点(1,138),首先根据firstincol得到第1列数据组的范围[3,5),然后比较查找row得到返回值15

这一索引方法巧妙利用“数组下标对应”代替“指针索引”方式,实现了空间的最大化压缩和快速查找。不仅可以在空间压缩上更进一步,访问速度也大大提升

总结:

数据空间常用的优化技术有:

不存储,重新计算 因为有时候访问硬盘等介质的开销远大于计算命令。如果需要的数据是可以直接生成的,那就不必耗费磁盘空间了

稀疏数据结构 如例子这样,将关键字作为索引节省开销,快速访问。记录位置信息,很有效!

数据压缩 尽可能用小的数据类型来存储数据,例如行号超过200,就可以用char表示

动态分配&垃圾回收 对不定的数据进行动态分配,一些常量在使用过之后可以同名用作其他功能,而不是再声明一个

代码空间的优化: 使用函数定义,解释程序,是代码更加清晰和易读

--编程珠玑column 10读后感,代码来自原文

-------后记

看了这章关于数据存储,我觉得掌握这些方法是很有意义的,想到前段时间做的管理一个锁字符串的数据结构,当时没多想就直接用链表了,并且string作为比较条件。后经指点改用string的hash实现一次定位,只有hash冲突和字符串相同的时候才会用到低效率的strcmp函数。依此所述,也许还可以优化的更好。以后写程序是得“三思而后行”呢


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值