最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java數(shù)據(jù)機(jī)構(gòu)中關(guān)于并查集的詳解

 更新時(shí)間:2021年09月15日 15:42:46   作者:飛人01_01  
并查集是一種樹(shù)型的數(shù)據(jù)結(jié)構(gòu),用于處理一些不相交集合的合并及查詢問(wèn)題,如果你還不了解并查集,請(qǐng)看接下來(lái)的文章,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值

image-20210905162056592

本期文章源碼:GitHub

一文徹底搞懂《并查集》!

概念

并查集是一種樹(shù)型的數(shù)據(jù)結(jié)構(gòu),用于處理一些不相交集合的合并及查詢問(wèn)題(即所謂的并、查)。比如說(shuō),我們可以用并查集來(lái)判斷一個(gè)森林中有幾棵樹(shù)、某個(gè)節(jié)點(diǎn)是否屬于某棵樹(shù)等。

具體的用法,我們會(huì)以下一篇文章《圖的相關(guān)算法》中,有一個(gè)克魯斯卡爾算法,用于生成最小生成樹(shù),會(huì)用到并查集。

并查集的主要作用是求連通分支數(shù)(如果一個(gè)圖中所有點(diǎn)都存在可達(dá)關(guān)系(直接或間接相連),則此圖的連通分支數(shù)為1;如果此圖有兩大子圖各自全部可達(dá),則此圖的連通分支數(shù)為2……)

在現(xiàn)實(shí)生活中,也是存在著并查集的一些概念,例如最近《天龍八部》里的人物關(guān)系,可能你并不認(rèn)識(shí)丐幫的一些小人物,但是你一定認(rèn)識(shí)丐幫幫主喬峰。當(dāng)你看見(jiàn)一個(gè)叫花子,你就會(huì)想到他的老大就是幫主喬峰,像這樣的場(chǎng)景,就有了一定的歸屬感, 會(huì)自動(dòng)的認(rèn)為叫花子就是跟丐幫合并在一起的……

image-20210905145035613

說(shuō)簡(jiǎn)單一點(diǎn),并查集就是將一些數(shù)據(jù)進(jìn)行分類,這樣數(shù)據(jù)為一組,那些數(shù)據(jù)為另一組。如何判斷其中兩個(gè)數(shù)據(jù),在不在一個(gè)組?我們就會(huì)去找每個(gè)組的代表,看這兩個(gè)數(shù)據(jù)的代表是不是同一個(gè)?如果是,那就是在一個(gè)組;如果不是,那就不在一個(gè)組。所以并查集的大致框架就是下面這樣:

//并查集大致框架---代碼中的數(shù)據(jù)(Node),可以是其他,比如二叉樹(shù)節(jié)點(diǎn)、圖的邊、節(jié)點(diǎn)等等 抽象的數(shù)據(jù)
public class UnionSet {
    private HashMap<Node, Node> fatherMap; //key表示當(dāng)前這個(gè)數(shù)據(jù),value表示這個(gè)數(shù)據(jù)的代表(父親)是誰(shuí)
    private HashMap<Node, Integer> sizeMap; //表示當(dāng)前這個(gè)組(集合)的大小
    
    public UnionSet() { //構(gòu)造方法
        fatherMap = new HashMap<>();
        sizeMap = new HashMap<>();
    }
    
    public void makeSet(List<Node> list) { //生成初始化狀態(tài)的并查集,剛開(kāi)始每個(gè)數(shù)據(jù)都是獨(dú)立的
        
    }
    
    public boolean isSameSet(Node node1, Node node2) { //判斷當(dāng)前這兩個(gè)數(shù)據(jù),是不是一個(gè)組的?
        
    }
    
    private Node findFather(Node node) { //查找這個(gè)數(shù)據(jù),它那個(gè)組的代表(父親)是誰(shuí)?
        
    }
    
    public void union(Node node1, Node node2) { //將這兩個(gè)數(shù)據(jù),放到一個(gè)組
         
    }
}

上面就是大致的框架,就是幾個(gè)方法:初始化并查集、判斷是不是一個(gè)組、查找代表、合并到一個(gè)組。四個(gè)方法,就是并查集。簡(jiǎn)不簡(jiǎn)單?

并查集在判斷兩個(gè)數(shù)據(jù),是否在一個(gè)組時(shí),時(shí)間復(fù)雜度能做到O(1),所以這種數(shù)據(jù)結(jié)構(gòu)還是非常有用的。

實(shí)現(xiàn)

初始化并查集

我們首先從第一個(gè)方法:初始化并查集開(kāi)始。

傳入進(jìn)去的參數(shù)不一定是List,也可以是Collection等等,表示一組數(shù)據(jù)即可! 首先我們的成員變量只有兩個(gè),分別是存儲(chǔ)節(jié)點(diǎn)的代表 和 當(dāng)前這個(gè)組的大小。初始化時(shí),我們分別認(rèn)為 每個(gè)節(jié)點(diǎn)是自己一個(gè)人一組的,也就是說(shuō),自己一個(gè)組,代表就是自己本身;大小的話,就是自己本身咯,也就是1。

//初始化并查集
public void makeSet(List<Node> list) {
    if (list == null) {
        return;
    }
    fatherMap.clear();
    sizeMap.clear(); //先將表清空
    
    //遍歷list,把每一個(gè)節(jié)點(diǎn),都放入哈希表中
    for (Node node : list) {
        fatherMap.put(node, node); //第一個(gè)參數(shù)是節(jié)點(diǎn)本身,第二個(gè)參數(shù)就是這個(gè)組的代表
        sizeMap.put(node, 1); //第一個(gè)參數(shù)是這個(gè)組的代表,第二個(gè)參數(shù)是大小
    }
}

image-20210905153047158

判斷是不是同一個(gè)組

isSameSet 比較簡(jiǎn)單,就是判斷兩個(gè)數(shù)據(jù)所在的組的代表,是不是用一個(gè)數(shù)據(jù)即可!如果代表是同一個(gè)人,那就是在一個(gè)組,反之就不是!

//判斷是不是同一個(gè)組
public boolean isSameSet(Node node1, Node node2) {
    if (node1 == null || node2 == null) {
        return false;
    }
    return findFather(node1) == findFather(node2); //查找各自的代表節(jié)點(diǎn),看是不是同一個(gè)。
}

查找當(dāng)前節(jié)點(diǎn)的代表節(jié)點(diǎn)

findFather,我自己覺(jué)得算是并查集的核心,也這是這個(gè)方法,是并查集的查找的時(shí)間復(fù)雜度能在O(1)的主要因素。

思路就跟二叉樹(shù)向上查找根結(jié)點(diǎn)的思路一樣,也就是說(shuō),在fatherMap中一直查找,直到一個(gè)節(jié)點(diǎn)的代表節(jié)點(diǎn)(父節(jié)點(diǎn))是它自己本身時(shí),此時(shí)就查找完了;然后最關(guān)鍵的一步,就是路徑壓縮,在我們向上查找的過(guò)程中,我們需要記錄沿途的所有節(jié)點(diǎn),在查找結(jié)束后,我們將沿途的這些節(jié)點(diǎn),在fatherMap中的進(jìn)行修改,直接將這些節(jié)點(diǎn)的代表節(jié)點(diǎn),寫成這個(gè)組的代表節(jié)點(diǎn),可能聽(tīng)糊涂了,看下圖:

image-20210905155005868

這樣的設(shè)計(jì),就能使查找的時(shí)間復(fù)雜度控制在O(1)。

//查找代表節(jié)點(diǎn),并做路徑壓縮
private Node findFather(Node node) {
    if (node == null) {
        return null;
    }
    //查找代表節(jié)點(diǎn)
    Stack<Node> path = new Stack<>(); //存儲(chǔ)沿途的節(jié)點(diǎn)
    while (node != fatherMap.get(node)) { //代表節(jié)點(diǎn)不是自己本身,就繼續(xù)查找
        path.push(node);
        node = fatherMap.get(node);
    }
    //路徑壓縮
    while (!path.isEmpty()) {
        Node tmp = path.pop();
        fatherMap.put(tmp, node); //此時(shí)的node,就是這個(gè)組的代表節(jié)點(diǎn)
    }
    
    return node;
}

合并操作

終于來(lái)到了最后的操作:合并。合并也比較簡(jiǎn)單,記住一個(gè)要點(diǎn):小組掛在大組的下面。也就是說(shuō),這一個(gè)節(jié)點(diǎn)所在的組要小一點(diǎn),我們直接將他“掛”在另一個(gè)組的下面。說(shuō)簡(jiǎn)單一點(diǎn):這一個(gè)組的代表節(jié)點(diǎn)的vaule域,直接指向另一個(gè)組的代表節(jié)點(diǎn)。

//合并操作
public void union(Node node1, Node node2) {
    if (node1 == null || node2 == null) {
        return;
    }
    int node1Size = sizeMap.get(node1);
    int node2Size = sizeMap.get(node2); //分別得到兩個(gè)節(jié)點(diǎn)所在組的大小
    Node node1Father = fatherMap.get(node1);
    Node node2Father = fatherMap.get(node2); //分別拿到兩個(gè)節(jié)點(diǎn)的代表節(jié)點(diǎn)
    
    if (node1Father != node2Father) { //兩個(gè)節(jié)點(diǎn),不在同一個(gè)組,就合并
        if (node1Size < node2Size) { //node1 掛在 node2
            fatherMap.put(node1Father, node2Father);
            sizeMap.put(node2Father, node1Size + node2Size); //新的組,大小是原來(lái)兩個(gè)組的和
            sizeMap.remove(node1Father); //小組的數(shù)據(jù),就不需要了,刪除
        } else { //node2 掛在 node1
            //跟上面操作類似
            fatherMap.put(node2Father, node1Father); 
            sizeMap.put(node1Father, node1Size + node2Size);
            sizeMap.remove(node1Father);
        }
    }
}

上面就是并查集的所有操作了,是不是很簡(jiǎn)單呢?

好啦,本期更新到此就結(jié)束啦,同學(xué)們,下期見(jiàn)?。?!

到此這篇關(guān)于Java數(shù)據(jù)機(jī)構(gòu)中關(guān)于并查集的詳解的文章就介紹到這了,更多相關(guān)Java 數(shù)據(jù)機(jī)構(gòu) 并查集內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java Spring數(shù)據(jù)單元配置過(guò)程解析

    Java Spring數(shù)據(jù)單元配置過(guò)程解析

    這篇文章主要介紹了Java Spring數(shù)據(jù)單元配置過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-12-12
  • Java日志框架之logback使用詳解

    Java日志框架之logback使用詳解

    這篇文章主要介紹了Java日志框架之logback使用詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • 圖文詳解mybatis+postgresql平臺(tái)搭建步驟

    圖文詳解mybatis+postgresql平臺(tái)搭建步驟

    從頭開(kāi)始搭建一個(gè)mybatis+postgresql平臺(tái),這篇文章主要介紹了圖文詳解mybatis+postgresql平臺(tái)搭建步驟,感興趣的小伙伴們可以參考一下
    2016-07-07
  • this關(guān)鍵字詳解

    this關(guān)鍵字詳解

    這篇文章主要介紹了this關(guān)鍵字,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Idea2023創(chuàng)建springboot不能選擇java8的解決方法(最新推薦)

    Idea2023創(chuàng)建springboot不能選擇java8的解決方法(最新推薦)

    在idea2023版本創(chuàng)建springboot的過(guò)程中,選擇java版本時(shí)發(fā)現(xiàn)沒(méi)有java8版本,只有java17和java20,遇到這樣的問(wèn)題如何解決呢,下面小編給大家分享Idea2023創(chuàng)建springboot不能選擇java8的解決方法,感興趣的朋友一起看看吧
    2024-01-01
  • Java中的gateway自定義過(guò)濾器詳解

    Java中的gateway自定義過(guò)濾器詳解

    這篇文章主要介紹了Java中的gateway自定義過(guò)濾器詳解,過(guò)濾器是指gateway在路由過(guò)程中(A地址路由到B地址)生效進(jìn)行過(guò)濾操作的,所有首先你得先配一個(gè)地址路由,本文提供了部分實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2023-11-11
  • SpringBoot統(tǒng)一響應(yīng)和統(tǒng)一異常處理詳解

    SpringBoot統(tǒng)一響應(yīng)和統(tǒng)一異常處理詳解

    在開(kāi)發(fā)Spring Boot應(yīng)用時(shí),處理響應(yīng)結(jié)果和異常的方式對(duì)項(xiàng)目的可維護(hù)性、可擴(kuò)展性和團(tuán)隊(duì)協(xié)作有著至關(guān)重要的影響,統(tǒng)一結(jié)果返回和統(tǒng)一異常處理是提升項(xiàng)目質(zhì)量的關(guān)鍵策略之一,所以本文給大家詳細(xì)介紹了SpringBoot統(tǒng)一響應(yīng)和統(tǒng)一異常處理,需要的朋友可以參考下
    2024-08-08
  • Java編程中的檢查型異常與非檢查型異常分析

    Java編程中的檢查型異常與非檢查型異常分析

    這篇文章主要介紹了Java編程中的未檢查型異常與非檢查型異常,以及異常的處理方式,需要的朋友可以參考下
    2017-09-09
  • java實(shí)現(xiàn)ReadWriteLock讀寫鎖的示例

    java實(shí)現(xiàn)ReadWriteLock讀寫鎖的示例

    ReadWriteLock是Java并發(fā)包中的接口,定義了讀鎖和寫鎖,讀鎖允許多線程同時(shí)訪問(wèn)共享資源,而寫鎖則要求獨(dú)占,這種機(jī)制適用于讀多寫少的場(chǎng)景,可以提高并發(fā)效率同時(shí)保證數(shù)據(jù)一致性,本文就來(lái)詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下
    2024-09-09
  • java分布式緩存方案

    java分布式緩存方案

    本文簡(jiǎn)單介紹了緩存的種類,緩存常見(jiàn)的問(wèn)題,對(duì)大家的學(xué)習(xí)和工作有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2021-08-08

最新評(píng)論

元阳县| 淮安市| 嘉祥县| 沧源| 永吉县| 刚察县| 高邮市| 新昌县| 铁力市| 抚远县| 松阳县| 邯郸县| 唐山市| 元阳县| 始兴县| 晴隆县| 吉安市| 浦江县| 沙河市| 北流市| 芜湖市| 盖州市| 延长县| 芦溪县| 西乌珠穆沁旗| 汝州市| 泽州县| 青铜峡市| 临武县| 武川县| 上林县| 双鸭山市| 阿克陶县| 潍坊市| 墨竹工卡县| 雷山县| 望江县| 昭觉县| 通辽市| 金门县| 永州市|