LinkedListComponents [source code]


public class LinkedListComponents {
static
/******************************************************************************/
class Solution {
    public int numComponents(ListNode head, int[] G) {
        int N = G.length;
        Map<Integer, Set<Integer>> adj_lists = new HashMap<> ();
        for (int node : G) {
            adj_lists.put (node, new HashSet<> ());
        }
        while (head != null) {
            if (head.next != null) {
                if (adj_lists.containsKey (head.val) && adj_lists.containsKey (head.next.val)) {
                    adj_lists.get (head.val).add (head.next.val);
                    adj_lists.get (head.next.val).add (head.val);
                }
            }
            head = head.next;
        }
        Set<Integer> visited = new HashSet<> ();
        int num_cc = 0;
        for (int node : adj_lists.keySet ()) {
            if (!visited.contains (node)) {
                dfs (node, visited, adj_lists);
                num_cc++;
            }
        }
        return num_cc;
    }

    void dfs (int root, Set<Integer> visited, Map<Integer, Set<Integer>> adj_lists) {
        visited.add (root);
        for (int neighbor : adj_lists.get (root)) {
            if (!visited.contains (neighbor))
                dfs (neighbor, visited, adj_lists);
        }
    }
}
/******************************************************************************/

    public static void main(String[] args) {
        LinkedListComponents.Solution tester = new LinkedListComponents.Solution();
    }
}

读完题目感觉不难, 建一个graph然后走DFS找CC就行了;

也是很轻松解决了;

这题其实想复杂了, 真正的做法其实就是一个分段算法就行了; 这个是一个LinkedList, 单向的, 所以如果是CC, 就是连在一起, 很简单的, 所以只要走一遍, 看分段, 就完事了;

当然, 我这个笨办法就当做是练习基本功了, 不过这种做法面试的时候多半是要被喷的;

注意当我从这个LinkedList找graph的时候, 还是站在parent的地方来看link, 这个也是一个很基本的LinkedList操作习惯了;


editorial

Approach #1: Grouping [Accepted]

Intuition

Instead of thinking about connected components in G, think about them in the linked list. Connected components in G must occur consecutively in the linked list.

没错, 这题其实本身是不难, 怎么做都能做出来, 关键就是你能不能想到一个最优的解法; 这种题目也挺奇葩的. 如果你拿到题目立刻就跟我一样, 被CC的思路给带跑偏了, 那么这题直接最优解的机会就没有了;

Algorithm

Scanning through the list, if node.val is in G and node.next.val isn't (including if node.next is null), then this must be the end of a connected component.

For example, if the list is 0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7, and G = [0, 2, 3, 5, 7], then when scanning through the list, we fulfill the above condition at 0, 3, 5, 7, for a total answer of 4.

class Solution {  
    public int numComponents(ListNode head, int[] G) {  
        Set<Integer> Gset = new HashSet();  
        for (int x: G) Gset.add(x);  

        ListNode cur = head;  
        int ans = 0;  

        while (cur != null) {  
            if (Gset.contains(cur.val) &&  
                    (cur.next == null || !Gset.contains(cur.next.val)))  
                ans++;  
            cur = cur.next;  
        }  

        return ans;  
    }  
}

他这个写法你仔细看, 是要处理收尾的; 不过他比较狡猾, 他用prelook的思路, 在一个逻辑位置直接把收尾一起处理了: 如果是下降沿, 或者后面没有了: 就开始trigger收分段的操作; 这个操作实际上我自己也用过, 就是最近ML的hw5写的那个小脚本的时候, 我就用的这个思路了;

复杂度是O(N + G.length). 分段算法嘛. 注意这个加法不能忽略, preprocess的时间也要算的;


UNFINISHED


uwi:

class Solution {  
    public int numComponents(ListNode head, int[] G) {  
        boolean[] hit = new boolean[10005];  
        for(int v : G)hit[v] = true;  

        int rep = 0;  
        int con = 0;  
        while(head != null){  
            if(hit[head.val]){  
                if(rep == 0)con++;  
                rep++;  
            }else{  
                rep = 0;  
            }  

            head = head.next;  
        }  
        return con;  
    }  
}

看来是有点门道的, 我这个肯定是做复杂了;

这个就是分段算法, 不过这里是偷师成功, 看到了uwi的分段算法的思路; 另外他这个hit数组其实就是冒充set来用, 这个就是他比赛的时候抢分的小技巧, 不用管; 面试的时候别搞这种事情;

主要是看他的这个分段算法;

两个变量含义不难理解, rep就是当前如果在一个run里面, 这个run的长度, 然后con就是包括当前run, 已经得到的run的个数;

然后逻辑就比较简单; 他更新con的时间点, 是根据进入run的上升沿来判断: 如果我们看到一个hit, 然后下面rep是肯定要++的, 但是我们判断一下这个++是不是0->1的上升沿, 如果是的话, 说明con也要++了; 然后如果没有hit, 把rep给清空就行了, 这个在比如1 1 1 0 0 0 1 1 1 这样的, 中间段的三个0每一次都要做一个, 不过最后也是不复杂; 这样一个分段算法真的是相当的简练; 核心就是要理解, rep这个东西看起来好像对分段算法本身没有意义, 但是用他来维护con, 要比自己直接在收尾的时候去维护, 用trigger思维来做要方便的多;

cchao:

class Solution {  
public:  
    int numComponents(ListNode* head, vector<int>& G) {  
        set<int> s;  
        for (int x : G) s.insert(x);  
        bool vis = false;  
        int ans = 0;  
        for (ListNode *x = head; x != nullptr; x = x->next) {  
            bool t = s.count(x->val) != 0;  
            if (!vis && t) {  
                ans++;  
            }  
            vis = t;  
        }  
        return ans;  
    }  
};

这个是跟uwi类似的算法, 不过他把rep换成了一个boolean; 高手之间还是有点相通的地方的;


Problem Description

We are given head, the head node of a linked list containing unique integer values.

We are also given the list G, a subset of the values in the linked list.

Return the number of connected components in G, where two values are connected if they appear consecutively in the linked list.

Example 1:

Input:   
head: 0->1->2->3  
G = [0, 1, 3]  
Output: 2  
Explanation:   
0 and 1 are connected, so [0, 1] and [3] are the two connected components.

Example 2:

Input:   
head: 0->1->2->3->4  
G = [0, 3, 1, 4]  
Output: 2  
Explanation:   
0 and 1 are connected, 3 and 4 are connected, so [0, 1] and [3, 4] are the two connected components.

Note:

  • If N is the length of the linked list given by head, 1 <= N <= 10000.
  • The value of each node in the linked list will be in the range [0, N - 1].
  • 1 <= G.length <= 10000.
  • G is a subset of all values in the linked list.

Difficulty:Medium
Total Accepted:1.3K
Total Submissions:3K
Contributor:awice
Companies
google
Related Topics
linked list

results matching ""

    No results matching ""