Skip to content

The Earliest Moment When Everyone Become Friends

Linda Zhou edited this page Oct 25, 2022 · 16 revisions

Problem Highlights

1: U-nderstand

Understand what the interviewer is asking for by using test cases and questions about the problem.

  • Established a set (2-3) of test cases to verify their own solution later.
  • Established a set (1-2) of edge cases to verify their solution handles complexities.
  • Have fully understood the problem and have no clarifying questions.
  • Have you verified any Time/Space Constraints for this problem?
  • How can we sort the log items?
    • Sort the log items by their timestamp.
  • How can we model this problem as a graph problem?
    • To keep track of connectivity of each element in the subset or connectivity of subsets with each other, you can use Union Find data structure to do this operation efficiently.
HAPPY CASE
Input: logs = [[20190101,0,1],[20190104,3,4],[20190107,2,3],[20190211,1,5],[20190224,2,4],[20190301,0,3],[20190312,1,2],[20190322,4,5]], n = 6
Output: 20190301

Input: logs = [[0,2,0],[1,0,1],[3,0,3],[4,1,2],[7,3,1]], n = 4
Output: 3

EDGE CASE
Input: logs = [[1,0,1],[20190104,3,4],[20190107,2,3],[20190211,1,5],[20190224,2,4],[20190301,0,3],[20190312,1,2],[0,4,5]], n = 10
Output: -1

2: M-atch

Match what this problem looks like to known categories of problems, e.g. Linked List or Dynamic Programming, and strategies or patterns in those categories.

For graph problems, we want to consider the following approaches:

  • Union Find: Let's use a union-find data structure. At the beginning we have a graph with N nodes but no edges. Then we loop through the events and if unite each node until the number of connected components reach to 1. Notice that each time two different connected components are united the number of connected components decreases by 1.
  • Depth First Search: Suppose we have a set. We can keep track of the friend circle and add people in the set. As soon as we find a new relation (by iterating through logs), we can check if this new relation will bring every member in set.

3: P-lan

Plan the solution with appropriate visualizations and pseudocode.

General Idea: Write a 1-2 sentence overview that explains the general approach.

1. Initialize a variable time, which stores the value of the current timestamp of the DSU.
2. Traverse the given array, and perform union operation between and update the current timestamp to time if belong to the different sets.
3. If the total number of sets after completely traversing through the array is 1, return the value of the variable time, else return -1.

⚠️ Common Mistakes

  • What are some common pitfalls students might have when implementing this solution?

You can think of the node "representing" the connected component as sometimes further (a certain number of edges) away. A common mistake is not seeing that the parent is closer. To ensure best runtime, the smaller connected component is merged into the larger one.

4: I-mplement

Implement the code to solve the algorithm.

class Solution:
    def earliestAcq(self, logs: List[List[int]], n: int) -> int:
        # sort the events in chronological order
        logs.sort(key = lambda x: x[0])

        uf = UnionFind(n)
        # treat each individual as a separate group
        group_cnt = n

        # merge the groups as we traverse the logs
        for timestamp, friend_a, friend_b in logs:
            if uf.union(friend_a, friend_b):
                group_cnt -= 1

            # return timestamp when individuals are connected to each other
            if group_cnt == 1:
                return timestamp

        # not everyone is connected
        return -1
class Solution {
    public int earliestAcq(int[][] logs, int n) {
        // sort the events in chronological order
        Arrays.sort(logs, new Comparator<int[]>() {
            public int compare(int[] log1, int[] log2) {
                Integer tsp1 = new Integer(log1[0]);
                Integer tsp2 = new Integer(log2[0]);
                return tsp1.compareTo(tsp2);
            }
        });

        // treat each individual as a separate group
        int groupCount = n;
        UnionFind uf = new UnionFind(n);

        for (int[] log : logs) {
            int timestamp = log[0];
            int friendA = log[1];
            int friendB = log[2];
            // merge the groups
            if (uf.union(friendA, friendB)) {
                groupCount -= 1;
            }

            // return timestamp when individuals are connected to each other
            if (groupCount == 1) {
                return timestamp;
            }
        }

        // not everyone is connected
        return -1;
    }
}

5: R-eview

Review the code by running specific example(s) and recording values (watchlist) of your code's variables along the way.

  • Trace through your code with an input to check for the expected output
  • Catch possible edge cases and off-by-one errors

6: E-valuate

Evaluate the performance of your algorithm and state any strong/weak or future potential work.

Let E be the number of edges or the number of timestamps. Let V be the number of vertices or the number of people.

  • Time Complexity: O(E * lgE + EV) = O(E(lgE+V)), as sorting logs takes O(E * lgE) and going through each log takes O(E) and in each log we call union(with rank and find with which its worst case takes O(V) (amortized close to O(1)), thus the loop takes O(E * V)
  • Space Complexity: O(V), as we used union find.
Clone this wiki locally