Java算法之哈希算法
哈希表
哈希表(Hash Table),也稱為散列表,是一種根據(jù)關(guān)鍵碼值(Key Value)直接進行訪問的數(shù)據(jù)結(jié)構(gòu)衙伶。它通過哈希函數(shù)(Hash Function)將關(guān)鍵碼值映射到哈希表中的一個位置,以實現(xiàn)數(shù)據(jù)的快速插入、刪除和查詢操作研侣。
哈希表主要由一個數(shù)組構(gòu)成,數(shù)組的每個元素被稱為哈希桶(Bucket)或槽(Slot)炮捧,其中存放著鍵-值對庶诡。哈希函數(shù)是哈希表的核心部分,它將任意長度的輸入(Key)映射為固定長度的輸出(Hash值)咆课。通過這個映射末誓,哈希表能夠快速定位到存儲特定鍵值對的位置,從而實現(xiàn)高效的查找操作傀蚌。
哈希表具有非常高的空間效率和時間效率基显。在空間效率方面,哈希表不需要為每個鍵都保存一個位置善炫,而是通過哈希函數(shù)將鍵映射為一個哈希值撩幽,然后將鍵放置在對應(yīng)的位置上,因此其空間利用率通常很高。在時間效率方面窜醉,哈希表的插入宪萄、查找和刪除操作的時間復(fù)雜度均為O(1),即與元素數(shù)量多少無關(guān)榨惰,因此能夠?qū)崿F(xiàn)非嘲萦ⅲ快速的查找。
然而琅催,哈希表也存在一些潛在的問題居凶,如哈希沖突。哈希沖突是指不同的鍵經(jīng)過哈希函數(shù)后映射到了同一個哈希值的情況藤抡。為了處理這種情況侠碧,哈希表通常使用一些沖突解決策略,如鏈地址法或開放地址法等缠黍。
總的來說弄兜,哈希表是一種高效、靈活的數(shù)據(jù)結(jié)構(gòu)瓷式,廣泛應(yīng)用于各種需要快速查找的場景替饿,如數(shù)據(jù)庫索引、緩存系統(tǒng)等贸典。
接下來视卢,就介紹一下哈希表的實現(xiàn)。
定義一個哈希表
static class Entry{
int hash;//哈希碼
Object key;//鍵
Object value;//值
Entry next;
public Entry(int hash, Object key, Object value) {
this.hash = hash;
this.key = key;
this.value = value;
}
}
- 類定義:
static class Entry {
這里定義了一個靜態(tài)內(nèi)部類Entry
瓤漏。靜態(tài)內(nèi)部類意味著這個類不需要外部類的實例就可以被實例化腾夯。在HashMap
的實現(xiàn)中,這樣的設(shè)計有助于減少內(nèi)存占用蔬充,因為每個Entry
對象不需要持有對外部HashMap
對象的引用蝶俱。
- 成員變量:
int hash;//存儲鍵的哈希碼。當(dāng)向哈希表中插入一個鍵值對時饥漫,會先計算鍵的哈希碼榨呆,然后根據(jù)這個哈希碼確定鍵應(yīng)該放在哈希表的哪個位置。
Object key;// 存儲鍵庸队。鍵用于唯一標識一個鍵值對积蜻。
Object value;//存儲值。與鍵相關(guān)聯(lián)的值彻消。
Entry next; //指向下一個`Entry`的引用竿拆。這是鏈地址法解決哈希沖突的一種常見方式。當(dāng)兩個或多個鍵的哈希碼相同(即產(chǎn)生了哈希沖突) //時宾尚,這些鍵對應(yīng)的`Entry`對象會形成一個鏈表丙笋,通過`next`字段鏈接在一起谢澈。
- 構(gòu)造函數(shù):
public Entry(int hash, Object key, Object value) {
this.hash = hash;
this.key = key;
this.value = value;
}
這是Entry
類的構(gòu)造函數(shù),它接受一個哈希碼御板、一個鍵和一個值作為參數(shù)锥忿,并將它們分別賦值給類的成員變量。
在HashMap
的實際實現(xiàn)中怠肋,這個Entry
類(或其類似的形式)會被用來在內(nèi)部存儲鍵值對敬鬓。當(dāng)向HashMap
中插入一個鍵值對時,會先計算鍵的哈希碼笙各,然后根據(jù)這個哈希碼確定鍵應(yīng)該放在哈希表的哪個位置(通常是一個數(shù)組的索引)钉答。如果那個位置已經(jīng)有一個或多個Entry
(即發(fā)生了哈希沖突),新的Entry
會被添加到鏈表的末尾酪惭。
當(dāng)從HashMap
中查找一個鍵時希痴,會再次計算該鍵的哈希碼,然后定位到哈希表中的相應(yīng)位置春感。然后,會遍歷該位置上的鏈表虏缸,直到找到匹配的鍵或遍歷完整個鏈表(即鍵不存在)鲫懒。
總的來說,這個Entry
類是實現(xiàn)哈希表數(shù)據(jù)結(jié)構(gòu)的關(guān)鍵部分刽辙,它負責(zé)存儲鍵值對并處理哈希沖突窥岩。
哈希表的get方法
Object get(int hash,Object key){
int index = hash&(table.length-1);
if(table[index]==null){
return null;
}
Entry p = table[index];
while(p!=null){
if(p.key.equals(key)){
return p.value;
}
p = p.next;
}
return null;
}
你提供的get
方法是一個典型的從哈希表中根據(jù)鍵(Key)獲取值(Value)的實現(xiàn)。這里我為你詳細解釋這個方法的每個部分:
方法定義
Object get(int hash, Object key) {
這個方法接受兩個參數(shù):hash
是鍵的哈希碼宰缤,key
是你要查找的鍵颂翼。方法的返回類型是Object
,這意味著它可以返回任何類型的值慨灭。
計算索引
int index = hash & (table.length - 1);
這行代碼計算了鍵在哈希表中的索引位置朦乏。這里使用了位與操作(&
)和哈希表長度減一(table.length - 1
)來確保索引在哈希表的有效范圍內(nèi)。這種計算方式通常用于實現(xiàn)哈希表的開放尋址法中的線性探測或二次探測氧骤,但在這里它更像是用于數(shù)組形式的哈希表呻疹,其中數(shù)組的每個位置可能包含一個鏈表來處理哈希沖突。
檢查索引位置的鏈表頭
if (table[index] == null) {
return null;
}
這里首先檢查哈希表中指定索引位置的鏈表頭是否為null
筹陵。如果是null
刽锤,說明該位置沒有鏈表,因此鍵肯定不在哈希表中朦佩,方法返回null
并思。
遍歷鏈表
Entry p = table[index];
while (p != null) {
if (p.key.equals(key)) {
return p.value;
}
p = p.next;
}
如果鏈表頭不為null
,代碼會遍歷鏈表以查找鍵语稠。遍歷過程中宋彼,使用equals
方法比較當(dāng)前節(jié)點的鍵與要查找的鍵是否相等。如果找到匹配的鍵,則返回對應(yīng)的值宙暇。如果遍歷完整個鏈表都沒有找到匹配的鍵输枯,方法會返回null
。
返回結(jié)果
return null;
如果在哈希表中沒有找到與給定鍵匹配的項占贫,方法最終會返回null
桃熄。
總結(jié)
這個get
方法實現(xiàn)了在哈希表中根據(jù)鍵查找值的基本邏輯。它首先計算鍵的哈希碼對應(yīng)的索引位置型奥,然后檢查該位置是否有鏈表瞳收,并遍歷鏈表以查找匹配的鍵。如果找到厢汹,返回對應(yīng)的值伞广;否則,返回null
毒费。這種方法是哈希表實現(xiàn)中常見的查找邏輯捡需,特別是在處理哈希沖突時使用鏈表作為解決方案時。
哈希表的put方法
void put(int hash,Object key,Object value){
int index = hash & (table.length-1);
if(table[index] == null) {
//如果此處有空位直接新增
table[index] = new Entry(hash, key, value);
}else {
//如果沒有空位搭综,遍歷下面的元素
Entry p = table[index];
while (true) {
//如果此元素下的key與新增的key相同垢箕,則更新元素
if (p.key.equals(key)) {
p.value = value;
return;
}
//如果p的下一個元素為空,則需要在下一個空位上新增兑巾,此時跳出循環(huán)条获,執(zhí)行新增操作
if(p.next==null){
break;
}
p=p.next;
}
//進行新增操作
p.next = new Entry(hash,key,value);
}
//數(shù)組大小變大
size++;
}
你提供的put
方法是一個用于向哈希表中插入鍵值對的實現(xiàn)。下面我將為你詳細解釋這個方法的每個部分:
方法定義
void put(int hash, Object key, Object value) {
這個方法接受三個參數(shù):hash
是鍵的哈希碼蒋歌,key
是要插入的鍵帅掘,value
是與鍵相關(guān)聯(lián)的值。
計算索引
int index = hash & (table.length - 1);
這行代碼與get
方法中的計算索引的方式相同堂油,用于確定鍵應(yīng)該插入到哈希表的哪個位置修档。
檢查索引位置的鏈表頭
if (table[index] == null) {
// 如果此處有空位直接新增
table[index] = new Entry(hash, key, value);
} else {
// 如果沒有空位,遍歷下面的元素
Entry p = table[index];
...
}
首先檢查哈希表中指定索引位置的鏈表頭是否為null
称诗。如果是null
萍悴,說明該位置還沒有鏈表,直接創(chuàng)建一個新的Entry
對象并將其放在該位置寓免。
遍歷鏈表
如果鏈表頭不為null
癣诱,則開始遍歷鏈表:
while (true) {
// 如果此元素下的key與新增的key相同,則更新元素
if (p.key.equals(key)) {
p.value = value;
return;
}
// 如果p的下一個元素為空袜香,則需要在下一個空位上新增撕予,此時跳出循環(huán),執(zhí)行新增操作
if (p.next == null) {
break;
}
p = p.next;
}
遍歷鏈表時蜈首,會檢查每個Entry
的鍵是否與要插入的鍵相等实抡。如果找到相等的鍵欠母,則更新該Entry
的值并返回。如果遍歷到鏈表的末尾(即p.next
為null
)吆寨,則跳出循環(huán)赏淌,準備在鏈表末尾插入新的Entry
。
在鏈表末尾插入新的Entry
// 進行新增操作
p.next = new Entry(hash, key, value);
在鏈表的末尾插入一個新的Entry
對象啄清,將其next
字段設(shè)置為null
六水。
更新哈希表大小
// 數(shù)組大小變大
size++;
總結(jié)
這個put
方法實現(xiàn)了向哈希表中插入鍵值對的基本邏輯。它首先計算鍵的哈希碼對應(yīng)的索引位置辣卒,然后檢查該位置是否有鏈表掷贾。如果沒有鏈表,則直接在該位置創(chuàng)建一個新的Entry
荣茫。如果有鏈表想帅,則遍歷鏈表以查找是否有相同鍵的Entry
。如果找到啡莉,則更新其值港准;如果沒找到,則在鏈表末尾插入新的Entry
票罐。最后叉趣,更新哈希表中鍵值對的數(shù)量。
哈希表的remove方法
Object remove(int hash,Object key){
//根據(jù)hash碼的值找出索引
int index = hash & (table.length-1);
//如果當(dāng)前索引為空该押,那么直接返回空
if(table[index]==null){
return null;
}
//定義兩個指針,一個為前驅(qū)一個為后繼
Entry p = table[index];
//前驅(qū)初始化為null
Entry prve = null;
//當(dāng)p不為null時阵谚,執(zhí)行循環(huán)
while(p!=null) {
//如果找到了key的值相同蚕礼,那么執(zhí)行刪除操作
if (p.key.equals(key)) {
//當(dāng)前驅(qū)為null時,說明只有一個結(jié)點梢什,直接讓索引處的值指向p.next
if (prve==null){
table[index] = p.next;
}//如果前驅(qū)不為null奠蹬,那就讓前驅(qū)的next指向p的next
else {
prve.next = p.next;
}
//執(zhí)行完刪除操作,size減一
size--;
//返回刪除的值
return p.value;
}
//更新prve和p的值
prve = p;
p=p.next;
}
//如果沒有找到相對應(yīng)的值嗡午,返回空
return null;
}
方法定義
java復(fù)制代碼
Object remove(int hash, Object key) {
該方法接受兩個參數(shù):hash
是鍵的哈希碼囤躁,key
是要刪除的鍵。方法返回被刪除的值荔睹,如果鍵不存在則返回null
狸演。
計算索引
java復(fù)制代碼
int index = hash & (table.length - 1);
通過哈希碼和哈希表長度計算鍵在哈希表中的索引位置。
檢查索引位置的鏈表頭
if (table[index] == null) {
return null;
}
如果索引位置的鏈表頭為null
僻他,說明沒有鏈表存在宵距,直接返回null
。
初始化前驅(qū)和后繼指針
Entry p = table[index];
Entry prve = null;
p
用于遍歷鏈表吨拗,prve
用于記錄p
的前一個節(jié)點满哪,初始化為null
婿斥。
遍歷鏈表
while (p != null) {
if (p.key.equals(key)) {
...
}
prve = p;
p = p.next;
}
使用while
循環(huán)遍歷鏈表。如果找到鍵與要刪除的鍵相同的Entry
哨鸭,則執(zhí)行刪除操作民宿;否則,更新前驅(qū)和后繼指針像鸡,繼續(xù)遍歷活鹰。
執(zhí)行刪除操作
if (prve == null) {
table[index] = p.next;
} else {
prve.next = p.next;
}
size--;
return p.value;
當(dāng)找到要刪除的Entry
時,根據(jù)前驅(qū)是否為null
來判斷是刪除鏈表頭還是鏈表中間的節(jié)點坟桅。如果前驅(qū)為null
华望,說明p
是鏈表頭,直接讓索引處的鏈表頭指向p
的下一個節(jié)點仅乓;否則赖舟,讓前驅(qū)的next
指向p
的下一個節(jié)點。無論哪種情況夸楣,都需要將size
減一宾抓,并返回被刪除的值。
返回結(jié)果
return null;
如果遍歷完整個鏈表都沒有找到要刪除的鍵豫喧,則返回null
石洗。
總結(jié)
這個remove
方法實現(xiàn)了從哈希表中刪除指定鍵及其對應(yīng)的值的功能。它首先根據(jù)哈希碼計算鍵的索引位置紧显,然后遍歷該位置的鏈表來查找要刪除的鍵讲衫。找到后,根據(jù)前驅(qū)節(jié)點是否為null
來執(zhí)行不同的刪除操作孵班,并更新哈希表的大小涉兽。如果遍歷完鏈表都沒有找到要刪除的鍵,則返回null
篙程。
哈希表的resize方法
在哈希表中枷畏,當(dāng)鍵值對數(shù)量達到某個閾值時,會進行數(shù)組的擴容(即創(chuàng)建一個新的更大的數(shù)組虱饿,并重新計算所有鍵值對的哈希碼和索引位置)拥诡,但在上面的代碼中,還暫時沒有實現(xiàn)這個方法氮发,接下來就是實現(xiàn)這個方法渴肉,在put方法中,如果達到了這個閾值折柠,就直接調(diào)用resize方法進行擴容宾娜。
private void resize(){
Entry[] newtable = new Entry[table.length<<1];
for(int i = 0;i< table.length;i++){
//拿到每個鏈表頭
Entry p = table[i];
if(p != null){
/*
* 拆分鏈表,移動到新數(shù)組扇售,拆分規(guī)律
* 一個鏈表最多拆分成兩個
* hash & table.length == 0的一組
* hash & table.length != 0的一組
* 例如對于長度為8的table前塔,現(xiàn)在有哈希值為0嚣艇,8,16华弓,24
* 32食零,40,48的七個數(shù)據(jù)
* 那么拆分后寂屏,0贰谣,16,32迁霎,48就為一組吱抚,剩下的為另一組
* */
Entry a = null;
Entry b = null;
Entry ahead = null;
Entry bhead = null;
while(p != null){
if((p.hash&table.length)==0){
if(a!=null){
a.next = p;
}else{
ahead = p;
}
//分配到a
a = p;
}else{
if(b!=null){
b.next=p;
}else{
bhead = p;
}
b = p;
}
p = p.next;
}
if(a!=null){
a.next = null;
newtable[i] = ahead;
}
if(b!=null){
b.next = null;
newtable[i+table.length] = bhead;
}
}
}
}
幾個問題
/*為什么計算索引位置用式子:hash & (數(shù)組長度-1)
為什么舊鏈表會拆分成兩條,一條hash & 舊數(shù)組長度==0考廉,另一條hash & 舊數(shù)組長度!=0
為什么拆分后的兩條鏈表秘豹,一個原索引不變,另一個是原索引+舊數(shù)組長度
它們都有個共同的前提:數(shù)組長度是2的n次方
1.hash % 數(shù)組長度等價于hash & (數(shù)組長度-1)
在十進制中昌粤,對于求10既绕,100,1000這些數(shù)作為除數(shù)的余數(shù)涮坐,只需要看最后幾位就可以了
因為前面的都被整除掉了凄贩。這是十進制求余的一個小規(guī)律,二進制也有類似的鬼綠
30 % 2 = 0袱讹,0011110 % 0000010 = 0000000
30 % 4 = 2, 0011110 % 0000100 = 0000010
30 % 8 = 2, 0011110 % 0001000 = 0000110
30 % 16 = 2, 0011110 % 0010000 = 0001110
30 % 32 = 2, 0011110 % 0100000 = 0011110
對于二進制的數(shù)疲扎,2用二進制表示為10,4為100捷雕,8為1000评肆,以此類推通過上面式子可知,對于二進制來說
求2,4盹廷,8這種是2的n次方的數(shù)的余數(shù),也可以只看后幾位俄占,比如2,只需要看被除數(shù)二進制的最后一位
那么4就要看被除數(shù)的后兩位缸榄。
那么求余數(shù),我們只需要保留后幾位就可以了甚带,對于2佳头,保留后一位晴氨,4,保留后兩位籽前。根據(jù)按位與運算的規(guī)律
與0進行按位與運算,結(jié)果還是0枝哄,與1進行按位與運算,結(jié)果仍是原數(shù)挠锥,那保留后三位众羡,只需要與0000111進行按位與運算即可
而111就是十進制中的7,所以計算索引位置時瘪贱,我們可以用式子hash & (數(shù)組長度-1)纱控,因為數(shù)組長度是2的n次方是前提
2.一條hash & 舊數(shù)組長度==0,另一條hash & 舊數(shù)組長度 != 0
進行與運算其實就是檢查更高位上的數(shù)字是否為1菜秦,如果為1甜害,那么就應(yīng)該是新索引
3.理解了第二個問題,那么第三個問題就迎刃而解了*/
Object.hashCode()方法
Object.hashCode()
是 Java 中所有對象的基類 Object
類的一個方法球昨。該方法用于返回對象的哈希碼值尔店,這個哈希碼通常用于數(shù)據(jù)結(jié)構(gòu),如哈希表(如 HashMap
和 HashSet
)主慰。因為上面的方法中嚣州,哈希值是由人為輸入的,但是人為輸入哈希值共螺,很容易出現(xiàn)沖突的情況该肴,所以可以在此使用hashCode方法進行哈希值的獲取。
基本概念
-
哈希碼(Hash Code):哈希碼是一個整數(shù)藐不,它是通過對象的內(nèi)部信息(通常是對象的字段)計算出來的匀哄。如果兩個對象根據(jù)
equals(Object)
方法是相等的,那么調(diào)用這兩個對象的hashCode
方法必須產(chǎn)生相同的整數(shù)結(jié)果雏蛮。 - 哈希表:哈希表是一種數(shù)據(jù)結(jié)構(gòu)涎嚼,它允許我們以平均常數(shù)時間復(fù)雜度進行插入、刪除和查找操作挑秉。哈希表通過計算鍵(key)的哈希碼來確定元素在表中的位置法梯。
使用注意事項
-
一致性:如果兩個對象根據(jù)
equals(java.lang.Object)
方法是相等的,那么調(diào)用這兩個對象的hashCode
方法必須產(chǎn)生相同的整數(shù)結(jié)果犀概。 - 性能:哈希碼的計算應(yīng)該盡可能快立哑,因為哈希表在插入夜惭、刪除和查找元素時都會使用哈希碼。
- 分布:理想情況下刁憋,哈希碼應(yīng)該均勻地分布在整個整數(shù)范圍內(nèi)滥嘴,以減少哈希沖突并提高哈希表的性能至耻。
String.hashCode()方法
在Java中,String
類的 hashCode()
方法返回該字符串的哈希碼值走触,該值是根據(jù)字符串內(nèi)容計算得出的互广。哈希碼通常用于在哈希表中快速查找鍵惫皱,或者在其他數(shù)據(jù)結(jié)構(gòu)(如哈希集合)中確定元素的唯一性尤莺。
String
類的 hashCode()
方法的設(shè)計保證了:
- 對于兩個不同的字符串颤霎,只要它們的內(nèi)容不同,它們的哈希碼也很可能不同晴音。這有助于在哈希表中區(qū)分不同的鍵锤躁。
- 對于相同的字符串(即內(nèi)容完全相同的字符串)进苍,無論它們是在何時創(chuàng)建的鸭叙,或者在程序中的哪個位置沈贝,它們的哈希碼都是相同的宋下。這保證了哈希表能夠正確地識別相同的鍵。
注意罩引,雖然哈希碼沖突(即不同的字符串具有相同的哈希碼)在理論上是可能的袁铐,但在實際使用中横浑,String
類的 hashCode()
方法設(shè)計得非常出色徙融,使得哈希碼沖突的概率非常低欺冀。
Object的hashCode方法只能生成數(shù)字,而String類的hashCode方法可以生成字符串等饺饭,使用起來更為廣泛砰奕。
力扣算法題
兩數(shù)之和
首先是力扣題庫的第一題
兩數(shù)之和军援,第一次做的時候我是直接使用暴力循環(huán)來做的胸哥,居然沒有超時赡鲜,時間復(fù)雜度為O(n^2)
class Solution {
public int[] twoSum(int[] nums, int target) {
for(int i = 0;i<nums.length;i++){
for(int j = i+1;j<nums.length;j++){
if(nums[i]+nums[j]==target){
return new int[] {i,j};
}
}
}
return new int [0];
}
}
那么既然現(xiàn)在學(xué)習(xí)了哈希表银酬,就可以使用哈希表來進行時間復(fù)雜度為O(1)的算法
使用hash表進行的算法大概思路就是揩瞪,遍歷數(shù)組,然后確定與他相匹配的數(shù)字壹将,在map中查找這個數(shù)字毛嫉,如果能查到承粤,返回這兩個數(shù)字的索引,如果不能查到颜启,那就把這個數(shù)字插入到map中缰盏。
class Solution {
public int[] twoSum(int[] nums, int target) {
HashMap<Integer,Integer> h = new HashMap<>();
for(int i = 0;i< nums.length;i++){
int x = target-nums[i];
if(h.containsKey(x)){
return new int[]{h.get(x),i};
}else{
h.put(nums[i],i);
}
}
return null;
}
}
執(zhí)行用時擊敗了99.35%的用戶口猜,比使用暴力循環(huán)快了很多济炎。
無重復(fù)字符的最長子串
這一題也可以用哈希表來進行求解
class Solution {
public int lengthOfLongestSubstring(String s) {
HashMap<Character, Integer> map = new HashMap<>();
int maxlength = 0;
int start = 0;
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (map.containsKey(c)) {
//start = map.get(c)+1;
start = Math.max(start, map.get(c) + 1);
}
map.put(c, i);
maxlength = Math.max(maxlength, i - start + 1);
}
return maxlength;
}
}
基本思想是先定義兩個指針须尚,一個起始指針耐床,一個末尾指針撩轰,對字符串進行遍歷堪嫂,檢查字符串中是否存在這個字符木柬,如果存在眉枕,就讓起始指針向后移娇唯,在這里有一個點要注意寂玲,按照正常思維來說拓哟,應(yīng)該是把起始指針向重復(fù)的那一位向后再移動一位断序,但是當(dāng)中間有重復(fù)的時违诗,有可能會出現(xiàn)start指針回退的情況疮蹦,所以代碼應(yīng)該修改為
start = Math.max(start, map.get(c) + 1);
字母異位詞分組
class Solution {
public List<List<String>> groupAnagrams(String[] strs) {
HashMap<String,List<String>> map = new HashMap<>();
for (String str:strs){
//把字符串轉(zhuǎn)化為數(shù)組阵苇,方便排序
char[] c = str.toCharArray();
//排序
Arrays.sort(c);
//再把數(shù)組轉(zhuǎn)化為字符串
String s = new String(c);
//確定集合的位置感论,因為每一個映射中都含有一個集合
List<String> list = map.get(s);
//如果集合為空比肄,就新建一個集合,并插入到哈希表中
if(list == null){
list = new ArrayList<>();
map.put(s,list);
}
//在集合中插入數(shù)據(jù)
list.add(str);
}
return new ArrayList<>(map.values());
}
}
基本思路
- 遍歷字符串?dāng)?shù)組掀亥,每個字符串中的字符重新排序后作為key值
- 所謂分組就是準備一個集合铺浇,把這些單詞加入到key相同的集合中
- 返回分組結(jié)果