楼主酷爱王者,但是由于忙于业务,王者有一段时间没玩了,待再次上线的时候,TM发来了一封邮件,亲爱的召唤师,欢迎回归王者荣耀,你已有88日没有登录过游戏,这是为你精心准备的回归大礼包,礼包是一些体验卡。作为一名程序员,让楼主更在意的是88这个数字的统计方式。
我们知道王者荣耀用户数很多,假如有一亿用户,如何来记录用户的登录信息,如何来查询活跃用户(如一周内登录三次的),最常规的做法就是建一张用户登录信息表,有用户ID,有登录时间这样的,然后用户每登录一次就往表中插入一条数据,没毛病,那么假设一天之内有1亿用户登录,那么2天表中就会有2亿数据,这里会有很严重的问题,首先表中不可能承载这么多数据量,其次就算可以装得下这么多数据,那你怎么统计这么多数据的表?效率性能如何。所以在数据库层面是不好解决的。
因此,我们不妨设置用一个1bit位来标识用户的登录状态,1/0,1是代表登录,0是代表没登录,那么可以建立如下的模型
假设有10个用户,统计一周之内用户的登录次数
星期一:0000011111
星期二:1001011011
星期三:1001011111
这样就标识着星期一这天后5个用户登录了,前5个用户没登录,星期二和三同理,清晰可见,便于统计。这里的数字是一个字符串或者是int【】数组,这是一种思路
下面进入主题,位图算法的介绍
简单的说就是用数组存放若有数据就标志为1或true,若不存在标志为0或false。比如1,2,2,5,这里最大值为5,0至5中不存0,3,4,所以:
Array[0]=0,Array[1]=1,Array[2]=2,Array[3]=0,Array[4]=0,Array[5]=1
int[] ={0,1,2,0,0,1}
上面数中由于2有两个,所以用int存数组的值,不用boolean型,这样如果有多个同样的数字可以用值表示个数。如上面Array[2]=2,就表示2有2个。
假设我们有{15,10,7,6,5,4,1}这数组,在位图中数据结构是这样的
这种算法的缺点在于,最大值和最小值之间不能相差太大,否则浪费空间。
应用:
1.判断一个数是否存在某数据中,假如有40亿数据,我们如何快速判断指定一个数是否存在
申请512M的内存 512M=512*1024*1024bit
一个bit位代表一个int值
读入40亿个数,设置相应的bit位
读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在
2.判断整形数组是否重复
它的做法是按照集合中最大元素max创建一个长度为max+1的新数组,然后再次扫描原数组,遇到几就给新数组的第几位置上1,如遇到 5就给新数组的第六个元素置1,这样下次再遇到5想置位时发现新数组的第六个元素已经是1了,这说明这次的数据肯定和以前的数据存在着重复。它的运算次数最坏的情况为2N。如果已知数组的最大值即能事先给新数组定长的话效率还能提高一倍。
3.给数组排序
首先遍历数组,得到数组的最大最小值,然后根据这个最大最小值来缩小bitmap的范围。这里需要注意对于int的负数,都要转化,而且取位的时候,数字要减去最小值。
给出JAVA代码
public class WeiTu { public static int[] bitmapSort(int[] arr) { // 找出数组中最值 int max = arr[0]; int min = max; for (int i : arr) { if (max < i) { max = i; } if (min > i) { min = i; } } //初始化位图数组大小 int temp=0;//用于解决数组有负数的情况 int[] newArr=null; if(min<0){ temp=0-min; newArr = new int[max - min + 1]; }else{ newArr = new int[max+1]; min=0; } //构建位图 for(int i:arr){ newArr[i+temp]++;//算法体现 } // 重新调整arr数组中的元素 int index = 0; for (int i = 0; i < newArr.length; i++) { while (newArr[i] > 0) { arr[index] = i + min; index++; newArr[i]--; } } return arr; } public static void main(String[] args) { int[] arr={5,2,3,7,1}; //int[] arr={-5,2,-3,7,1}; int[] arrsort=bitmapSort(arr); for(int i:arrsort) System.out.println(i); } }