第一章

Posted PrayG

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第一章相关的知识,希望对你有一定的参考价值。

位图数据结构:这个数据结构主要描述了一个有限定义域内的稠密集合,其中的每一个元素最多出现一次,并且没有其他任何数据与该元素相关联。即使这些条件没有完全                            满足(例如,存在重复元素或额外的数据),也可以用有限定义域内的键作为一个表项更复杂的表格的索引。

例如: 可以用一个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 }  
位向量的实现

 

以上是关于第一章的主要内容,如果未能解决你的问题,请参考以下文章

《大道至简》第一章读后感及伪代码

大道至简第一章java伪代码读后感

第一章读书笔记

大道至简第一章伪代码

大道至简第一章伪代码

编译技术图示(第一章 编译概述)