bitmap
點陣圖法,用每個bit位儲存狀態(如0/1),用於判斷某個資料是否存在。適用於資料量很大,但狀態不多的情況。
STL中的
bitset
就是點陣圖法。
(1)bitmap原理及實現
#pragma warning(disable : 4996 4800)
#include <stdlib.h>
#include <stdio.h>
#include <memory.h>
#include "math.h"
class Bitmap { //點陣圖bitmap類
private:
char* M; int N; //位元圖所存放的空間M[],容量為N*sizeof(char)*8位元
protected:
void init(int n) //初始化點陣圖空間
{
M = new char[N = (n + 7) / 8]; //申請記憶體
memset(M, 0, N); //初始化記憶體塊
}
public:
Bitmap(int n = 8) //按指定或預設規模建立位元圖(為測試暫時選用較小的預設值)
{
init(n);
}
Bitmap(char* file, int n = 8) //按指定或預設規模,從指定檔案中讀取位元圖
{
init(n);
FILE* fp = fopen(file, "r");
fread(M, sizeof(char), N, fp);
fclose(fp);
}
~Bitmap() //析構時釋放位元圖空間
{
delete[] M; M = NULL;
}
//置位第k個標誌位
void set(int k)
{
expand(k); //拓容
M[k >> 3] |= (0x80 >> (k & 0x07)); //M[第k個標誌位所在的位元組(k/8 取整)] |= (第k個標誌位在所在位元組中的位數(取餘))
}
//復位第k個標誌位
void clear(int k)
{
expand(k); //拓容
M[k >> 3] &= ~(0x80 >> (k & 0x07)); //M[第k個標誌位所在的位元組(k/8 取整)] &= ~(第k個標誌位在所在位元組中的位數(取餘))
}
//取出指定位元組中的指定位
bool test(int k)
{
expand(k); //拓容
return M[k >> 3] & (0x80 >> (k & 0x07)); //M[第k個標誌位所在的位元組(k/8 取整)] &(第k個標誌位在所在位元組中的位的值)
}
//將點陣圖整體匯出至指定的檔案,以便對此後的新點陣圖批次初始化
void dump(char* file)
{
FILE* fp = fopen(file, "w"); fwrite(M, sizeof(char), N, fp); fclose(fp);
}
//將前n位轉換為字串
char* bits2string(int n)
{
expand(n - 1); //此時可能被訪問的最高位為bitmap[n - 1]
char* s = new char[n + 1]; s[n] = '\0'; //字串所佔空間,由上層呼叫者負責釋放
for (int i = 0; i < n; i++) s[i] = test(i) ? '1' : '0';
return s; //返回字串位置
}
//若被訪問的bitmap[k]已出界,則需擴容
void expand(int k)
{
if (k < 8 * N) return; //仍在界內,無需擴容
int oldN = N; char* oldM = M;
init(2 * k); //與向量類似,加倍策略
memcpy_s(M, N, oldM, oldN); delete[] oldM; //原資料轉移至新空間
}
//逐位列印以檢驗點陣圖內容,非必需介面
void print(int n)
{
expand(n);
for (int i = 0; i < n; i++)
printf(test(i) ? "1" : "0");
}
//判斷某個數是否為素數
static bool isPrime(int n) {
if (n <= 3) {
return n > 1;
}
// 不在6的倍數兩側的一定不是質數
if (n % 6 != 1 && n % 6 != 5) {
return false;
}
int s = (int)sqrt(n);
for (int i = 5; i <= s; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
};
(2)std::vector<bool>
但bitset
效率極低,做不了bitmap。
而vector<bool>
在cpp中不是儲存bool的vector,而是被標準庫特化為了位元,極大節省了空間且效率極高。
(3)應用場景
-
有500萬個數字,數字分佈在範圍1000w~1500w內,要求排序且複雜度為O(N),查詢數字且複雜度為O(1)
#include <iostream> #include <stdlib.h> #include <vector> #include <random> #include <string> #define MIN 10000000 //資料下限 #define MAX 14999999 //資料上限(右閉區間) #define COUNT 5000000 //資料個數 int main() { using namespace std; std::random_device rd; //如果可用的話,從一個隨機數發生器上獲得一個真正的隨機數 std::mt19937 gen(rd()); //gen是一個使用rd()作種子初始化的標準梅森旋轉演算法的隨機數發生器 std::uniform_int_distribution<> distrib(MIN, MAX); vector<bool> bitmap(MAX - MIN + 1, false); //全部置0 printf("capacity=%d,size=%d\n", bitmap.capacity(), bitmap.size()); for (int i = 0; i < COUNT; i++) { int index = distrib(gen) - MIN; //原始資料要減去MIN對映到0~MAX-MIN的區間上 bitmap[index] = true; } while (true) { cout << "input:" << endl; int a; cin >> a; if (a<MIN || a>MAX) { cout << to_string(a) << " is not existed..\n"; continue; } if (bitmap[a - MIN]) cout << to_string(a)<< "is existed..\n"; else cout << to_string(a)<< "is not existed..\n"; } /*int count = 0; for(auto item:bitmap) { if (++count == 100) { cout << endl; count = 0; } if (item) printf("1"); else printf("0"); } cout << endl; printf("capacity=%d,size=%d\n", bitmap.capacity(), bitmap.size());*/ return 0; }
#include <iostream> #include <stdlib.h> #include <vector> #include <random> #include <string> #include "Bitmap.h" #define MIN 10000000 #define MAX 14999999 #define COUNT 5000000 int main() { using namespace std; std::random_device rd; //如果可用的話,從一個隨機數發生器上獲得一個真正的隨機數 std::mt19937 gen(rd()); //gen是一個使用rd()作種子初始化的標準梅森旋轉演算法的隨機數發生器 std::uniform_int_distribution<> distrib(MIN, MAX); Bitmap bitmap(COUNT); for (int i = 0; i < COUNT; i++) { int index = distrib(gen) - MIN; bitmap.set(index); } for (int i = 0; i < (MAX - MIN); i++) { if (bitmap.test(i)) { cout << "min=" << i + MIN << endl; break; } } for (int i = MAX - MIN - 1; i >= 0; i--) { if (bitmap.test(i)) { cout << "max=" << i + MIN << endl; break; } } //bitmap.print(COUNT); while (true) { cout << "input:" << endl; int a; cin >> a; if (a<MIN || a>MAX) { cout << to_string(a) << " is not existed..\n"; continue; } if (bitmap.test(a-MIN)) cout << to_string(a)<< "is existed..\n"; else cout << to_string(a)<< "is not existed..\n"; } return 0; }
-
40億個非負整數中算中位數和找出現兩次的數