• 第一章


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

    例如: 可以用一个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
    ",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
    ",test(1001));  
    33     }  
    34     else  
    35         printf("false
    ");  
    36   
    37     clr(1001);  
    38     if( test(1001))  
    39         printf("true
    ");  
    40     else  
    41         printf("false
    ");  
    42   
    43   
    44     return 0;  
    45 }  
    位向量的实现
  • 相关阅读:
    【洛谷P1962】斐波那契数列
    【洛谷P3390】【模板】矩阵快速幂
    【洛谷P3070】[USACO13JAN]岛游记Island Travels
    【NOIp模拟赛】antipalindrome
    【洛谷P3930】SAC E#1
    【洛谷P3928】SAC E#1
    【洛谷P3927】SAC E#1
    【NOIp模拟赛】binary
    【NOIp模拟赛】value
    【洛谷P2341】[HAOI2006]受欢迎的牛
  • 原文地址:https://www.cnblogs.com/PrayG/p/8227965.html
Copyright © 2020-2023  润新知