在全部具備性能優化的數據結構中,我想你們使用最多的就是hash表,是的,在具備定位查找上具備O(1)的常量時間,多麼的簡潔優美,算法
可是在特定的場合下:數組
①:對10億個不重複的整數進行排序。性能優化
②:找出10億個數字中重複的數字。服務器
固然我只有普通的服務器,就算2G的內存吧,在這種場景下,咱們該如何更好的挑選數據結構和算法呢?數據結構
一:問題分析數據結構和算法
這年頭,大牛們寫的排序算法也就那麼幾個,首先咱們算下放在內存中要多少G: (10億 * 32)/(1024*1024*1024*8)=3.6G,可憐ide
的2G內存直接爆掉,因此各類神馬的數據結構都玩不起來了,固然使用外排序仍是能夠解決問題的,因爲要走IO因此暫時剔除,由於咱們性能
要玩高性能,無望後咱們想一想可不能夠在二進制位上作些手腳? 優化
好比我要對{1,5,7,2}這四個byte類型的數字作排序,該怎麼作呢?咱們知道byte是佔8個bit位,其實咱們能夠將數組中的值做爲bit位的this
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