• Bitmap算法


    在所有具有性能优化的数据结构中,我想大家使用最多的就是hash表,是的,在具有定位查找上具有O(1)的常量时间,多么的简洁优美,

    但是在特定的场合下:

    ①:对10亿个不重复的整数进行排序。

    ②:找出10亿个数字中重复的数字。

    当然我只有普通的服务器,就算2G的内存吧,在这种场景下,我们该如何更好的挑选数据结构和算法呢?

    一:问题分析

         这年头,大牛们写的排序算法也就那么几个,首先我们算下放在内存中要多少G: (10亿 * 32)/(1024*1024*1024*8)=3.6G,可怜

    的2G内存直接爆掉,所以各种神马的数据结构都玩不起来了,当然使用外排序还是可以解决问题的,由于要走IO所以暂时剔除,因为我们

    要玩高性能,无望后我们想想可不可以在二进制位上做些手脚?  

    比如我要对{1,5,7,2}这四个byte类型的数字做排序,该怎么做呢?我们知道byte是占8个bit位,其实我们可以将数组中的值作为bit位的

    key,value用”0,1“来标识该key是否出现过?下面看图:

    从图中我们精彩的看到,我们的数组值都已经作为byte中的key了,最后我只要遍历对应的bit位是否为1就可以了,那么自然就成有序数组了。

    可能有人说,我增加一个13怎么办?很简单,一个字节可以存放8个数,那我只要两个byte就可以解决问题了。

    可以看出我将一个线性的数组变成了一个bit位的二维矩阵,最终我们需要的空间仅仅是:3.6G/32=0.1G即可,要注意的是bitmap排序不

    是N的,而是取决于待排序数组中的最大值,在实际应用上关系也不大,比如我开10个线程去读byte数组,那么复杂度为:O(Max/10)。

    二:代码

      我想bitmap的思想大家都清楚了,这一次又让我们见证了二进制的魅力,当然这些移位都是位运算的工作了,熟悉了你就玩转了。

    1:Clear方法(将数组的所有bit位置0)

       比如要将当前4对应的bit位置0的话,只需要1左移4位取反与B[0] & 即可。

    复制代码
     1         #region 初始化所用的bit位为0
     2         /// <summary>
     3         /// 初始化所用的bit位为0
     4         /// </summary>
     5         /// <param name="i"></param>
     6         static void Clear(byte i)
     7         {
     8             //相当于 i%8 的功能
     9             var shift = i & 0x07;
    10 
    11             //计算应该放数组的下标
    12             var arrindex = i >> 3;
    13 
    14             //则将当前byte中的指定bit位取0,&后其他对方数组bit位必然不变,这就是 1 的妙用
    15             var bitPos = ~(1 << shift);
    16 
    17             //将数组中的指定bit位置一  “& 操作”
    18             a[arrindex] &= (byte)(bitPos);
    19         }
    20         #endregion
    复制代码

    2:Add方法(将bit置1操作)

      同样也很简单,要将当前4对应的bit位置1的话,只需要1左移4位与B[0] | 即可。

    复制代码
     1 #region 设置相应bit位上为1
     2         /// <summary>
     3         /// 设置相应bit位上为1
     4         /// </summary>
     5         /// <param name="i"></param>
     6         static void Add(byte i)
     7         {
     8             //相当于 i%8 的功能
     9             var shift = i & 0x07;
    10 
    11             //计算应该放数组的下标
    12             var arrindex = i >> 3;
    13 
    14             //将byte中的 1 移动到i位
    15             var bitPos = 1 << shift;
    16 
    17             //将数组中的指定bit位置一  “| 操作”
    18             a[arrindex] |= (byte)bitPos;
    19         }
    20         #endregion
    复制代码

    2:Contain方法(判断当前bit位是否是1)

        如果看懂了Clear和Add,我相信最后一个方法已经不成问题了。

    复制代码
     1         #region 判断当前的x在数组的位中是否存在
     2         /// <summary>
     3         ///判断当前的x在数组的位中是否存在
     4         /// </summary>
     5         /// <param name="i"></param>
     6         /// <returns></returns>
     7         static bool Contain(byte i)
     8         {
     9             var j = a[i >> 3] & (1 << (i & 0x07));
    10 
    11             if (j == 0)
    12                 return false;
    13             return true;
    14         }
    15         #endregion
    复制代码

    最后上总的代码:

    复制代码
     1 using System;
     2 using System.Collections.Generic;
     3 using System.Linq;
     4 using System.Text;
     5 using System.Diagnostics;
     6 using System.Threading;
     7 using System.IO;
     8 
     9 namespace ConsoleApplication2
    10 {
    11     public class Program
    12     {
    13         static byte n = 7;
    14 
    15         static byte[] a;
    16 
    17         public static void Main()
    18         {
    19             //节省空间的做法
    20             a = new byte[(n >> 3) + 1];
    21 
    22             for (byte i = 0; i < n; i++)
    23                 Clear(i);
    24 
    25             Add(4);
    26             Console.WriteLine("插入4成功!");
    27 
    28             var s = Contain(4);
    29 
    30             Console.WriteLine("当前是否包含4:{0}", s);
    31 
    32             s = Contain(5);
    33 
    34             Console.WriteLine("当前是否包含5:{0}", s);
    35 
    36             Console.Read();
    37         }
    38 
    39         #region 初始化所用的bit位为0
    40         /// <summary>
    41         /// 初始化所用的bit位为0
    42         /// </summary>
    43         /// <param name="i"></param>
    44         static void Clear(byte i)
    45         {
    46             //相当于 i%8 的功能
    47             var shift = i & 0x07;
    48 
    49             //计算应该放数组的下标
    50             var arrindex = i >> 3;
    51 
    52             //则将当前byte中的指定bit位取0,&后其他对方数组bit位必然不变,这就是 1 的妙用
    53             var bitPos = ~(1 << shift);
    54 
    55             //将数组中的指定bit位置一  “& 操作”
    56             a[arrindex] &= (byte)(bitPos);
    57         }
    58         #endregion
    59 
    60         #region 设置相应bit位上为1
    61         /// <summary>
    62         /// 设置相应bit位上为1
    63         /// </summary>
    64         /// <param name="i"></param>
    65         static void Add(byte i)
    66         {
    67             //相当于 i%8 的功能
    68             var shift = i & 0x07;
    69 
    70             //计算应该放数组的下标
    71             var arrindex = i >> 3;
    72 
    73             //将byte中的 1 移动到i位
    74             var bitPos = 1 << shift;
    75 
    76             //将数组中的指定bit位置一  “| 操作”
    77             a[arrindex] |= (byte)bitPos;
    78         }
    79         #endregion
    80 
    81         #region 判断当前的x在数组的位中是否存在
    82         /// <summary>
    83         ///判断当前的x在数组的位中是否存在
    84         /// </summary>
    85         /// <param name="i"></param>
    86         /// <returns></returns>
    87         static bool Contain(byte i)
    88         {
    89             var j = a[i >> 3] & (1 << (i & 0x07));
    90 
    91             if (j == 0)
    92                 return false;
    93             return true;
    94         }
    95         #endregion
    96     }
    97 }
    复制代码

  • 相关阅读:
    Proguard打包混淆报错:can't find superclass or interface
    proguard returned with error code 1.异常的解决方法
    android 混淆配置
    解决android混淆编译出现Proguard returned with error code 1和文件名、目录名或卷标语法不正确错误
    Eclipse提示No java virtual machine
    [mysql]数据库查询实例
    [算法]高效求素数
    [笔试]程序员面试宝典
    [linux]进程间通信IPC
    [linux]信号的捕获和处理
  • 原文地址:https://www.cnblogs.com/mmcmmc/p/3952391.html
Copyright © 2020-2023  润新知