编程珠玑(一)位图

这个问题是编程之美中第一章开篇提到的问题,bitmap排序。大概意思是这样的,要对n个不重复的整数进行排序,每个数小于n(10的7次方)要求内存1M。其实是区号为800开始的电话号码,800不算在内。这个是典型的位图(bitmap,应该是位映射我觉得,估计是好翻译才把map翻译成图的)。下面是代码:

#include <iostream>
using namespace std;

const int NUM_COUNT = 10000000;
const int MAXLINE = 9; //缓冲区大小,最大读到的为8位整数
const int MASK = 0x1F; //后5位表示在一个int型中的对应的位
const int SHIFT = 5;

inline void setBit(int *a, int num);
inline int getBit(int *a, int num);

int main()
{
        int a[NUM_COUNT/32 + 1] = {0};
        FILE *fp;
        char buf[MAXLINE];
        fp = fopen("./num.txt", "r");
        if(fp)
        {
                while(fgets(buf, MAXLINE, fp) != NULL)
                {
                        int num = atoi(buf);
                        setBit(a, num);
                }
        }

        for(int i=0; i<NUM_COUNT; i++)
        {
                if(getBit(a, i))
                        printf("%d\n", i);
        }
        return 0;
}

inline void setBit(int *a, int num)
{
        a[num>>SHIFT] |= (1<<(num & MASK));
}

inline int getBit(int *a, int num)
{
        return a[num>>SHIFT] & (1<<(num & MASK));
}



编程珠玑后面还有几个习题供思考,简单写下

(1)程序要求有1M空间,但是我们的代码要使用1.25M的空间,如果1MB空间是严格的边界,如何处理?
1M内存空间共有位为1*1024*1024*8=838W,还差170w个位。可以按照数的特征去掉一部分数,例如没有以0和1开头的电话,这样就去掉200w个,就可以满足了(这个是习题解答上给的,没有以1开头的电话么)。还可以用两趟排序来解决。

(2)如果不是每个整数最多出现一次,而是每个整数最多出现10次,如何改进?
这个用四位即可表示0~15,所以用四位表示一个整数即可(可以是每个整数最多出现15次)。代码如下:

#include <iostream>
using namespace std;

const int NUM_COUNT = 10000000;
const int MAXLINE = 9; //缓冲区大小,最大读到的为8位整数
const int MASK = 7; //后3位表示在一个int型中的对应的位
const int SHIFT = 3; //4比特表示一个数,一个32位的整数能保存8个数

inline void addBit(int *a, int num);
inline int getBit(int *a, int num);

int main()
{
        int a[NUM_COUNT/8 + 1] = {0};
        FILE *fp;
        char buf[MAXLINE];
        fp = fopen("./num.txt", "r");
        if(fp)
        {
                while(fgets(buf, MAXLINE, fp) != NULL)
                {
                        int num = atoi(buf);
                        addBit(a, num);
                }
        }

        for(int i=0; i<NUM_COUNT; i++)
        {
                if(int count = getBit(a, i))
                        while(count > 0)
                        {
                                printf("%d\n", i);
                                count--;
                        }
        }
        return 0;
}

inline void addBit(int *a, int num)
{
        //先取出来num对应的四位的值value
        int value_mask = 15<<((num & MASK) * 4);
        int value = a[num>>SHIFT] & value_mask;
        value >>= ((num & MASK) * 4);
        //数目加一,表示多出现一次
        value++;
        //重新设置原来的的4位值
        value <<= ((num & MASK) * 4);
        a[num>>SHIFT] &= ~value_mask;
        a[num>>SHIFT] |= value;
}

inline int getBit(int *a, int num)
{
        int value_mask = 15<<((num & MASK) * 4);
        int value = a[num>>SHIFT] & value_mask;
        value >>= ((num & MASK) * 4);
        return value;
}


 

(3)以前免费电话的区号都是800,现在又有区号为877、888等电话,又该如何按照电话号码排序呢?
两种思路:
1)给每个区号赋予一个权值(为素数),例如800为2,877为3,888为5,用乘积来表示不同区号相同号码出现的情况,例如只出现800的某个电话,则为2,出现800和877的同一个号码,则为6,同理都出现为30。情况一共有八种,一个都没出现、只有一个出现、只有一个没出现、都出现这八种情况,可以用三位来表示,如果对内存有要求,用三位来表示这八种情况即可。这里只是一种思路。当区号较多的时候,素数难找,而且占用空间较大
2)还是位图,每个号码用三位表示即可,每一位对应一个区号。如果内存有要求,从前往后多次排序。

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值