数据结构之 — bitMap

数据结构之 — bitMap面试官:有多达10亿无序整数,已知最大值为15亿,请对这个10亿个数进行排序,怎么实现 ? 传统排序算法都不可能解决这个排序问题,即便内存允许,其计算时间也是漫长的,如果使用BitMap就极为简单。

面试官:有多达10亿无序整数,已知最大值为15亿,请对这个10亿个数进行排序,怎么实现?

1、先从一个一个简单的问题开始

已知有n整个整数,这些整数的范围是[0, 100],请你设计一种数据结构,使用数组存储这些数据,并提供两种方法,分别是addMember 和 isExist,下面是这种数据结构的类的定义,请你实现它的两个方法

function FindClass(){
    var datas = [];    //存储数据

    // 加入一个整数 member
    this.addMember = function(member){
    };

    // 判断member是否存在
    this.isExist = function(member){
    };
}

var fc = new FindClass();
var arr = [0, 3, 5, 6, 9, 34, 23, 78, 99];
for(var i = 0;i < arr.length;i++){
    fc.addMember(arr[i]);
}

console.log(fc.isExist(3));
console.log(fc.isExist(7));
console.log(fc.isExist(78));

当你实现addMember和isExist之后,执行上面的代码,预期的输出结果是

true
false
true

1.1 毫无难度的实现

这种算法题,没有任何难度,只要会使用数组,就可以实现,先来看addMember的实现

        // 加入一个整数 member
        this.addMember = function(member){
            datas.push(member);
        };

接下来看isExist的实现,我这里提供两种方法

方法1

       // 判断member是否存在
        this.isExist = function(member){
            for(var i = 0;i < datas.length; i++){
                if(datas[i]==member){
                    return true;
                }
            }
            return false;
        };

方法2

        // 判断member是否存在
        this.isExist = function(member){
            if(datas.indexOf(member) >= 0){
                return true;
            }
            return false;
        };

1.2 更快的方法

现在,题目难度升级,前面的实现虽然满足要求,但是速度慢,不论使用for循环查找,还是使用indexOf方法,时间复杂度都是O(n),加入的元素越多,isExist方法的速度越慢,我们需要一个时间复杂度是O(1)的算法,不论向里面增加了多少数据,isExist的执行速度都是常量时间。

通过索引操作数据,时间复杂度就是O(1),题目说明这些数在0到100之间,那么就用每个数自身的值作为索引,比如对于3这个数,可以让data[3] = 1,就表示把3添加进来了,data[2] = 0,表示2没有添加进来,那么这样一来,isExist方法就可以利用索引来判断member是否存在。
实现代码如下:

function FindClass(){
    var datas = new Array(100); //存储数据
    // 先都初始化成0
    for(var i = 0;i < datas.length;i++){
        datas[i] = 0;
    }

    // 加入一个整数 member
    this.addMember = function(member){
        datas[member] = 1;
    };

    // 判断member是否存在
    this.isExist = function(member){
        if(datas[member] == 1){
            return true;
        }
        return false;
    };
}


var fc = new FindClass();
var arr = [0, 3, 5, 6, 9, 34, 23, 78, 99];
for(var i = 0;i < arr.length;i++){
    fc.addMember(arr[i]);
}

console.log(fc.isExist(3));
console.log(fc.isExist(7));
console.log(fc.isExist(78));

刚刚,我们实现了一个更加快速的算法,但这不是终点。

1.3 更节省空间的算法

1.2 中实现的算法,已经很快,但是却面临一个新的问题,如果数据非常多,多达1个亿,每个整数是4个字节,那么一亿个整数数就是4亿个字节,1024字节是1kb,1024kb是1M,4亿个字节就381M的内存空间。

如果没有这么多内存该怎么办?我们需要一种数据压缩的算法,用很少的空间来表示这一亿个数的存在与否。

2. 街边的路灯

街边有8栈路灯,编号分别是1 2 3 4 5 6 7 8 ,其中2号,5号,7号,8号路灯是亮着的,其余的都处于不亮的状态,请你设计一种简单的方法来表示这8栈路灯亮与不亮的状态。

一个非计算机专业的人看到这个问题,多半会嗤之以鼻,这算什么问题,答案是2578,表示这些编号的路灯是亮的,其余的都是不亮的。

而一个计算机专业的人看到这个问题,应该回答说75,因为75的二进制表示是0 1 0 0 1 0 1 1,恰好与路灯亮灭的状态相对应

1 2 3 4 5 6 7 8
0 1 0 0 1 0 1 1

仅仅是8个bit位就能表示8栈路灯的亮灭情况,那么一个整数有32个bit位,就可以表示32栈路灯的亮灭情况,回想我们在第一小节中遇到的问题,isExist方法要判断一个数是否存在,是不是也可以借助这种表达方式呢?

value是一个int类型的数据,初始化成0,当addMember传进来的参数是0的时候,就把value的二进制的第1位设置为1,二进制表示就是

00000000 00000000 00000000 00000001

此时,value = 1
如果又增加了一个3,就把value的二进制的第4为设置为1,二进制表示就是

00000000 00000000 00000000 00001001

此时,value = 9, 9可以表示 0 和 3都存在,一个整数,其实可以表示 0-31 的存在与否,如果我创建一个大小为10的数组,数组里存储整数,那么这个数组就可以表示0~319的存在与否

datas[0]  表示0~31存在与否
datas[1]  表示32~63存在与否
....
datas[9]  表示288~319存在与否

通过这种方式,就可以把空间的使用降低到原来的32分之一,存储一亿个整数的存在与否,只需要12M的内存空间,那么该如何对整数的二进制位进行操作呢。

3. 位运算

内存中的数据,最终的存储方式都是二进制,位运算就是对整数在内存中的二进制位进行操作。

3.1 按位与 &

两个整数进行按位与运算,相同二进制位的数字如果都是1,则结果为1,有一个位为0,则结果为0, 下面是 3 & 7的计算过程

二进制    整数

0 1 1     3
1 1 1     7

0 1 1     3

3 & 7 = 3

3.2 按位或 |

两个整数进行按位或运算,相同二进制位的数字如果有一个为1,则结果为1,都为0,则结果为0, 下面是 5 | 8 的计算过程

二进制        整数

0 1 0 1       5
1 0 0 0       8

1 1 0 1       13

5 | 8 = 13

3.3 左移 <<

二进制向左移动 n 位,在后面添加 n 个0
下面是 3<<1 计算过程。

二进制       整数

 1 1 3 
  1 1 0      6

3<<1 = 6

3.4 小试牛刀

一组数,内容以为 3, 9, 19, 20,请用一个整数来表示这些四个数

var value = 0;
value = value | 1<<3;
value = value | 1<<9;
value = value | 1<<19;
value = value | 1<<20;
console.log(value);

程序输出结果为1573384

4. bitmap

4.1 新的实现方式

经过前面一系列的分析和位运算学习,现在,我们要重新设计一个类,实现addMember和isExist方法,用更快的速度,更少的内存。

  • 数据范围是0到100,那么只需要4个整数就可以表示4*32个数的存在与否,创建一个大小为4的数组
  • 执行addMember时,先用member/32,确定member在数组里的索引(arr_index),然后用member%32,确定在整数的哪个二进制位行操作(bit_index),最后执行bit_arr[arr_index] = bit_arr[arr_index] | 1<<bit_index;
  • 执行isExist时,先用member/32,确定member在数组里的索引(arr_index),然后用member%32,确定在整数的哪个二进制位行操作(bit_index),最后执行bit_arr[arr_index] & 1<<bit_index,如果结果不为0,就说明memeber存在

新的实现方法

function BitMap(size){
    var bit_arr = new Array(size);
    for(var i=0;i<bit_arr.length;i++){
        bit_arr[i] = 0;
    }

    this.addMember = function(member){
        var arr_index = Math.floor(member / 32); // 决定在数组中的索引
        var bit_index = member % 32; // 决定在整数的32个bit位的哪一位上
        bit_arr[arr_index] = bit_arr[arr_index] | 1<<bit_index;
    };

    this.isExist = function(member){
        var arr_index = Math.floor(member / 32); // 决定在数组中的索引
        var bit_index = member % 32; // 决定在整数的32个bit位的哪一位上
        var value = bit_arr[arr_index] & 1<<bit_index;
        if(value != 0){
            return true;
        }
        return false;
    };
}

var bit_map = new BitMap(4);
var arr = [0, 3, 5, 6, 9, 34, 23, 78, 99];
for(var i = 0;i < arr.length;i++){
    bit_map.addMember(arr[i]);
}

console.log(bit_map.isExist(3));
console.log(bit_map.isExist(7));
console.log(bit_map.isExist(78));

程序运行结果为

true
false
true

4.2 概念

不知不觉中,我们实现了一种数据结构,这种数据结构基于位做映射,能够用很少的内存存储数据,和数组不同,它只能存储表示某个数是否存在,可以用于大数据去重,大数据排序,两个集合取交集。

BitMap在处理大数据时才有优势,而且要求数据集紧凑,如果要处理的数只有3个,1, 1000, 100000,那么空间利用率太低了,最大的值决定了BitMap要用多少内存。

4.3 大数据排序

有多达10亿无序整数,已知最大值为15亿,请对这个10亿个数进行排序。
传统排序算法都不可能解决这个排序问题,即便内存允许,其计算时间也是漫长的,如果使用BitMap就极为简单。

BitMap存储最大值为15亿的集合,只需要180M的空间,空间使用完全可以接受,至于速度,存储和比较过程中的位运算速度都非常快,第一次遍历,将10亿个数都放入到BitMap中,第二次,从0到15亿进行遍历,如果在BitMap,则输出该数值,这样经过两次遍历,就可以将如此多的数据排序。

为了演示方便,只用一个很小的数组,[0, 6, 88, 7, 73, 34, 10, 99, 22],已知数组最大值是99,利用BitMap排序的算法如下

var arr = [0, 6, 88, 7, 73, 34, 10, 99, 22];
var sort_arr = [];

var bit_map = new BitMap(4);
for(var i = 0;i < arr.length;i++){
    bit_map.addMember(arr[i]);
}

for(var i = 0;i <= 99;i++){
    if(bit_map.isExist(i)){
        sort_arr.push(i);
    }
}
console.log(sort_arr);

程序输出结果为

[ 0, 6, 7, 10, 22, 34, 73, 88, 99 ]

需要强调的一点,利用BitMap排序,待排序的集合中不能有重复数据

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
转载请注明出处: https://daima100.com/37166.html

(0)

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注