算法-並查集/DFS/BFS-朋友圈

算法-並查集/DFS/BFS-朋友圈

1 題目概述

1.1 題目出處

https://leetcode-cn.com/problems/friend-circles/

1.2 題目描述

班上有 N 名學生。其中有些人是朋友,有些則不是。他們的友誼具有是傳遞性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那麼我們可以認爲 A 也是 C 的朋友。所謂的朋友圈,是指所有朋友的集合。

給定一個 N * N 的矩陣 M,表示班級中學生之間的朋友關係。如果M[i][j] = 1,表示已知第 i 個和 j 個學生互爲朋友關係,否則爲不知道。你必須輸出所有學生中的已知的朋友圈總數。

示例 1:

輸入:
[[1,1,0],
[1,1,0],
[0,0,1]]
輸出: 2
說明:已知學生0和學生1互爲朋友,他們在一個朋友圈。
第2個學生自己在一個朋友圈。所以返回2。
示例 2:

輸入:
[[1,1,0],
[1,1,1],
[0,1,1]]
輸出: 1
說明:已知學生0和學生1互爲朋友,學生1和學生2互爲朋友,所以學生0和學生2也是朋友,所以他們三個在一個朋友圈,返回1。

注意:

  • N 在[1,200]的範圍內。
  • 對於所有學生,有M[i][i] = 1。
  • 如果有M[i][j] = 1,則有M[j][i] = 1。

2 並查集

2.1 思路

遍歷每個學生,並從該學生的下一個序號開始處理,只要值爲1代表兩個學生爲朋友,此時就進行並查集合並代表同屬一個朋友圈。

最後返回並查集內不同root祖先個數,即得到朋友圈個數。

2.2 代碼

class Solution {
    public int findCircleNum(int[][] M) {
        if(M == null || M.length == 0){
            return 0;
        }
        // 並查集,大小爲學生個數
        int[] unionFindSet = new int[M.length];

        // 初始化並查集值爲元素序號本身
        for(int i = 1; i < unionFindSet.length; i++){
            unionFindSet[i] = i;
        }

        // 按行遍歷每個學生
        for(int i = 0; i < unionFindSet.length; i++){
            // 從每個學生的下一個序號開始遍歷處理朋友關係
            for(int j = i + 1; j < unionFindSet.length; j++){
                // 只要值爲1,代表i學生和j號學生是朋友,則將他們的並查集合並
                if(M[i][j] == 1){
                    join(unionFindSet, i, j);
                }
            }
        }

        // 用來統計朋友圈的HashSet
        Set<Integer> filterSet = new HashSet<>();

        // 遍歷並查集,看不同的root祖先有幾個就知道有幾個不相連的並查集
        // 即有幾個朋友圈
        for(int id : unionFindSet){
            filterSet.add(search(unionFindSet, id));
        }

        return filterSet.size();
    }

    /**
     * 將兩個元素的祖先節點所在並查集合並
     */
    private void join(int[] unionFindSet, int p, int q){
        //
        int left = search(unionFindSet,p);
        int right = search(unionFindSet,q);
        if (left == right){
            // 同一並查集
            return;
        }
        // 將p元素的root祖先節點的祖先設爲q元素的root祖先節點
        // 這樣就合併爲了一個並查集
        unionFindSet[left] = right;
    }

    /**
     * 尋找並查集root祖先
     */
    private int search(int[] unionFindSet, int p){
        int son = p;
        // 從當前節點開始迭代查找祖先
        // 直到某個元素祖先節點是自己,那就是找到了root祖先元素
        while(p != unionFindSet[p]){
            p = unionFindSet[p];
        }
        // 路徑壓縮算法,祖先的祖先全部設爲p,高度變爲2
        while(son != p){
            int tmp = unionFindSet[son];
            unionFindSet[son] = p;
            son = tmp;
        }
        // 返回root祖先元素
        return p;
    }
}

2.3 時間複雜度

在這裏插入圖片描述

2.4 空間複雜度

O(N)

3 圖-DFS

3.1 思路

按行遍歷學生,只要沒訪問過就說明屬於一個新的獨立朋友圈,count加1。此時,開始從下標爲i+1的開始遍歷其他學生j,只要M[i,j] == 1 說明是朋友,開始對學生j dfs。一趟dfs中遍歷到的學生都屬於這次i學生的朋友圈,只計數1次。

最後返回count即可。

3.2 代碼

class Solution {
    public int findCircleNum(int[][] M) {
        if(M == null || M.length == 0){
            return 0;
        }
        // 朋友圈個數
        int count = 0;

        // 記錄是否已遍歷
        int[] visited = new int[M.length];
        
        // 按行遍歷每個學生
        for(int i = 0; i < M.length; i++){
            if(visited[i] == 1){
                continue;
            }
            // 沒人訪問過該學生,說明該學生目前是個獨立朋友圈
            // 那麼此時遍歷到的關係爲1的學生以及由此進行dfs訪問到的學生同屬i學生的一個朋友圈
            count++;
            // 標記該學生已訪問過
            visited[i] = 1;
            // 從每個學生的下一個序號開始遍歷處理朋友關係
            for(int j = i + 1; j < M.length; j++){
                // 只要值爲1,代表i學生和j號學生是朋友,則將他們的朋友圈合併
                if(visited[j] == 0 && M[i][j] == 1){
                    dfs(M, j, visited);
                }
            }
        }

        return count;
    }

    /**
     * i作爲朋友圈代號
     * j爲當前遍歷學生序號
     */
    private void dfs(int[][] M, int j, int[] visited){
        visited[j] = 1;
        // 這裏注意要出0開始,因爲有可能還沒訪問過的、序號更小的節點和當前節點是朋友
        // 原因是採用了DFS,無順序性!!
        for(int k = 0; k < M.length; k++){
            // 只要值爲1,代表j學生和k號學生是朋友,則將他們的朋友圈合併
            if(visited[k] == 0 && M[j][k] == 1){
                dfs(M, k, visited);
            }
        }
    }
}

3.3 時間複雜度

在這裏插入圖片描述

3.4 空間複雜度

O(N)

  • 因爲需要遞歸

4 BFS

4.1 思路

4.2 代碼


4.3 時間複雜度

參考文檔

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章