1. 程式人生 > >【資料結構】點陣圖BitMap

【資料結構】點陣圖BitMap

給40億個不重複的無符號整數,沒拍過序。給定一個無符號整數,如何可以高效的判斷是否存在這些資料中。

直接的想法是,我們將這些的無符號整數儲存到記憶體中,然後用給定的數進行一一比較。

我們分析一下,一個無符號整數是4個位元組,40億* 4個位元組,10億個位元組大概就是4個G,40億個無符號整數就是16G

計算機記憶體一般沒這麼大,就算有這麼大,也要分給作業系統什麼的吧,直接儲存整形不好實現;

再說,這對比一遍,如果不存在,那麼要比較40億多次,效率太低了

現在,我們可以用點陣圖---BitMap來解決這一問題。即節省空間,又可以快速的解決這個問題

點陣圖的概念

點陣圖是用陣列的每一個元素的每一個二進位制位來表示資料的存在與否;

0表示不存在,1則表示存在

點陣圖的優勢

點陣圖可以快速的判斷一個儲存的關鍵字是否存在

並且40億個整數,一個整數只佔一個位,空間佔用率比之前少了31倍,只需要大概500M左右的樣子。

點陣圖的剖析

步驟1 選用一種我們已學過的資料結構

點陣圖需要的是二進位制位,通過用二進位制位的0,1來表示一個數是否存在

我們用vector來儲存這些數,可以將vector放入任意基本型別;

步驟2 找到對應的位

這次我們放入size_t 無符號整形,有32個位元位。也就是說,一個vector的一個對應下標可以表示32個數是否存在;

	vector<size_t> _v;//儲存各個數的狀態
	size_t _size;//標誌有多少個數存入
現在給我們一個數,我們用它除以32,得到的商就是對應的下標,得到的餘數就是該對應下標中的第幾個位元位;


步驟3 考慮如何將該位置為1

我們知道二進位制的或運算,可以將一個位置為1;


置位的程式碼

	//放入,將num的所在位置為1
	void Set(size_t num)
	{
		//index用來判斷在vector的第幾個元素中
		//value表示在該元素中從低位向高位數具體的哪一位
		//index用右移5位,相當於除32,用移位運算效率高
		int index = num >> 5;
		int value = num % 32;

		//利用或運算,將該位置為1
		_v[index] |= 1 << (value-1);
		_size++;
	}

步驟4 如何重置,將一個數在點陣圖中抹去

還是用位運算,這次用異或;

相同為0,相異為1;

	//移出,將num的所在位置為0
	void Reset(size_t num)
	{
		//index用來判斷在vector的第幾個元素中
		//value表示在該元素中從低位向高位數具體的哪一位
		//index用右移5位,相當於除32,用移位運算效率高
		int index = num >> 5;
		int value = num % 32;

		//利用異或運算,將該位置為0
		_v[index] ^= 1 << (value-1);
		_size--;
	}

步驟5 判斷一個數是否存在於點陣圖中

依舊位運算,用與,都為1則為1,否則為0

	//用來判斷一個數是否已經存入點陣圖中
	bool Find(size_t num)
	{
		int index = num >> 5;
		int value = num % 32;

		//判斷該位是否為1
		return (_v[index] >> (value-1)) & 1;
	}

原始碼

#include<vector>

//定義點陣圖
class BitMap
{
public:
	//點陣圖的建構函式
	BitMap(size_t size = 1024)
		:_size(0)
	{
		//一個無符號整形(size_t)可以有32個位分別表示32個數
		_v.resize(size / 32 + 1);
	}

	//放入,將num的所在位置為1
	void Set(size_t num)
	{
		//index用來判斷在vector的第幾個元素中
		//value表示在該元素中從低位向高位數具體的哪一位
		//index用右移5位,相當於除32,用移位運算效率高
		int index = num >> 5;
		int value = num % 32;

		//利用或運算,將該位置為1
		_v[index] |= 1 << (value-1);
		_size++;
	}

	//移出,將num的所在位置為0
	void Reset(size_t num)
	{
		//index用來判斷在vector的第幾個元素中
		//value表示在該元素中從低位向高位數具體的哪一位
		//index用右移5位,相當於除32,用移位運算效率高
		int index = num >> 5;
		int value = num % 32;

		//利用異或運算,將該位置為0
		_v[index] ^= 1 << (value-1);
		_size--;
	}

	//用來判斷一個數是否已經存入點陣圖中
	bool Find(size_t num)
	{
		int index = num >> 5;
		int value = num % 32;

		//判斷該位是否為1
		return (_v[index] >> (value-1)) & 1;
	}
protected:
	vector<size_t> _v;
	size_t _size;
};

點陣圖的一些應用

應用1 利用點陣圖進行排序

將這些數遍歷一遍,放入點陣圖的對應位置上。然後從低位到高位遍歷一遍每一個位元位;

缺陷是對於負數,要轉化成無符號的整數,輸出的時候再減去最小值即可

應用2 判斷是否出現重複

遍歷這些數,如果不存在則放入點陣圖中,否則則找到了重複的元素。