《编程珠玑》一书中习题1.2中如何使用位逻辑运算来实现位向量的理解

这是一道非常基础的题目,考察对位运算的理解,乍看题目只觉得好眼熟,然后(手贱)瞟了一眼答案,第一遍没看明白答案的内容,就上网查了一下,网上的人要么就是一笔带过(大概是觉得太简单),要么就是误人子弟。

解决题目之前应该先搞清楚题目是干嘛的:

位向量顾名思义就是用位来存储一个数,文中说存储N=10000000个数,每一位代表一个数。

我们可以定义一个int类型的数组int a[N],那么如果a[9]的值为1,则表明文件中存在一个值为9。

这样的话,我们就可以用一个数组来表示这么多数。我们又知道,一个int型的数有4个字节,也就是32位,那么我们可以用N/32个int型数来表示这N个数:

a[0]表示第1~32个数(0~31)

a[1]表示第33~64个数(32~63)

这样,每当输入一个数字i,我们应该先找到该数字在数组的第几个元素中,也就是a[?],然后再确定在这个元素的第几位中。

举个例子来说,比如输入35,那么35/32为1余3,则应该将a[1]的第4位置为1。

好,有了上面的概念,可以先来看看题中set是怎么实现的:

void set(int i)
{
    a[i>>SHIFT] |= (1<<(i &MASK));
}

根据题目的要求,我们不可以用/运算符来设计程序,那除的话我们可以用右移来替代:

m>>n,表示m往右移动n位

输入i,除以32相当于往右移动5位,则i>>SHIFT代表i/32得到应该放在数组的第几个元素中,然后要置相应的位置位1了:

先来看看1<<(i&MASK)是什么意思。i&MASK相当于取i右移掉的部分,说白了就是取余数。

比如35的二进制表示是:… 0010 0011

MASK的二进制是0001 1111

两个相与操作得到0 0011

而右移5位,移掉的数是0 0011,换算成10进制是3,正是余数,与上面的操作值相等,都是0 0011。

因此1<<(i&MASK)就变成了1<<3,也就是将1右移3位,变成了1000。

然后在做一个|操作就将a[1]的第4位置1了。

对于clr函数,就是找到位置,然后清零

对于test函数,就是找到位置,做一个与操作,如果存在这个数,则返回1,不存在的话,因为是&操作,所以返回0。

下面是所有的答案:

复制代码

#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1+N/BITSPERWORD];
void set(int i)
{
    a[i>>SHIFT] |= (1<<(i &MASK));
}
void clr(int i )
{
    a[i>>SHIFT] &= ~(1<<(i &MASK));
}
int test(int i )
{

复制代码

可以写一个main函数测试一下:

 

复制代码

#include <iostream>//不要忘记它
using namespace std;//不要忘记它
int main(){
    int i = 35;
    //设置i,也就是置相应位置位1
    set(i);
    //测试是否置1了
    if(test(i))
        cout<<"ok"<<endl;
    return 0;
}

复制代码

image

大概就这样了。

(原文地址:http://www.cnblogs.com/marsdu/p/3181734.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值