目錄
創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站制作、做網(wǎng)站、外貿(mào)營銷網(wǎng)站建設與策劃設計,云龍網(wǎng)站建設哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設十載,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:云龍等地區(qū)。云龍做網(wǎng)站價格咨詢:028-869222201.位圖
1.1什么是位圖?
1.2位圖的作用?
2.bitset應用
2.1bitset構造
2.2bitset成員函數(shù)與使用?
3.bitset模擬實現(xiàn)?
構造函數(shù)?
set
reset
test
flip
count?
size?
none,any
在前文中我們介紹了哈希的一些內容,接下來我們介紹一個新奇的玩意,位圖。?
1.1什么是位圖?1.2位圖的作用?
- 位圖其實就是哈希的變形,同樣通過映射來處理數(shù)據(jù)。
- 位圖本身并不存儲數(shù)據(jù),而是存儲標記通過一個比特位來標記這個數(shù)據(jù)是否存在,1代表存在,0代表不存在。
- 位圖通常情況下用在數(shù)據(jù)量龐大,且數(shù)據(jù)不重復的情景下判斷某個數(shù)據(jù)是否存在。
相比通過上面的介紹,各位還是不知道位圖有啥作用。接下來我們說一下位圖的作用。
2.bitset應用 ?2.1bitset構造騰訊出過一個面試題:?給40億個不重復的無符號整數(shù),沒排過序。給一個無符號整數(shù),如何快速判斷一個數(shù)是否在這40億個數(shù)中。
1整數(shù)=4字節(jié)=32bit。 40億個整數(shù) = 160億個字節(jié) 而1GB = 1024MB 1024MB = 1024 * 1024KB 1024 * 1024KB = 1024 * 1024 * 1024B(yte )≈ 10億字節(jié) 綜上1GB ≈ 10億字節(jié) ? ?16GB ≈ 160億字節(jié)
遍歷時間復雜度O(N);排序(O(NlogN))利用二分查找: logN;這兩種方式除了效率不夠高,還有個問題是內存無法完全同時加載40億個整數(shù)需要的16G大小空間。
我們也可以用set容器,但是底層的紅黑樹存放這么多數(shù)據(jù)也吃不消所以我們用位圖來解決這個問題。
1個整形是32bit位,就比如整數(shù)在計算機中以二進制來存儲的。而這32個位置上只有0或1,那我們可以讓1表示有數(shù)據(jù),0表示沒存放數(shù)據(jù)。我們以32比特位表示一個單元。
如果采用這種方法,只需占500M內存就可以:
1G = 2^30Byte ≈ 10億字節(jié) (2^32 - 1)Byte ≈ 40億字節(jié) ≈ 4G 1Byte = 8bit (2^32 - 1)bit = 4G/8 ≈ 500MB
例如:int a[]={2,5,7,19,24,30}
2.2bitset成員函數(shù)與使用?位圖構造有三種形式,這里我們直接上代碼。
void testbitset1() { //1.創(chuàng)建一個16位位圖,初始0 std::bitset<16>foo; // 創(chuàng)建一個16位位圖 //2.賦值16進制 std::bitset<16>bar(0xfa2); //16進制 4002 //3.用0,1賦值或者string std::bitset<16>baz(std::string("0101111001")); std::bitset<16>bs1("011010001"); //foo : 0000000000000000 //bar : 0000111110100010 4002 //baz : 0000000101111001 //bs1 : 0000000011010001 std::cout<< "foo: "<< foo<< '\n'; std::cout<< "bar: "<< bar<< '\n'; std::cout<< "baz: "<< baz<< '\n'; std::cout<< "bs1: "<< bs1<< '\n'; }
3.bitset模擬實現(xiàn)?
成員函數(shù)? ? ? 功能 set? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 設置指定位或所有位 reset?? 清空指定位或所有位 test?? 獲取指定位的狀態(tài) flip?? 反轉指定位或所有位 count? ?獲取被設置位的個數(shù) size?? 獲取可以容納的位的個數(shù) any? ?? 如果有任何一個位被設置則返回true none? ?? 如果沒有位被設置則返回true all? ?? 如果所有位都被設置則返回true void testbitset2() { std::bitset<16>bs1; bs1.set(2); //0000000000000100 cout<< bs1<< endl; bs1.set(4); bs1.set(5); //0000000000110100 cout<< bs1<< endl; //獲取指定位置的狀態(tài) cout<< bs1.test(5)<< endl; //位置5的狀態(tài)是1 cout<< bs1.test(10)<< endl; //位置10的狀態(tài)是0 //反轉 cout<< bs1.flip(4)<< endl; //反轉第4位 cout<< bs1.flip()<< endl; //反轉所有位 //清空 cout<< bs1.reset(10)<< endl; //清空第10位 cout<< bs1.reset()<
bitset運算符那個就不講了。我們直接模擬實現(xiàn)一下。
構造函數(shù)?
- 模擬bitset就是用一個普通的數(shù)組來存儲數(shù)據(jù)以達到模擬的目的。
- 一個單元我們分成4個小組,每個小組8個位置。存放一個數(shù)據(jù)要先判斷在那個單元,然后是那個小組,那個位置。
- 找到對應單元后,數(shù)據(jù)/8=對應小組;數(shù)據(jù)%8=對應位置。
- 如果我們以一個整型作為比特位的容器,那么如果要求0~N范圍的比特位,就需要有
N/32+1
個整型來容納這些比特位,同理如果以char為容器,則需要N/8+1
個char來容納N個比特位。這里我們用vector數(shù)組作為底層容納比特位的容器。之所以+1是怕開的空間存不下,所以多開一個,就算剩下也剩不了多少。namespace cpp { //N個比特位的位圖 template
class bitset { public: //構造函數(shù) bitset(); //把value映射的位標記成1 void set(size_t value); //把value映射的位標記成0 void reset(size_t value); //判斷指定位value的狀態(tài)是否為1 bool test(size_t value); //翻轉指定位置 void flip(size_t value); //獲取位圖中可以容納位N的個數(shù) size_t size() //統(tǒng)計set中1的位數(shù) size_t count(); //判斷所有比特位若無置為1,返回true bool none(); //判斷位圖中是否有位被置為1,若有則返回true bool any(); //全部NUM個bit位被set返回true bool all(); private: vector _bits;//位圖 }; } 這里我們用的存儲類型是char。
set構造還是構造,但是文章已不是當年的文章了。
public : //構造函數(shù) bit_set { _bits.resize(N / 8 + 1,0); // 全部賦值0 }
之所以+1是因為如果數(shù)據(jù)的個數(shù)不是8的倍數(shù)時,可以再存進去,要不那多余的數(shù)據(jù)咋弄?
reset把映射的位置標記為1(把設置位置標記為1)。
例如:value=13;
//映射的位置標為1 void set(size_t value) { size_t i = value / 8; //找到小組 size_t j = value % 8; //找到位置 //把對應位置標記為1 _bits[i] |= 1<< j; //或運算,有1則1 }
test清空指定位或所有位?
這個和set正好相反,這個是把指定位置的數(shù)變成0,最后一步變化,其他不變,先按位取反,在按位與即可。j<<1按位取反后變成1,在按位與_bits[i]后就設置成0了。
//把value映射的位標記成0 void reset(size_t value) { size_t i = value / 8; //找到小組 size_t j = value % 8; //找到位置 //把對應位置標記為1 _bits[i] &=~(( 1<< j)); //先按位取反在與運算 }
flip判斷指定位置是否為1
這個跟上面的reset就缺少一個按位取反。只進行一個按位與運算即可
//判斷指定位value的狀態(tài)是否為1 bool test(size_t value) { size_t i = value / 8; //找到小組 size_t j = value % 8; //找到位置 //把對應位置標記為1 _bits[i] &= (1<< j); }
count?指定位置數(shù)據(jù)翻轉:?如果是1變成0,0變成1。
這個按位異或就行了 ,相同為0,1變成0,相異為1,0^1=1
//翻轉指定pos void flip(size_t value) { size_t i = value / 8; //找到小組 size_t j = value % 8; //找到位置 //把對應位置標記為1 _bits[i] ^= (1<< j); }
size?統(tǒng)計位圖中出現(xiàn)的1的次數(shù)?
這個跟前面幾個就不一樣了。
這個是n&n-1
size_t count() { size_t count = 0; for (auto e : _bits) { int n = e; //再設置個中間變量,別直接用e while (n) { n = n & (n - 1); count++; } } return count; }
none,anysize的作用是獲取位圖中可以容納位N的個數(shù)
size_t size() { return N; }
none是查找位圖中是否全部是0,是就返回true,不是返回false。
any是查找位圖中是否有1,是就返回true,沒有返回false。any可以復用none。完整
bool none() { //遍歷每個char for (auto e : _bits) { if (e != 0)//位圖中有位置被置為1,返回false return false; } return true;//說明全為0,返回true } //若有位置為1,返回true bool any() { return !none; }
完整代碼可到gitee查看
C++ 進階: 本倉庫存放一些較難C++代碼https://gitee.com/j-jun-jie/c---advanced.git
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧