數據結構之位圖和布隆過濾器

系列文章目錄

數據結構之ArrayList_arraylist o(1) o(n)-CSDN博客

數據結構之LinkedList-CSDN博客

數據結構之棧_棧有什么方法-CSDN博客

數據結構之隊列-CSDN博客

數據結構之二叉樹-CSDN博客

數據結構之優先級隊列-CSDN博客

常見的排序方法-CSDN博客

數據結構之Map和Set-CSDN博客

數據結構之二叉平衡樹-CSDN博客


目錄

系列文章目錄

前言

一、位圖

1. 位圖的概念

2. 位圖的模擬實現

3. 位圖的應用

二、布隆過濾器

1. 布隆過濾器的概念

2. 布隆過濾器的模擬實現

3. 布隆過濾器誤判

4. 布隆過濾器和哈希表對比

5. 布隆過濾器的應用

三、海量數據問題的解決思路

問題 1:

問題 2:

問題 3:

問題 4:?

問題 5:

問題 6:

四、一致性哈希

1. 普通的哈希算法

2. 一致性哈希算法

五、哈希與加密


前言

本文介紹了兩種高效處理海量數據的數據結構:位圖和布隆過濾器。位圖通過二進制位存儲狀態,適合整數查詢,40億數據僅需500M空間。布隆過濾器擴展到位圖存儲字符串,通過多個哈希函數減少沖突,但存在誤判可能。文章還討論了海量數據問題的解決思路,包括哈希切割、雙位圖統計等,并對比了一致性哈希與普通哈希的區別。最后區分了哈希算法(不可逆)與加密算法(可逆)的特性。這些數據結構和技術特別適用于需要高效查詢和節省內存的大數據場景。


一、位圖

1. 位圖的概念

位圖是利用某個位存放某種狀態的數據結構,例如可以用一個字節中的 8 個位,用來表示 0 ~ 7 這 8 個數字是否存在,如果第 i 位是 1,表示 i 存在,是 0 表示不存在;

2. 位圖的模擬實現

elem 定義一個 byte[] 數組,用來存放某個數字是否在位圖中存在;

usedSize 表示存放數字的個數;

MyBitSet() 構造方法,默認開辟的空間為 1 個字節;

set(int val): void 將 val 存放到位圖中;

思路:將位圖中的第 val 個位置 1;

定義 byteIndex 表示第幾個字節,bitIndex 表示第幾個位;例如存放數字 12 到位圖中,就需要將第 1 字節的第 4 位置 1;

存放數據是要注意是否存滿,滿了存放之前要擴容;存放完成后,usedSize++;

get(int val): boolean 表示 val 是否在位圖中存在,存在返回 true,不存在返回 false;

reset(int val): void 將 val 在位圖中刪除,刪除完成后 usedSize--;

getUsedSize():返回存放元素的個數;

public class MyBitSet {public byte[] elem;private int usedSize;public MyBitSet(){this.elem = new byte[1];}public MyBitSet(int n){this.elem = new byte[n / 8 + 1];}public void set(int val){if(val < 0){throw new PosOutOfBoundsException();}int byteIndex = val / 8;int bitIndex = val % 8;if(byteIndex >= this.elem.length){this.elem = Arrays.copyOf(this.elem, byteIndex + 1);}this.elem[byteIndex] |= (1 << bitIndex);this.usedSize++;}public boolean get(int val){if(val < 0 || val / 8 >= this.elem.length){throw new PosOutOfBoundsException();}int byteIndex = val / 8;int bitIndex = val % 8;if(((this.elem[byteIndex] >> bitIndex) & 1) == 1){return true;}return false;}public void reset(int val){if(val < 0 || val / 8 >= this.elem.length){throw new PosOutOfBoundsException();}int byteIndex = val / 8;int bitIndex = val % 8;this.elem[byteIndex] &= ~(1 << bitIndex);this.usedSize--;}public int getUsedSize(){return this.usedSize;}
}

3. 位圖的應用

位圖適合海量數據的查詢:

假設有 40 億個不重復的整數,并且沒有進行過排序,如何判斷某個數是否在這 40 億個數中?

通常使用遍歷一遍進行查找的時間復雜度是 O(N);使用排序加二分的查找方式,時間復雜度是 O(N*logN) + O(logN);

如果使用位圖,將這 40 億個整數都放在位圖當中,查詢某個整數的時間復雜度只是 O(1),因為只需要判斷該整數對應的位是否被置 1 即可;

位圖的優勢:存放 40 億個整數大于需要 16G,通常來說內存是存不下的,如果使用位圖,500M就能存下,因此位圖更適用于海量數據的查詢;

位圖的缺點:位圖只能存放整數,適用于數據無重復的場景;

二、布隆過濾器

1. 布隆過濾器的概念

布隆過濾器利用哈希函數,可以將字符串轉化為某個整數,再存放在位圖里,解決了位圖不能存放字符串的問題,更適合實際應用;它是一種緊湊的,概率型數據結構,可以進行高效的插入和查詢;

2. 布隆過濾器的模擬實現

DEFAULT_SIZE 默認開辟空間的大小;

bitSet 位圖,用于存放通過哈希函數生成的整數;

usedSize 存放元素的個數;

seeds 隨機種子,用于不同的哈希函數生成不同的哈希算法;

simpleHashes 簡單的哈希函數;

MyBloomFilter() 構造方法,定義一個布隆過濾器,開辟默認大小的空間,生成多個不同的哈希函數;

add(String val): void 在布隆過濾器中存放元素;

思路:針對同一個字符串,利用不同的哈希函數,生成多個不同的整數,都存放再位圖當中;

contains(String val): boolean 判斷布隆過濾器中是否包含 val,包含返回 true,不包含返回 false;

思路:對同一個字符串,利用不同的哈希函數,生成多個不同的整數,在位圖中檢查這些整數對應的位是否被置 1;

public class MyBloomFilter {public static final int DEFAULT_SIZE = 1 << 20;// 位圖public BitSet bitSet;public int usedSize;public static final int[] seeds = {5, 7, 11, 13, 27, 33};public SimpleHash[] simpleHashes;public MyBloomFilter(){bitSet = new BitSet(DEFAULT_SIZE);simpleHashes = new SimpleHash[seeds.length];for(int i = 0; i < seeds.length; i++){simpleHashes[i] = new SimpleHash(DEFAULT_SIZE, seeds[i]);}}public void add(String val){for(int i = 0; i < simpleHashes.length; i++){int index = simpleHashes[i].hash(val);bitSet.set(index);}this.usedSize++;}public boolean contains(String val){for(int i = 0; i < simpleHashes.length; i++){int index = simpleHashes[i].hash(val);if(!bitSet.get(index)) return false;}return true;}
}class SimpleHash{public int cap;public int seed;public SimpleHash(int cap, int seed){this.cap = cap;this.seed = seed;}public int hash(String key){int h;// (n - 1) & hashreturn (key == null) ? 0 : (seed * (cap - 1)) & ((h = key.hashCode()) ^ (h >>> 16));}
}

3. 布隆過濾器誤判

布隆過濾器中存在多個哈希函數,但仍然會有一定的概率多個哈希函數針對不同的字符串 s1 和 s2計算出了相同的哈希值,如果容器里面存放了 s1,進行查詢的時候就會誤以為也存了 s2;

因此布隆過濾器確定某個元素不存在時,該元素一定不存在;確定某個元素存在時,該元素只是可能存在,并不一定存在,因為會存在誤判的情況;

4. 布隆過濾器和哈希表對比

哈希表查詢速度很快,但是對空間的利用率并不高,通常只有 50%;當在海量數據的應用場景下,哈希表存儲效率的問題就會顯現出來;

而布隆過濾器使用哈希加位圖的思想,通過不同的哈希函數,計算出不同對象的哈希值,在位圖當中存儲,既解決了哈希表空間利用率低的問題,又保證了查詢效率,但是它會存在誤判的情況,在準確性方面是不如哈希表的。

5. 布隆過濾器的應用

布隆過濾器是在哈希和位圖的基礎上實現的,因此布隆過濾器也適用于海量數據的查詢,并且占用的內存較小;

布隆過濾器的優點:

  • 查詢元素的時間復雜度為 O(k),k 為哈希函數的個數;
  • 哈希函數之間沒有關系,方便硬件并行運算;
  • 布隆過濾器不需要存儲元素本身,在對保密要求比較嚴格的場合有很大優勢;
  • 在能夠承受一定的誤判率時,布隆過濾器比其他的數據結構有很大的空間優勢;

布隆過濾器的缺點:

  • 布隆過濾器會存在誤判的情況,因為哈希函數針對不同的字符串有可能計算出相同的哈希值,即哈希沖突;
  • 布隆過濾器不能獲取元素本身;
  • 一般情況不能從布隆過濾器中刪除元素;

google 的 guava 包中有對布隆過濾器的實現;

布隆過濾器可用于網頁爬蟲時,對 URL 進行去重,避免重復爬相同的 URL;

垃圾郵件過濾,可以從十幾億個垃圾郵件地址判斷某郵箱是否是垃圾郵箱;

三、海量數據問題的解決思路

問題 1:

假設有一個超過 100G 大小的 log file,log 中存放這 IP地址,如何找到出現次數最多的 IP 地址,及如何找到出現頻率前 k 的IP地址??

解法一:哈希表

如果不考慮占用空間的大小,可以使用哈希表來統計每一個 IP 的數量,找到出現次數最多的即可;

解法二:哈希切割

但 100G 的數據是無法一次性加載到內存當中,需要將 100G 的數據分割成若干個小文件;分別統計每一個文件中出現次數最多的 IP 地址及次數,找出出現次數前 k 個 IP 地址即可;

注意:分割不能采用均分方式,因為一個小文件中出現最多次數的?IP 不一定是所有 IP 中出現次數最多的IP;

分割文件采用哈希切割的方式,即對不同的 IP 計算哈希值,哈希值相同的 IP 存放到一個文件當中,讀取每一個文件,統計每個 IP 出現的次數,找到前 k 個即可;


問題 2:

給定 100 億個整數,設計算法找到其中只出現一次的數字;

解法一:哈希切割

使用哈希切割的方式,分成若干個小文件,計算所有數字的哈希值,將具有相同哈希值的數字放到一個文件中,讀取每一個文件,找到只出現一次的數字;

解法二:兩個位圖

使用兩個位圖 bitSet1 和 bitSet2,遍歷 100 億整數:

出現一次就將 bitSet1 中的對應位置 1;

出現兩次則將 bitSet2 中的對應位置 1,bitSet1 中的對應位置 0;

出現三次或者三次以上,就將 bitSet1 和 bitSet2 中的對應位都置 1;

遍歷位圖,找到只出現一次的數字。

解法三:一個位圖

使用位圖中連續的兩個位來表示出現的次數,00 表示沒出現,01 表示出現一次,10 表示出現兩次,11 表示出現三次及三次以上;

遍歷位圖,找到出現一次的整數;

在位圖中存放時:

字節序號:byteIndex = val / 4;位序號:bitIndex = (val % 4)* 2;


問題 3:

給兩個文件,每個文件有 100 億個整數,只有 1G 內存,如何找到兩個文件的交集?

解法一:哈希切割

遍歷第一個文件,將所有的整數都通過哈希計算,具有相同哈希值的數字,都放到一個文件當中,形成若干個小文件,用 A[i] 表示;

遍歷第二個文件,將所有的整數都通過哈希計算,具有相同哈希值的數字,都放到一個文件當中,形成若干個小文件,用 B[i] 表示;

將 A[i] 和 B[i] 緩存到內存中,找到相同的數字,都存放到結果文件中;

最終遍歷完所有的小文件,得到的就是結果文件就是交集;?

解法二:位圖

創建一個位圖 bitSet,遍歷第一個文件,將第一個文件的整數都放到 bitSet 中;

遍歷第二個文件,并在 bitSet 中查找,找到的所有數據就是兩個文件的交集;


衍生問題:如果求上述問題中數據的并集和差集?

創建一個位圖 bitSet1,遍歷第一個文件,將第一個文件的所有的數字都放到 bitSet1 中;

創建一個位圖 bitSet2,遍歷第二個文件,將第二個文件的所有的數字都放到 bitSet2 中;

遍歷位圖,將第一個位圖和第二個位圖的每一位進行按位或操作,得到的新的位圖 bitSet3;

遍歷 bitSet3,將位圖中的數字都存到新的文件當中,新的文件就是并集;

如果進行按位與操作,得到的數據就是交集;

如果進行按位異或操作,得到的數據就是差集;


問題 4:?

一個文件有 100 億個整數,現有 1G 內存,設計算法找到出現不超過兩次的所有整數;

解法一:哈希切割

遍歷文件,將所有的整數都通過哈希計算,具有相同哈希值的數字,都放到一個文件當中,形成若干個小文件;

遍歷每一個小文件,統計所有數字的出現次數,找出出現次數不超過兩次的整數;

解法二:兩個位圖

創建兩個位圖 bitSet1 和 bitSet2:

出現一次就將 bitSet1 中的對應位置 1;

出現兩次則將 bitSet2 中的對應位置 1,bitSet1 中的對應位置 0;

出現三次或者三次以上,就將 bitSet1 和 bitSet2 中的對應位都置 1;

遍歷位圖,找到只出現一次和出現兩次的數字。


問題 5:

給兩個文件,分別有 100 億個?query,我們只有 1G 內存,如何找到兩個文件的交集,分別給出精確算法和近似算法;

精確算法:哈希切割

遍歷第一個文件,使用哈希函數計算每一個 query 對應的哈希值,相同哈希值的 query 放到同一個文件中,形成若干個小文件 A[i];

遍歷第二個文件,使用哈希函數計算每一個 query 對應的哈希值,相同哈希值的 query 放到同一個文件中,形成若干個小文件 B[i];

分別求 A[i] 和 B[i] 的交集,得到所有的交集的并集就是兩個大文件的交集;

近似算法:布隆過濾器

遍歷第一個文件,將所有的 query 都映射到布隆過濾器中,讀取第二個文件,去布隆過濾器中查找,得到的所有 query 就是交集,但有可能會出現誤判;


問題 6:

如何擴展布隆過濾器,使它能夠支持刪除操作?

將布隆過濾器的每個位都擴展成一個小的計數器,插入元素時給 k 個計數器加 1,刪除元素時給每個計數器減 1;

但是當哈希沖突多了,計數器有可能溢出;

四、一致性哈希

1. 普通的哈希算法

假設有一張圖片需要緩存到服務器上,如果有三臺服務器,通過哈希函數求到圖片對應的哈希值,哈希值 % 3 就找到了對應的服務器,將圖片緩存即可。“哈希值?% 服務器數量”的這種算法,就是普通的哈希算法;

普通哈希算法的缺陷:

如果新增服務器,或者某臺服務器損壞,服務器的數量就會發生改變;

如果哈希函數不變,同一張圖片算出來的哈希值就會改變,那么所有的緩存就會失效,造成緩存的雪崩;

應用獲取不到這些圖片,就會向后端發送請求,大量的請求會帶給服務器巨大的壓力,有可能導致服務器宕機。

2. 一致性哈希算法

一致性哈希算法:使用服務器的 IP 或者編號,主機名等?% 2^32;

一致性哈希算法將整個哈希值空間按照順時針方向組織成一個虛擬的圓環,稱為 Hash 環;

將各個主機使用哈希函數進行哈希,確定主機在哈希環上的位置;

將圖片使用同樣的哈希函數進行哈希,確定圖片在哈希環上的位置;因為圖片不一定正好和主機在同一個位置,因此需要將圖片存到它順時針旋轉遇到的第一臺服務器;

優點:即使出現服務器宕機,也會只有一部分圖片需要緩存到下一臺服務器,不會出現大量緩存雪崩;也不會向后端發送大量的請求,帶給服務器巨大壓力;

缺點:如果哈希環出現傾斜(服務器分布不均勻),大部分數據緩存在某一臺服務器上,可能導致這臺服務器宕機,之后所有圖片再緩存到下一臺服務器,再造成下一臺服務器宕機,最終會導致服務器集群宕機;

解決方法:使用服務器虛擬節點,每個服務器創建多個虛擬節點,均勻分布在哈希環上,這樣圖片緩存就會均勻分布在多態服務器上;

五、哈希與加密

哈希算法往往設計成輸出相同長度的哈希值,例如 MD5 算法,是不可逆的;

加密算法輸出的加密數據往往和輸入數據的長度成正比,不一定都是長度相同的,是可逆的;

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/88757.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/88757.shtml
英文地址,請注明出處:http://en.pswp.cn/web/88757.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Web攻防-PHP反序列化魔術方法觸發條件POP鏈構造變量屬性修改黑白盒角度

知識點&#xff1a; 1.WEB攻防-PHP反序列化-序列化和反序列化 2.WEB攻防-PHP反序列化-常見魔術方法觸發規則 3.WEB攻防-PHP反序列化-反序列化漏洞產生原因 4.WEB攻防-PHP反序列化-黑白盒&POP鏈構造 一、演示案例-WEB攻防-PHP反序列化-序列化和反序列化 什么是反序列化操作…

C# VB.NET多進程-管道通信,命名管道(Named Pipes)

要向已運行的進程發送特定命令&#xff08;如/exit&#xff09;&#xff0c;而不是啟動新進程&#xff0c;需要使用進程間通信&#xff08;IPC&#xff09;機制。以下是幾種常見的實現方法&#xff1a;一、使用命名管道&#xff08;Named Pipes&#xff09;如果ABC.EXE支持通過…

C++ 右值引用 (Rvalue References)

右值引用是C11引入的革命性特性&#xff0c;它徹底改變了C中資源管理和參數傳遞的方式。下面我將從多個維度深入講解右值引用。一、核心概念1. 值類別(Value Categories)lvalue (左值): 有標識符、可取地址的表達式int x 10; // x是左值 int* p &x; // 可以取地址rvalue…

反激變換器設計全流程(一)——電路拓撲及工作流程

一、電路拓撲原理 拓撲結構概述 開關反激電源采用反激式拓撲結構&#xff0c;主要由開關管&#xff08;通常為 MOSFET&#xff09;、變壓器、輸出整流二極管、輸出濾波電容以及控制電路等組成。其基本工作原理是通過開關管的周期性開關動作&#xff0c;將輸入直流電壓轉換為高…

uniapp語音播報天氣預報微信小程序

1.產品展示2.頁面功能(1)點擊上方按鈕實現語音播報4天天氣情況。3.uniapp代碼<template><view class"container"><view class"header"><text class"place">地址:{{city}}</text><text class"time"&g…

Pycharm 報錯 Environment location directory is not empty 如何解決

好長時間不看不寫代碼了&#xff0c;人也跟著犯糊涂。今天在Pycharm 導入虛擬環境時&#xff0c;一直報錯&#xff1a;“Environment location directory is not empty”&#xff0c;在網上百度很多很多方法都無法解決&#xff0c;直到我翻出我之前自己寫的導入虛擬環境的詳細過…

React強大且靈活hooks庫——ahooks入門實踐之場景類(scene)hook詳解

什么是 ahooks&#xff1f; ahooks 是一個 React Hooks 庫&#xff0c;提供了大量實用的自定義 hooks&#xff0c;幫助開發者更高效地構建 React 應用。其中場景類 hooks 是 ahooks 的一個重要分類&#xff0c;專門針對特定業務場景提供解決方案。 安裝 ahooks npm install …

大模型之Langchain篇(二)——RAG

寫在前面 跟著樓蘭老師學習【LangChain教程】2025吃透LangChain框架快速上手與深度實戰&#xff0c;全程干貨無廢話&#xff0c;三天學完&#xff0c;讓你少走百分之99彎路&#xff01;_嗶哩嗶哩_bilibili 計算相似度 一般用的余弦相似度&#xff0c;這里只是演示計算。 fr…

深入理解圖像二值化:從靜態圖像到視頻流實時處理

一、引言&#xff1a;圖像分析&#xff0c;從“黑與白”開始在計算機視覺任務中&#xff0c;**圖像二值化&#xff08;Image Binarization&#xff09;**是最基礎也是最關鍵的圖像預處理技術之一。它通過將灰度圖像中每個像素轉換為兩個離散值&#xff08;通常是0和255&#xf…

云蝠智能 VoiceAgent重構企業呼入場景服務范式

在數字化轉型浪潮中&#xff0c;企業呼入場景面臨客戶服務需求激增與人力成本攀升的雙重挑戰。傳統呼叫中心日均處理僅 300-500 通電話&#xff0c;人力成本占比超 60%&#xff0c;且服務質量受情緒波動影響顯著。云蝠智能推出的 VoiceAgent 語音智能體&#xff0c;通過全棧自研…

java進階(一)+學習筆記

1.JAVA設計模式1.1 什么是設計模式設計模式是軟件開發過程中前輩們在長期實踐中針對重復出現的問題總結出來的最佳解決方案。這些模式不是具體的代碼實現&#xff0c;而是經過驗證的、可重用的設計思想&#xff0c;能夠幫助開發者更高效地解決特定類型的問題。設計模式的重要性…

Pandas-數據清洗與處理

Pandas-數據清洗與處理一、數據清洗的核心目標二、缺失值處理1. 缺失值檢測2. 缺失值處理策略&#xff08;1&#xff09;刪除法&#xff08;2&#xff09;填充法三、異常值識別與處理1. 異常值檢測方法&#xff08;1&#xff09;統計法&#xff08;2&#xff09;業務規則法2. 異…

在 MacOS 上安裝和配置 Kafka

消息代理是一種軟件&#xff0c;充當在不同應用程序之間發送消息的中介。它的功能類似于服務器&#xff0c;從一個應用程序&#xff08;稱為生產者&#xff09;接收消息&#xff0c;并將其路由到一個或多個其他應用程序&#xff08;稱為消費者&#xff09;。消息代理的主要目的…

基于Leaflet調用天地圖在線API的多層級地名檢索實戰

目錄 前言 一、天地圖在線檢索 1、在線檢索功能 2、再談后后接口 二、Leaflet多層級實現實例 1、層級調用實現原理 2、Leaflet中多層級調用 3、成果展示 三、總結 前言 “地圖是世界的索引&#xff0c;而地名則是索引中的索引。”當互聯網地圖進入 Web 2.0 時代&#x…

基于Prompt結構的語校解析:3H日本語學校信息建模實錄(4/500)

基于Prompt結構的語校解析&#xff1a;3H日本語學校信息建模實錄&#xff08;4/500&#xff09; 系列延續&#xff1a;500所日本語言學校結構數據工程 關鍵詞&#xff1a;招生結構、JLPTEJU、國籍比例、認定校、Prompt訓練集 一、我們在構建什么樣的語言學校語料&#xff1f; …

Leaflet面試題及答案(61-80)

查看本專欄目錄 文章目錄 ?? 面試問題及答案(61-80)61. 如何在地圖上顯示一個動態更新的圖層?62. 如何實現地圖上的熱力圖(Heatmap)?63. 如何自定義地圖控件的位置?64. 如何處理地圖加載失敗的情況?65. 如何實現地圖的離線功能?66. 如何將地圖導出為圖片?67. 如何實…

MIG_IP核的時鐘系統

MIG_IP核的時鐘系統時鐘的種類和配置時鐘的種類和配置 整體框圖 DDR_PHY_CLK&#xff1a;DDR3的工作頻率&#xff0c;用來得到想要的線速率。假設此時鐘為800M&#xff0c;那么DDR雙沿采樣&#xff0c;線速率為1600Mbit&#xff1b; UI_CLK&#xff1a;DDR_PHY_CLK的四分之一…

若依框架集成阿里云OSS實現文件上傳優化

背景介紹 在若依框架目前的實現中&#xff0c;是把圖片存儲到了服務器本地的目錄&#xff0c;通過服務進行訪問&#xff0c;這樣做存儲的是比較省事&#xff0c;但是缺點也有很多&#xff1a; 硬件與網絡要求&#xff1a;服務器通常需要高性能的硬件和穩定的網絡環境&#xff0…

Mac如何連接惠普M126a打印機(教程篇)

這里寫自定義目錄標題Mac如何連接惠普M126a打印機&#xff08;教程篇&#xff09;教程配置如下&#xff1a;Mac如何連接惠普M126a打印機&#xff08;教程篇&#xff09; 惠普M126a連接Mac&#xff08;教程篇&#xff09; 教程配置如下&#xff1a; 首先&#xff0c;先獲取與HP打…

感恩日記:記錄生活中的美好時刻

感恩日記的landing page登錄注冊填寫感恩事項私信可以體驗一下