close

這題會給你一個undirected graph,而且是沒有cycle的

要找出多餘的連線,比如說:

Input: [[1,2], [1,3], [2,3]]
Output: [2,3]
Explanation: The given undirected graph will be like this:
  1
 / \
2 - 3

[1,2]就是表示1和2之間有連線,[1,3]就是表示1和3有連線,以此類推

另外裡面的數值就只會有1~N,不會重複

再舉個例子吧:

Input: [[1,2], [2,3], [3,4], [1,4], [1,5]]
Output: [1,4]
Explanation: The given undirected graph will be like this:
5 - 1 - 2
    |   |
    4 - 3

大概是這樣

 

Sol:

這題我先說,我一開始完全沒概念

真的完全不知道怎麼辦

大概只覺得可以一個一個建graph出來,然後邊建邊找這樣

不過我不太會熟graph...,但這題也可以這麼做就是

關於graph的題目,之後再來解幾題看看吧

 

這題主要用的概念是要用Union set

簡單說就是集合的概念

把每連結在一起的node當成一個集合,大概的意思是怎樣呢?

簡單說可以列一個大小為N的陣列來記錄每個node屬於哪個集合

在初始化的時候,每個node都屬於各自的集合:

int parent[N+1];  // 這邊只是為了方便,因為沒有0這個node

for (int i = 0; i <= N; ++i)

    parent[i] = i;

 

接下來先看一下怎麼推吧,就拿第一個例子來說好了

Input: [[1,2], [1,3], [2,3]]
Output: [2,3]
Explanation: The given undirected graph will be like this:
  1
 / \
2 - 3

初始化的parent為[0,1,2,3]

第一組為[1,2],表示1和2為同一個集合,所以把2的集合改為1,表示他們都是屬於1的這個集合

parent就會變成[0,1,1,3]

第二組為[1,3],表示1和3為同一個集合,所以把3的集合改為1,表示他們都是屬於1的這個集合

parent就會變成[0,1,1,1]

最後一組為[2,3],這邊一檢查就會發現他們都是屬於1的這個集合,所以[2,3]就是多餘的

 

大概是這個概念,只是光看這個例子會有陷阱

因為上面的例子裏沒有"要把兩個集合連在一起的情況"

比如說1,2是一個集合,3,4是一個集合,parent是[0,1,1,3,3...]

如果出現一個[1,3]的連結,那就要把1和3兩個集合連在一起

這時候怎麼做呢?

如果要把3和4都標成1的話也可以,不過其實可以把3的集合標為1就好了

只要改掉最源頭的那個node的標記就可以了,寫一個find()的函式來尋找node集合的源頭就行了:

    int find(int i, const vector<int>& parent)
    {
        if (parent[i] == i)
        {
            return i;
        }
        else
        {
            return find(parent[i], parent);
        }
        
        return 0;
    }

所以我們可以靠這個find()來找到node是屬於哪個集合

 

好吧,其實重點就是上面這個函式

有了這個東西,這題其實就解完了,大概長這樣:

    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        vector<int> parent;
        
        for(int i = 0; i <= edges.size(); ++i)  // 初始化,這邊是用vector
        {
            parent.push_back(i);
        }
        
        for (vector<int> vi : edges)  // vi表示node0和node1之間的連結
        {
            if (find(vi[0], parent) == find(vi[1], parent))  // 如果兩個node的集合是同一個的話,表示那是不必要的連結
            {
                return vi;
            }
            else
            {
                parent[find(vi[1], parent)] = find(vi[0], parent);  // 把node1的集合標成node0的集合,其實倒過來也行
            }
        }
       
        return vector<int>{0, 0};
    }

這就是比較快速的方法,接下來看一下用graph的方法好了

 

graph的方法稍微不適合這題,但就當作練習吧XD

相對於上一個方法存每個node的集合,這邊會存每個node連結到哪個node:

unordered_map<int, vector<int>> adjList;

簡單說如果1和2,3,4相連,adjList[1]的內容就會是[2,3,4]

透過這個adjList,我們可以查詢是否有多餘的連結

 

再來就是怎麼去搜尋graph了,必須要有一個陣列之類的結構來記錄那些節點是否被走過

我們稱它為color吧:

unordered_set<int> color;

所以code大概可以長成這樣:

    bool hasPath(unordered_map<int, vector<int>>& adjList, 
             const vector<int>& edge, 
             unordered_set<int>& color)
    {
        int from = edge[0];  // 起點和終點
        int to = edge[1];
        
        if (from == to)  // 表示兩個是一樣的點,當然就是相通的
            return true;
        
        color.insert(from);  // 在color裡加入from,表示這個點走過了
        
        for (const int& adj : adjList[from])  // 去找從from開始的連結
        {
            if (color.count(adj) == 0)  // 如果這個連結還沒被找過
            {
                if (hasPath(adjList, vector<int>{adj, to}, color))  // 就從這個連結繼續往to找,這邊用遞迴就可以很方便的一直找下去了
                    return true;
            }
        }
        
        return false;
    }
    
    vector<int> findRedundantConnection(vector<vector<int>>& edges) {
        unordered_map<int, vector<int>> adjList;

        for (vector<int> edge : edges)  // 找每個edge
        {
            unordered_set<int> color;  // 初始化color
            
            if (hasPath(adjList, edge, color))  // 如果edge裡的兩個node是連在一起的,表示他就是多餘的
            {
                return edge;
            }
            
            adjList[edge[0]].push_back(edge[1]);  // 把edge裡的連結加到adjList中
            adjList[edge[1]].push_back(edge[0]);
        }
        
        return vector<int>{0, 0};
    }

好吧,其實解釋完之後好像也不難懂吧

只是要靠自己想出來... 就要有點實力了

不過一開始總是要先參考別人的嘛,我又不是天才對吧XD

 

那這題就這樣了~下題再見~

arrow
arrow
    全站熱搜

    頁頁頁六滴 發表在 痞客邦 留言(0) 人氣()