第一章
位图数据结构:这个数据结构主要描述了一个有限定义域内的稠密集合,其中的每一个元素最多出现一次,并且没有其他任何数据与该元素相关联。即使这些条件没有完全 满足(例如,存在重复元素或额外的数据),也可以用有限定义域内的键作为一个表项更复杂的表格的索引。
例如: 可以用一个20位长的字符串来表示一个所有元素都小于20的简单的非负整数集合。
可以用如下字符串表示集合{1,2,3,5,8,13}:
0 1 1 1 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 代表集合中数值的位都置1,其他所有位都置0.
若给定表示文件中的整数集合的位图数据结构,则可以分为三个自然阶段来编写程序。第一个阶段将所有的位都置0,从而将集合初始化为空。第二个阶段通过读入文件中的每个整数来建立集合,将每个对应的位都置1,。第三个阶段检验每个位,如果该位为1,就输出对应的整数,由此产生有序的输出文件。
令n为位向量中的位数,程序的伪代码表示如下:
1 /* phase 1: initialize set to empty */ 2 for i = [0 , n) 3 bit[i] = 0 4 /*phase 2: insert present elements into the set */ 5 for each i in the input file 6 bit[i] = 1 7 /* phase : write sorted output */ 8 for i = [0,n) 9 if bit[i] == 1 10 write i on the output file
练习:
1. 自己写的
1 /************************************************************************* 2 > File Name: 1.cpp 3 > Author: 4 > Mail: 5 > Created Time: 2018年01月07日 星期日 11时21分26秒 6 ************************************************************************/ 7 8 #include<iostream> 9 #include<stdio.h> 10 #include<stdlib.h> 11 #include<algorithm> 12 using namespace std; 13 14 int intcomp(const void *x,const void *y) 15 { 16 return *(int *)x - *(int *)y; 17 } 18 19 int a[1000000]; 20 int main(void) 21 { 22 int i, n = 0; 23 while(scanf("%d",&a[n]) != EOF) 24 n++; 25 qsort(a, n , sizeof(int), intcomp); 26 for(i = 0;i < n; i++) 27 printf("%d\n",a[i]); 28 return 0; 29 }
1 /************************************************************************* 2 > File Name: 1.cpp 3 > Author: 4 > Mail: 5 > Created Time: 2018年01月07日 星期日 11时21分26秒 6 ************************************************************************/ 7 8 #include<iostream> 9 #include<stdio.h> 10 #include<stdlib.h> 11 #include<algorithm> 12 #include<set> 13 using namespace std; 14 15 int main() 16 { 17 set<int> S; 18 int i; 19 set<int>:: iterator j; 20 while(cin >> i) 21 S.insert(i); 22 for(j = S.begin(); j != S.end(); i++) 23 { 24 cout << *j << endl; 25 } 26 return 0; 27 }
2.转载自: http://blog.csdn.net/cherish0222/article/details/53044868
https://www.cnblogs.com/marsdu/p/3181734.html
这是一道非常基础的题目,考察对位运算的理解,好看题目只觉得好眼熟,然后(手贱)瞟了一眼答案,第一遍没看明白答案的内容,就上网查了一下,网上的人要么就是一笔带过(大概是觉得太简单),要么就是误人子弟。
解决题目之前应该先搞清楚题目是干嘛的:
位向量顾名思义就是用位来存储一个数,文中说存储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。
如何使用位逻辑运算实现位向量呢?
起初,看到这个问题,我也有点懵逼,位逻辑运算还是了解一点,可是位向量这个名词还是有点陌生。
所以,想要解决这个问题,就要明确两个概念:位逻辑运算和位向量。
1.位运算:&(按位与)、|(按位或)、>>和<<(移位)、^(异或)、~(取反)、>>>(c中无,java里有)。
位运算实现位向量主要用前三个位运算。
2.位向量:顾名思义,位向量就是用一些二进制位组成的向量。在很多的情况下,我们可以用一个二进制表示一个对象。但是,我们不能直接用一个变量名直接表示一个位(单独一个位组成的数据类型是不存在的),于是,我们就可以考虑将多个位组成基本的数据类型,然后通过对这个基本的数据类型进行操作,从而达到对位进行操作的目的。同时,为了方便,把由位组成的基本数据类型组成数组,这样就可以对一定范围的为位数据集合进行操作了。
3.如何真正的操作位向量:对位向量各位的操作不能直接通过名称去访问, 只能通过位置去操作,也就是我们要操作第几位数。在我们看来,位是由0-n连续的, 实际上,我们是用基本数据类型数组来存储的,因为这些位存在于不同的数组元素之中,分布于不同数组的不同位置当中。假设我们以int类型作为 基本数据类型,则一个int类型可以存储32个位(c++中)。则对于一个特定的位(pos),我们要先求出它处于哪个数组之中(pos/32),然后求出该位在这个数组中具体的位置(pos%32)。
4.对于位的具体操作有三个:对特定位置1,对特定位清0,判断特定位。
5.想要对位进行操作,还要理解下面几个表示:
①m/(2^n) = m>>n;
②m%(2^n) = ( m & 2^(n)-1 );
③将int型变量a的第k位置1: a = ( a | (1 << k) );
④将int型变量a的第k位清0: a = ( a & ~(1 <<k) ).
1 #include<stdio.h> 2 3 #define BITSPERWORD 32 4 #define SHIFT 5 5 #define MASK 0x1F//2^5-1 6 #define N 10000000 7 int a[1 + N/BITSPERWORD]; 8 9 //将第i位置1 10 void set( int i) 11 { 12 a[ i >> SHIFT] = ( a[ i >> SHIFT] | (1 << (i & MASK))); 13 } 14 15 //将第i位清0 16 void clr( int i) 17 { 18 a[ i >> SHIFT] = ( a[ i >> SHIFT] & ~(1 << (i & MASK))); 19 } 20 21 //判断对应位 22 int test( int i) 23 { 24 return a[ i >> SHIFT] & ( 1 << (i & MASK)); 25 } 26 27 int main() 28 { 29 set(1001); 30 if( test(1001)) 31 { 32 printf("true %d\n",test(1001)); 33 } 34 else 35 printf("false\n"); 36 37 clr(1001); 38 if( test(1001)) 39 printf("true\n"); 40 else 41 printf("false\n"); 42 43 44 return 0; 45 }