經典算法題每日演練——第十一題 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

最后上總的代碼:

View Code
 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 }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM