哈希表及其基礎(java詳解)

目錄

一、哈希表基礎?

二、哈希函數的設計

哈希函數的設計原則

三、java中的hashCode

基本數據類型的hashCode使用

自定義類型的hashCode使用?

需要注意

四、哈希沖突的處理 鏈地址法Seperate Chaining

五、實現屬于我們自己的哈希表

六、哈希表的動態空間處理和復雜度分析

七、哈希表更復雜的動態空間處理方法

我們哈希表的bug

八、更多哈希沖突的處理方法

開放地址法

其他


本篇博客花費大量時間寫的十分詳細,為了保證思路理解的連貫性,希望各位耐心地讀下去。

一、哈希表基礎?


參考LeetCode第387號問題:給定一個字符串,找到它的第一個不重復的字符,并返回它的索引。如果不存在,則返回 -1。

提示:你可以假定該字符串只包含小寫字母。

這種方法的時間復雜度是O(n)。

int[26] freq就是一個哈希表,每一個字符都和一個索引相對應,index = ch - 'a',這是哈希表的優勢——把字符轉化成一個索引。直接用一個數組來進行O(1)的查找操作,把字符轉化為索引的函數就稱為哈希函數f(ch) = ch - 'a'。

我們很難保證每一個鍵通過哈希函數的轉換會對應不同的索引,而兩個鍵對應同一個索引就會產生哈希沖突,我們需要在哈希表上操作來解決哈希沖突,這個我們后面再講。

哈希表完美的體現了用空間換時間的思想。比如我們18位數的身份證號的存儲,如果可用空間足夠大,我們可以用O(1)的時間實現。我們希望每一個鍵通過哈希函數得到的索引分布越均勻越好。

二、哈希函數的設計

學習哈希表我們最主要解決的兩個問題:一個是哈希函數的設計,一個是如何解決哈希沖突。

接下來我們來看各種類型的數據怎么通過哈希函數轉化為一個數組的索引。?

整型:

小范圍的正整數直接使用。

小范圍的負整數進行偏移。 -100 ~ 100 ——> 0 ~ 200

大整數:

比如18位的身份證號

方法:取模。? 比如取后四位,等同于mod10000,不能取后六位,因為牽扯到人的出生日期,一個月最多有31天,所以整個數據不會超過32萬,只利用32/100會造成分布不均勻。取后幾位還要考慮有沒有利用所有的信息,比如身份證號中還有人的出生省份、城市、月份等信息,如果不全面利用也會造成哈希沖突概率上升。一個簡單的解決辦法就是模一個素數。這個解決辦法的證明是數論的范疇,感興趣的朋友可以自己去了解一下。

由圖片我們可以看出,模一個素數比模一個和數分布更均勻。

?這個網站給出了根據數據規模不同,取什么素數是合適的。

根據你的數據范圍,最后一列給出了推薦的素數。當然這種東西我們不需要深究,了解一下就好了。

浮點型:

在計算機中都是32或者64位的二進制表示,只不過計算機解析成了浮點數,如果我們的鍵是浮點型的話,我們就可以使用這個浮點型所存儲的空間當作整型來進行處理。也就是把浮點數所占用的32位或者64位的空間使用整數來進行解析,這片空間就同樣可以表示一個整數,問題就轉化成了一個大的整數來轉換成索引的方式。

字符串:

轉成整型處理,如果要區分字母大小寫,那么26進制還不夠,用戶自己可以選擇轉成多少進制的大整型。

下面這兩個式子是等價的,只是簡化了一些。M就是一個素數。

為了防止整型溢出,我們可以把取模挪到括號里面。

//為了實現字符串的這個小邏輯,我們寫個循環
int hash = 0;
for(int i = 0; i < s.length(); i++){hash = (hash * B + s.charAt(i)) % M}

復合類型/自定義類型:

也是轉成整型類型,并且和字符串的處理方式差不多。

比如我們自定義一個日期類型——Date: year, month, day. 我們可以用字符串的方式來換成整型,如下式。

總而言之,我們都是轉成整型來處理,但這并不是唯一的方法,感興趣的朋友可以去了解更多。

哈希函數的設計原則

三、java中的hashCode

在java語言中,hashCode方法可以直接獲得基本數據類型的哈希函數的值,如果我們要自定義一個類型,我們可以覆蓋在Object類中就有的hashCode函數。

下面我們來看一下在java中hashCode的使用方法。

基本數據類型的hashCode使用

//基本數據類型的hashCode使用
public class Main {public static void main(String[]args){int a = 42;//因為a不是一個對象,所以我們轉化成包裝類System.out.println(((Integer)a).hashCode());int b = -42;System.out.println(((Integer)b).hashCode());double c = 3.1415926;System.out.println(((Double)c).hashCode());String d = "fyj";System.out.println(d.hashCode());}
}

自定義類型的hashCode使用?

//自定義類型
public class Student {int grade;//年級int cls;//班String firstName;//名String lastName;//姓Student(int grade, int cls, String firstName, String lastName){this.grade = grade;this.cls = cls;this.firstName = firstName;this.lastName = lastName;}@Overridepublic int hashCode(){//把四部分看成四個數字,把整體看成這四部分組成的B這個進制的數int B = 31;int hash = 0;hash = hash * B + grade;hash = hash * B + cls;//firstName是字符串,我們可以調用它的hashCode轉化為一個整數hash = hash * B + firstName.hashCode();hash = hash * B + lastName.hashCode();//如果我們不希望區分大小寫,可以改成下面//傳入后全換成小寫//hash = hash * B + firstName.toLowerCase().hashCode();//hash = hash * B + lastName.toLowerCase().hashCode();//傳入后全換成大寫//hash = hash * B + firstName.toUpperCase().hashCode();//hash = hash * B + lastName.toUpperCase().hashCode();return hash;}public static void main(String[]args) {Student student = new Student(2,2, "o","f");System.out.println(student.hashCode());}
}

有了初步了解后,我們就可以嘗試著使用java中給我們提供的有關哈希表的數據結構,下面是java哈希表的基本使用方法。

//導入底層基于哈希表的集合HashSet
import java.util.HashSet;
import java.util.HashMap;
public class Main {public static void main(String[]args){Student student = new Student(2,2, "o","f");System.out.println(student.hashCode());HashSet<Student> set = new HashSet<>();set.add(student);HashMap<Student, Integer> scores = new HashMap<>();scores.put(student, 100);}
}

需要注意

如果我們自己覆蓋的那個HashCode方法注釋掉,我們再次運行依然可以成功。因為對于java來說,每一個Object類默認有一個HashCode的實現,它是根據我們創建的每一個Obect的地址相應的轉化成一個整型。如果我們在注釋掉自己覆蓋的HashCode情況下再創建一個student2,讓student2和student的內容一樣,運行出來的它們兩個的HashCode值是不一樣的。因為兩個對象的地址不同,轉化為的整型就不同。如果用我們自己覆蓋的HashCode方法,兩個HashCode值就一樣了。

我們自己覆蓋的HashCode方法只是幫助獲取對象的HashCode值,在產生哈希沖突的時候,我們依舊需要比較這兩個類是不是相等的。除了覆蓋HashCode,我們還需要equals來判斷兩個對象是否相等。

@Override//我們需要傳入的是Object類型,而不是Student類型public boolean equals(Object o){//是否是同一個引用if(this == o){return true;}if(o == null){return false;}//獲取對象的運行時類if(getClass() != o.getClass()){return false;}Student another = (Student)o;return this.grade == another.grade &&this. cls == another.cls &&this.firstName == another.firstName &&this.lastName == another.lastName;}

這時候出現哈希沖突時,就算對應同一個值也可以區分出兩個對象。接下來我們將會真正實現一個哈希表。

四、哈希沖突的處理 鏈地址法Seperate Chaining

其實哈希表本質就是一個數組,我們先假設有這么一個數組,這時候來了一個鍵是k1的元素,我們只需要用hashcode(k1) % M,在java中hashcode相應的結果可能是負值,我們還要想辦法把負號給抹去。因為在Java中,哈希碼是一個int類型的值,可以是正數或負數。但是,哈希表中的索引值必須是非負整數,因此如果哈希碼是負數,就需要將其轉換為非負整數。

在看源碼或者別人寫的代碼中我們很常見到下面這種寫法:

通過整型和31個1按位與把最高位的負號抹去。因為計算機中二進制表示的最高位是符號位,1是負0是正,0和1按位與是0,1和1按位與還是0,最后都是正號。

假如k1計算完后索引為4,那么就存在數組索引為4的地方,這時候如果來了兩個索引都為2的k2、k3怎么辦?那我們就以鏈表的形式都存在索引為2的地方,這就是鏈地址法。

對于每個位置,我們說是存一個鏈表,其實它的本質就是存一個查找表, 查找表的實現方式還可以用樹結構實現,比如可以用平衡樹,這時候每個位置存的就不是一個鏈表,而是一個TreeMap的查找表,這時候我們的HashMap本質就是一個TreeMap的數組。

除了映射的角度,我們還可以從集合的角度,那時候HashSet 就是一個TreeSet數組。不管怎樣,哈希表的本質就是一個查找表,底層的數據結構只要是適合做查找表的就可以。

五、實現屬于我們自己的哈希表

//哈希表中的k不用像二分搜索樹一樣具有可比較性
//因此不用繼承Comparable接口
//同樣HashTable的k必須要實現hashCode這個方法
//不過在Java中所有的類型都是Object的子類,而Object默認實現了hashCode方法
//所以具體在代碼上我們對這個k也不需要有特殊的要求,如果有必要可以自己去覆蓋自己的hashCode方法//底層是一個紅黑樹
import java.util.TreeMap;public class HashTable<K, V> {private TreeMap<K, V>[] hashtable;private int size;//哈希表具體有多少個元素private int M; //哈希表有多少個位置/哈希表的大小public HashTable(int M){this.M = M;size = 0;hashtable = new TreeMap[M];//初始化數組for(int i = 0 ; i < M ; i ++)hashtable[i] = new TreeMap<>();//實例化}public HashTable(){this(97);//默認傳97的固定的值}private int hash(K key){//把key轉化成一個整型后通過按位與保證為非負數return (key.hashCode() & 0x7fffffff) % M;}public int getSize(){//哈希表中一共有多少元素return size;}public void add(K key, V value){//增TreeMap<K, V> map = hashtable[hash(key)];//基于多次重復計算的hash(key)小優化if(map.containsKey(key))//如果已存在key,那就理解成把key的值修改為valuemap.put(key, value);else{map.put(key, value);//真正的添加數據size ++;}}public V remove(K key){//刪V ret = null;//ret是return value的意思TreeMap<K, V> map = hashtable[hash(key)];//找到key對應的的哈希函數的值if(map.containsKey(key)){ret = map.remove(key);size --;}return ret;}public void set(K key, V value){//改TreeMap<K, V> map = hashtable[hash(key)];if(!map.containsKey(key))throw new IllegalArgumentException(key + " doesn't exist!");map.put(key, value);}public boolean contains(K key){//是否存在return hashtable[hash(key)].containsKey(key);}public V get(K key){//查詢某一個鍵對應的值是多少return hashtable[hash(key)].get(key);}
}

六、哈希表的動態空間處理和復雜度分析

前面我們說過,我們創建的哈希表有M個地址,如果放入N個元素,那么平均來看每個地址會有N/M個元素會產生哈希沖突。?

如果每個地址是鏈表,那么時間復雜度O(N/M)。

如果每個地址是平衡樹,那么時間復雜度O(log(N/M))。

以上是平均來看的,而不是最壞的情況,最壞的情況是所有元素都擠在一個地址,那時候就分別是O(N)和O(logN)了。我們前面也說過哈希表的設計原則,我們的哈希表要盡可能地分布均勻。所以在哈希表設計恰當時是不會出現最壞情況的,我們還是要看平均情況下。

我們之前說哈希表的優勢在于能讓時間復雜度成為O(1)級別,現在我們的時間復雜度為什么都和N有關呢?原因就在于這個M是固定的,它是一個常數,當N趨于無窮大的時候,N/M總體也是趨于無窮的。?我們的哈希表就是一個數組,我們整體數據結構如果基于一個靜態數組是不合理的,空間總是固定的,所以我們的數組應該是動態內存分配的。當平均每個地址承載的元素多過一定程度時我們就擴容,反之就縮容。

下面是我們引入動態擴容縮容的哈希表代碼:

import java.util.Map;
import java.util.TreeMap;public class HashTable<K, V> {//設定平均每個地址哈希沖突的上界/下界private static final int upperTol = 10;private static final int lowerTol = 2;//初始容量private static final int initCapacity = 7;private TreeMap<K, V>[] hashtable;private int size;private int M;public HashTable(int M){this.M = M;size = 0;hashtable = new TreeMap[M];for(int i = 0 ; i < M ; i ++)hashtable[i] = new TreeMap<>();}public HashTable(){this(initCapacity);}private int hash(K key){return (key.hashCode() & 0x7fffffff) % M;}public int getSize(){return size;}public void add(K key, V value){TreeMap<K, V> map = hashtable[hash(key)];if(map.containsKey(key))map.put(key, value);else{map.put(key, value);size ++;//擴容if(size >= upperTol * M) //等價于size/M >= upperTolresize(2 * M);}}public V remove(K key){V ret = null;TreeMap<K, V> map = hashtable[hash(key)];if(map.containsKey(key)){ret = map.remove(key);size --;//縮容if(size < lowerTol * M && M / 2 >= initCapacity)//M不要小于初始的容積量resize(M / 2);}return ret;}public void set(K key, V value){TreeMap<K, V> map = hashtable[hash(key)];if(!map.containsKey(key))throw new IllegalArgumentException(key + " doesn't exist!");map.put(key, value);}public boolean contains(K key){return hashtable[hash(key)].containsKey(key);}public V get(K key){return hashtable[hash(key)].get(key);}//自動擴容實現private void resize(int newM){TreeMap<K, V>[] newHashTable = new TreeMap[newM];for(int i = 0 ; i < newM ; i ++)newHashTable[i] = new TreeMap<>();int oldM = M;this.M = newM;for(int i = 0 ; i < oldM ; i ++){TreeMap<K, V> map = hashtable[i];for(K key: map.keySet())newHashTable[hash(key)].put(key, map.get(key));}this.hashtable = newHashTable;}
}

七、哈希表更復雜的動態空間處理方法

對于哈希表來說,元素從N增加到upperTol * N,地址空間增倍,均攤后的平均復雜度是O(1),每個操作在O(lowerTol) ~ O(upperTol)。

可能有細心的朋友會發現,當我們擴容M時,2 * M 不再是一個素數了,這樣可能會導致哈希表的分布不均勻。怎么解決呢?其實很簡單,我們可以根據前面提到的素數表來進行擴容。

比如,當我們初始的M是53,我們可以直接擴到下一個M推薦值97,而不是再用那種M * 2 的方式擴容了。觀察表我們可以發現,最后一列這些推薦的相鄰的素數值也在盡量地保持一個二倍左右的關系,這使擴容也更加合理。

下面是加入了素數表的哈希表代碼:

import java.util.TreeMap;public class HashTable<K extends Comparable<K>, V> {//素數表private final int[] capacity= {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289, 24593,49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741};private static final int upperTol = 10;private static final int lowerTol = 2;private int capacityIndex = 0;//指向capacity數組的索引為0所在位置的數字private TreeMap<K, V>[] hashtable;private int size;private int M;public HashTable(){this.M = capacity[capacityIndex];size = 0;hashtable = new TreeMap[M];for(int i = 0 ; i < M ; i ++)hashtable[i] = new TreeMap<>();}private int hash(K key){return (key.hashCode() & 0x7fffffff) % M;}public int getSize(){return size;}public void add(K key, V value){TreeMap<K, V> map = hashtable[hash(key)];if(map.containsKey(key))map.put(key, value);else{map.put(key, value);size ++;if(size >= upperTol * M && capacityIndex + 1 < capacity.length){//防止數組越界capacityIndex ++;resize(capacity[capacityIndex]);}}}public V remove(K key){V ret = null;TreeMap<K, V> map = hashtable[hash(key)];if(map.containsKey(key)){ret = map.remove(key);size --;if(size < lowerTol * M && capacityIndex - 1 >= 0){capacityIndex --;resize(capacity[capacityIndex]);}}return ret;}public void set(K key, V value){TreeMap<K, V> map = hashtable[hash(key)];if(!map.containsKey(key))throw new IllegalArgumentException(key + " doesn't exist!");map.put(key, value);}public boolean contains(K key){return hashtable[hash(key)].containsKey(key);}public V get(K key){return hashtable[hash(key)].get(key);}private void resize(int newM){TreeMap<K, V>[] newHashTable = new TreeMap[newM];for(int i = 0 ; i < newM ; i ++)newHashTable[i] = new TreeMap<>();int oldM = M;this.M = newM;for(int i = 0 ; i < oldM ; i ++){TreeMap<K, V> map = hashtable[i];for(K key: map.keySet())newHashTable[hash(key)].put(key, map.get(key));}this.hashtable = newHashTable;}
}

我們哈希表的均攤復雜度為O(1),同時我們犧牲了順序性。我們的算法難以避免的都是有得必有失的,有一種高級的數據結構是集合和映射,現在我們要有意識的把它分為基于平衡樹的有序集合和有序映射以及基于哈希表的無序集合和無序映射。希望感興趣的朋友可以嘗試自己封裝屬于自己的無序集合和無序映射。

我們哈希表的bug

我們前面代碼中提到我們的HashTable中的K是不需要具有可比較性的,這是哈希表的一個優點,同時我們也失去了哈希表的順序性。但是我們的實現的TreeMap的k是需要繼承Comparable具有可比較性的,這時K產生了矛盾,這是一個bug。

如果我們的每個地址對應一個鏈表 ,那么這個矛盾是不存在的,畢竟鏈表不需要有可比較性。我們前面提到的哈希沖突達到一定程度會轉成紅黑樹,其實這種轉換也是有條件的,前提就是實現了可比較性,不然就仍然保持鏈表,這樣就保證了我們的代碼是有邏輯的。

八、更多哈希沖突的處理方法

開放地址法

定義:對于我們哈希表中的每一個地址,所有的哈希值的元素都有可能進來。?每一個地址不再是存儲鏈表了,而是直接存儲元素的值,如果有兩個元素產生了哈希沖突,那么第二個元素就放在第一個元素往后找下一個空的地方,如下圖所示。

這種方法在某些情況下效率是很低的,比如如果一塊很大的連續空間已經被占滿了,這時又來了一個元素需要放在索引為1的位置,這時候它就需要從頭開始一直往下找空的位置。我們對此的解決方法是不再每次只走一個位置了,而是使用平方探測法,遇到哈希沖突時依次+1 +4 +9 +16來尋找空的地址。我們也可以用二次哈希法,遇到哈希沖突時我們用另一個哈希函數來計算它要存放的地址。

對于開放地址法,我們也可以設計擴容操作,只要擴容的負載率的值選的合適,那么時間復雜度也可以是O(1)級別的。

其他

除了鏈地址法和開放地址法這兩個有名的方法,還有再哈希法Rehashing、Coalesced Hashing,感興趣的朋友可以自己去了解。?

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

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

相關文章

通過項目管理軟件監管新員工入職流程的方法與策略

項目管理軟件是什么&#xff1f;項目管理軟件都能做什么&#xff1f;是不是只有項目團隊需要啊&#xff1f;NO&#xff01;項目管理軟件乍聽其名不免讓人覺得這不過是個項目領域的專用工具。 那什么是項目呢&#xff1f;項目是為創造獨特的產品、服務或成果而進行的體系化的工…

物聯網+AI智慧工地云平臺源碼(SaaS模式)

智慧工地云平臺充分運用數字化技術&#xff0c;聚焦施工現場崗位一線&#xff0c;依托物聯網、互聯網、AI等技術&#xff0c;圍繞施工現場管理的人、機、料、法、環五大維度&#xff0c;以及施工過程管理的進度、質量、安全三大體系為基礎應用&#xff0c;實現全面高效的工程管…

stm32中滴答定時器與普通定時器的區別

1、兩者在單片機中的位置不一樣 滴答定時器在內核上&#xff0c;普通定時器在外設上。 由于位置不同&#xff0c;滴答定時器的程序可以移植到所有相同內核的芯片上&#xff0c;但普通定時器的程序卻不可以。 2、兩者的中斷優先級不一樣 滴答定時器優先級高&#xff0c;普通定…

CTF刷題記錄

刷題 我的md5臟了KFC瘋狂星期四坤坤的csgo邀請simplePHPcurl 我的md5臟了 g0at無意間發現了被打亂的flag&#xff1a;I{i?8Sms??Cd_1?T51??F_1?} 但是好像缺了不少東西&#xff0c;flag的md5值已經通過py交易得到了&#xff1a;88875458bdd87af5dd2e3c750e534741 flag…

關于微信/支付寶等平臺驗簽/簽名sign生成算法

引言 我們在日常工作中經常會遇到對接微信平臺、支付寶平臺、或者自己對外開放一個api服務&#xff0c;那么這里經常會出現一個名字&#xff1a;sgin&#xff08;簽名&#xff09;。 舉個栗子 這是微信支付統一下單接口文檔&#xff0c;最簡單的理解就是&#xff0c;服務端為…

Unirest-Java:Java發起GET、POST、PUT、DELETE、文件上傳,文件下載工具類介紹

一、簡介 Unirest-Java是一個輕量級的HTTP客戶端庫&#xff0c;用于在Java應用程序中發送HTTP請求。 它提供了簡單易用的API&#xff0c;可以方便地處理GET、POST、PUT、DELETE等HTTP方法。 Unirest-Java支持異步和同步請求&#xff0c;可以輕松地與JSON、XML等數據格式進行…

最優化方法復習——線性規劃之對偶問題

一、線性規劃對偶問題定義 原問題&#xff1a; 對偶問題&#xff1a; &#xff08;1&#xff09;若一個模型為目標求 “極大”&#xff0c;約束為“小于等于” 的不等式&#xff0c;則它的對偶模型為目標求“極小”&#xff0c;約束是“大于等于”的不等式。即“Max&#xff0…

2024年甘肅省職業院校技能大賽信息安全管理與評估三階段理論樣題一

2024年甘肅省職業院校技能大賽高職學生組電子與信息大類信息安全管理與評估賽項樣題一 第六部分 理論技能與職業素養&#xff08;100 分&#xff09; 【注意事項】 1.該部分答題時長包含在第三階段競賽時長內&#xff0c;請在臨近競賽結束前提交。 2.參賽團隊可根據自身情況…

數據庫系統概論復習資料

數據庫系統概論考試需知 一、分值分布 1、判斷題&#xff08;10分&#xff09; 1分一個 2、填空題&#xff08;20分&#xff09; 2分一個 3、選擇題&#xff08;20分&#xff09; 2分一個 4、分析題&#xff08;30分&#xff09; 第一題10分&#xff0c;第二題…

如何設置echart圖表在vue頁面屏幕比例縮放自適應問題

問題&#xff1a;頁面的echart圖表在瀏覽器縮放屏幕比例時無法隨著屏幕的比例自動改變大小 解決方式&#xff1a; 可以通過監聽窗口的 resize 事件&#xff0c;并在事件回調函數中重新調整圖表的大小。 <template><div ref"chartContainer" style"w…

Enterprise Architect 12版本使用教程

Enterprise Architect 12版本使用教程 1.下載安裝Enterprise Architect 122.Enterprise Architect原始DDL模板配置及存在的問題1.DDL Column Definition原始模板&#xff08;沒有default值&#xff1a;可忽略&#xff09;2.DDL Data Type原始模板&#xff08;timestamp等時間字…

Apollo新版本Beta自動駕駛技術沙龍參會體驗有感—百度自動駕駛開源框架

在繁忙的都市生活中&#xff0c;我們時常對未來的科技發展充滿了好奇和期待。而近日&#xff0c;我有幸參加了一場引領科技潮流的線下技術沙龍&#xff0c;主題便是探索自動駕駛的魅力——一個讓我們身臨其境感受創新、了解技術巨擘的機會。 在12月2日我有幸參加了Apollo新版本…

智能優化算法應用:基于沙貓群算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼

智能優化算法應用&#xff1a;基于沙貓群算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼 文章目錄 智能優化算法應用&#xff1a;基于沙貓群算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼1.無線傳感網絡節點模型2.覆蓋數學模型及分析3.沙貓群算法4.實驗參數設定5.算法結果6.參考文獻7.…

kotlin協程反編譯java學習原理

前情提要 GlobalScope.launch(Dispatchers.Main) { // 默認是Default異步 // 1.從當前協程體作用域Dispatchers.Main 協程依附的線程 到 掛起點 掛起函數 到 執行 請求耗時操作 而 切換到 IO異步線程 // 2.IO異步線程執行完成后&#xff0c;開始恢復&#xff0c;當前作…

modbus轉profinet網關解決plc插槽號不夠用的情況

PLC作為常用的控制設備之一&#xff0c;其插槽號有時會限制外部設備的連接數量。然而&#xff0c;通過使用modbus轉profinet網關&#xff0c;可以解決這一問題。這種設備能夠將modbus協議轉換為profinet協議&#xff0c;實現PLC與更多外部設備的連接。 modbus轉profinet網關還具…

游戲盾的防御原理以及為什么程序類型更適合接入游戲盾。

游戲盾是一種專門用于游戲服務器的安全防護服務&#xff0c;旨在抵御各種網絡攻擊。它的原理主要包括以下幾個方面&#xff1a; 流量清洗和過濾&#xff1a;游戲盾會對進入游戲服務器的流量進行實時監測、分析和過濾。它通過識別惡意流量和攻擊流量&#xff0c;過濾掉其中的攻擊…

瀏覽器渲染頁面的過程以及原理

什么是瀏覽器渲染 簡單來說&#xff0c;就是將HTML字符串 —> 像素信息 渲染時間點 瀏覽器什么時候開始渲染&#xff1f; 網絡線程發送請求&#xff0c;取回HTML封裝為渲染任務并將其傳遞給渲染主線程的消息隊列。 問題&#xff1a;只取回HTML嗎&#xff1f;那CSS和JS呢&am…

面試經典150題(1-2)

leetcode 150道題 計劃花兩個月時候刷完&#xff0c;今天完成了兩道(1-2)150&#xff1a; &#xff08;88. 合并兩個有序數組&#xff09;題目描述&#xff1a; 給你兩個按 非遞減順序 排列的整數數組 nums1 和 nums2&#xff0c;另有兩個整數 m 和 n &#xff0c;分別表示 n…

元宇宙vr黨建云上實景展館擴大黨的影響力

隨著科技的飛速發展&#xff0c;VR虛擬現實技術已經逐漸融入我們的日常生活&#xff0c;尤其在黨建領域&#xff0c;VR數字黨建展館更是成為引領紅色教育新風尚的重要載體。今天&#xff0c;就讓我們一起探討VR數字黨建展館如何提供沉浸式體驗&#xff0c;助力黨建工作創新升級…

十年OpenCV開發以后發布的作品 - OpenCV實驗大師

OpenCV介紹 OpenCV是知名的計算機視覺框架&#xff0c;支持數十個不同的視覺處理模塊&#xff0c;提供了超過2000多個傳統算法&#xff0c;其核心功能支持圖像處理、圖像分析、特征提取、對象檢測、深度學習模型推理等。當前支持C、Python、JS、C#等多種語言SDK&#xff0c;支…