Java算法之哈希算法

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;
        }
    }
  1. 類定義:
static class Entry {

這里定義了一個靜態(tài)內(nèi)部類Entry瓤漏。靜態(tài)內(nèi)部類意味著這個類不需要外部類的實例就可以被實例化腾夯。在HashMap的實現(xiàn)中,這樣的設(shè)計有助于減少內(nèi)存占用蔬充,因為每個Entry對象不需要持有對外部HashMap對象的引用蝶俱。

  1. 成員變量:
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`字段鏈接在一起谢澈。
  1. 構(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.nextnull)吆寨,則跳出循環(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),如哈希表(如 HashMapHashSet)主慰。因為上面的方法中嚣州,哈希值是由人為輸入的,但是人為輸入哈希值共螺,很容易出現(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)的哈希碼來確定元素在表中的位置法梯。

使用注意事項

  1. 一致性:如果兩個對象根據(jù) equals(java.lang.Object) 方法是相等的,那么調(diào)用這兩個對象的 hashCode 方法必須產(chǎn)生相同的整數(shù)結(jié)果犀概。
  2. 性能:哈希碼的計算應(yīng)該盡可能快立哑,因為哈希表在插入夜惭、刪除和查找元素時都會使用哈希碼。
  3. 分布:理想情況下刁憋,哈希碼應(yīng)該均勻地分布在整個整數(shù)范圍內(nèi)滥嘴,以減少哈希沖突并提高哈希表的性能至耻。

String.hashCode()方法

在Java中,String 類的 hashCode() 方法返回該字符串的哈希碼值走触,該值是根據(jù)字符串內(nèi)容計算得出的互广。哈希碼通常用于在哈希表中快速查找鍵惫皱,或者在其他數(shù)據(jù)結(jié)構(gòu)(如哈希集合)中確定元素的唯一性尤莺。

String 類的 hashCode() 方法的設(shè)計保證了:

  1. 對于兩個不同的字符串颤霎,只要它們的內(nèi)容不同,它們的哈希碼也很可能不同晴音。這有助于在哈希表中區(qū)分不同的鍵锤躁。
  2. 對于相同的字符串(即內(nèi)容完全相同的字符串)进苍,無論它們是在何時創(chuàng)建的鸭叙,或者在程序中的哪個位置沈贝,它們的哈希碼都是相同的宋下。這保證了哈希表能夠正確地識別相同的鍵。

注意罩引,雖然哈希碼沖突(即不同的字符串具有相同的哈希碼)在理論上是可能的袁铐,但在實際使用中横浑,String 類的 hashCode() 方法設(shè)計得非常出色徙融,使得哈希碼沖突的概率非常低欺冀。

Object的hashCode方法只能生成數(shù)字,而String類的hashCode方法可以生成字符串等饺饭,使用起來更為廣泛砰奕。

力扣算法題

兩數(shù)之和

首先是力扣題庫的第一題

QQ截圖20240331081827.png

兩數(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;
    }
}
QQ截圖20240331083538.png

執(zhí)行用時擊敗了99.35%的用戶口猜,比使用暴力循環(huán)快了很多济炎。

無重復(fù)字符的最長子串

QQ截圖20240331083726.png

這一題也可以用哈希表來進行求解

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);  

字母異位詞分組

QQ截圖20240331091123.png
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());
    }
}

基本思路

  1. 遍歷字符串?dāng)?shù)組掀亥,每個字符串中的字符重新排序后作為key值
  2. 所謂分組就是準備一個集合铺浇,把這些單詞加入到key相同的集合中
  3. 返回分組結(jié)果
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鳍侣,一起剝皮案震驚了整個濱河市倚聚,隨后出現(xiàn)的幾起案子惑折,更是在濱河造成了極大的恐慌,老刑警劉巖白热,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屋确,死亡現(xiàn)場離奇詭異续扔,居然都是意外死亡纱昧,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門设联,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仑荐,“玉大人粘招,你說我怎么就攤上這事洒扎∷バ酰” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵胡诗,是天一觀的道長煌恢。 經(jīng)常有香客問我瑰抵,道長二汛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任氓栈,我火速辦了婚禮颤绕,結(jié)果婚禮上祟身,老公的妹妹穿的比我還像新娘袜硫。我一直安慰自己婉陷,他們只是感情好官研,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著担神,像睡著了一般妄讯。 火紅的嫁衣襯著肌膚如雪酷宵。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音男韧,去河邊找鬼。 笑死仍劈,一個胖子當(dāng)著我的面吹牛贩疙,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播组民,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼臭胜,長吁一口氣:“原來是場噩夢啊……” “哼癞尚!你這毒婦竟也來了浇揩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤积锅,失蹤者是張志新(化名)和其女友劉穎缚陷,沒想到半個月后箫爷,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體铆铆,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡翁都,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年柄慰,在試婚紗的時候發(fā)現(xiàn)自己被綠了税娜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖弧岳,靈堂內(nèi)的尸體忽然破棺而出业踏,到底是詐尸還是另有隱情勤家,我是刑警寧澤伐脖,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布讼庇,位于F島的核電站近尚,受9級特大地震影響肿男,放射性物質(zhì)發(fā)生泄漏舶沛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一如庭、第九天 我趴在偏房一處隱蔽的房頂上張望坪它。 院中可真熱鬧往毡,春花似錦、人聲如沸开瞭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至苍狰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間土榴,已是汗流浹背玷禽。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工呀打, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人撩银。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓额获,卻偏偏與公主長得像恭应,于是被迫代替她去往敵國和親昼榛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容