数据结构与算法专题——第一题 Bitmap算法
在所有具有性能優(yōu)化的數(shù)據(jù)結(jié)構(gòu)中,我想大家使用最多的就是hash表,是的,在定位查找場景上具有O(1)的常量時(shí)間,多么的簡潔優(yōu)美,
但是在特定的場合下:
①:對10億個不重復(fù)的整數(shù)進(jìn)行排序。
②:找出10億個數(shù)字中重復(fù)的數(shù)字。
當(dāng)然我只有普通的服務(wù)器,就算2G的內(nèi)存吧,在這種場景下,我們該如何更好的挑選數(shù)據(jù)結(jié)構(gòu)和算法呢?
一:問題分析
這年頭,大牛們寫的排序算法也就那么幾個,首先我們算下放在內(nèi)存中要多少G: (10億?32)/(1024102410248)=3.6G,可憐的2G內(nèi)存直接爆掉,所以各種神馬的數(shù)據(jù)結(jié)構(gòu)都玩不起來了,當(dāng)然使用外排序還是可以解決問題的,由于要走IO所以暫時(shí)剔除,既然是數(shù)據(jù)結(jié)構(gòu)和算法,所以高性能是必須的,想想可不可以在二進(jìn)制位上做些手腳?比如我要對?{1,5,7,2}?這四個byte類型的數(shù)字做排序,該怎么做呢?大家知道byte是占用8bit,轉(zhuǎn)變思路可以將數(shù)組中的值作為bit位的key,value用”0,1“來標(biāo)識該key是否出現(xiàn)過?下面看圖:
從圖中我們精彩的看到,數(shù)組值都已經(jīng)作為byte中的key了,最后我只要遍歷對應(yīng)的bit位是否為1就可以了,那么自然就成了有序數(shù)組。可能有人說,我增加一個13怎么辦?很簡單,一個字節(jié)可以存放8個數(shù),那兩個byte是不是就可以存16個數(shù)?剛好了覆蓋了13個,畫圖如下:
可以看出我將一個線性的數(shù)組變成了一個bit位的二維矩陣,最終需要的空間僅僅是:3.6G/32=0.1G即可,要注意的是bitmap排序不是O(N)的,而是取決于待排序數(shù)組中的最大值,有時(shí)候必須要加速的話,可以開10個線程去讀byte數(shù)組,那么復(fù)雜度為:O(Max/10)。
二:代碼
我想bitmap的思想大家都清楚了,這一次又讓我們見證了二進(jìn)制的魅力,當(dāng)然這些移位都是位運(yùn)算的工作了,熟悉了你就玩轉(zhuǎn)了。
1:Clear方法(將數(shù)組的所有bit位賦值0)
比如要將當(dāng)前4對應(yīng)的bit位賦值0的話,只需要1左移4位取反與B[0] & 即可。
/// <summary>/// 初始化所用的bit位為0/// </summary>/// <param name="i"></param>static void Clear(byte i){//相當(dāng)于 i%8 的功能var shift = i & 0x07;//計(jì)算應(yīng)該放數(shù)組的下標(biāo)var arrindex = i >> 3;//則將當(dāng)前byte中的指定bit位取0,&后其他對方數(shù)組bit位必然不變,這就是 1 的妙用var bitPos = ~(1 << shift);//將數(shù)組中的指定bit位置一 “& 操作”a[arrindex] &= (byte)(bitPos);}2:Add方法(將bit置1操作)
同樣也很簡單,要將當(dāng)前4對應(yīng)的bit位賦值1的話,只需要1左移4位與B[0] | 即可。
/// <summary>/// 設(shè)置相應(yīng)bit位上為1/// </summary>/// <param name="i"></param>static void Add(byte i){//相當(dāng)于 i%8 的功能var shift = i & 0x07;//計(jì)算應(yīng)該放數(shù)組的下標(biāo)var arrindex = i >> 3;//將byte中的 1 移動到i位var bitPos = 1 << shift;//將數(shù)組中的指定bit位置一 “| 操作”a[arrindex] |= (byte)bitPos;}3:Contain方法(判斷當(dāng)前bit位是否是1)
如果看懂了Clear和Add,我相信最后一個方法已經(jīng)不成問題了。
/// <summary>///判斷當(dāng)前的x在數(shù)組的位中是否存在/// </summary>/// <param name="i"></param>/// <returns></returns>static bool Contain(byte i){var j = a[i >> 3] & (1 << (i & 0x07));if (j == 0)return false;return true;}4. 最后上總的代碼:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Diagnostics;using System.Threading;using System.IO;namespace ConsoleApplication2{public class Program{static byte n = 7;static byte[] a;public static void Main(){//節(jié)省空間的做法a = new byte[(n >> 3) + 1];for (byte i = 0; i<n; i++)Clear(i);Add(4);Console.WriteLine("插入4成功!");var s = Contain(4);Console.WriteLine("當(dāng)前是否包含4:{0}", s);s = Contain(5);Console.WriteLine("當(dāng)前是否包含5:{0}", s);Console.Read();}#region 初始化所用的bit位為0/// <summary>/// 初始化所用的bit位為0/// </summary>/// <param name="i"></param>static void Clear(byte i){//相當(dāng)于 i%8 的功能var shift = i & 0x07;//計(jì)算應(yīng)該放數(shù)組的下標(biāo)var arrindex = i >> 3;//則將當(dāng)前byte中的指定bit位取0,&后其他對方數(shù)組bit位必然不變,這就是 1 的妙用var bitPos = ~(1 << shift);//將數(shù)組中的指定bit位置一 “& 操作”a[arrindex] &= (byte) (bitPos);}#endregion#region 設(shè)置相應(yīng)bit位上為1/// <summary>/// 設(shè)置相應(yīng)bit位上為1/// </summary>/// <param name="i"></param>static void Add(byte i){//相當(dāng)于 i%8 的功能var shift = i & 0x07;//計(jì)算應(yīng)該放數(shù)組的下標(biāo)var arrindex = i >> 3;//將byte中的 1 移動到i位var bitPos = 1 << shift;//將數(shù)組中的指定bit位置一 “| 操作”a[arrindex] |= (byte) bitPos;}#endregion#region 判斷當(dāng)前的x在數(shù)組的位中是否存在/// <summary>///判斷當(dāng)前的x在數(shù)組的位中是否存在/// </summary>/// <param name="i"></param>/// <returns></returns>static bool Contain(byte i){var j = a[i >> 3] & (1 << (i & 0x07));if (j == 0)return false;return true;}#endregion}}非常簡單的一個數(shù)據(jù)結(jié)構(gòu),您學(xué)會了嗎?
總結(jié)
以上是生活随笔為你收集整理的数据结构与算法专题——第一题 Bitmap算法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数据结构与算法专题——第二题 优先队列
- 下一篇: 酸吗?28岁程序员财务自由宣布退休!