4

I'm struggling with this problem on HackerRank. https://www.hackerrank.com/challenges/friend-circle-queries/problem I tried solving it using a custom linked list - NodeList. It has three fields - Node first, Node current, int size. 'add' is an overloaded method. It can add a value or another NodeList. I have put code for NodeList in comments because it doesn't matter much.

Fields :-

static HashMap<Integer, Integer> personToIndex = new HashMap<>();
static int largestCircleSize = 0;
static ArrayList<NodeList> groups = new ArrayList<>();

This is my business logic method. When only one person is part of a friend circle, I add the other person in the circle. When both the people who are shaking hands are already part of other circles, I merge the circles.

static void updateFriendCircles(int friend1, int friend2) {
    int friend1Index, friend2Index;
    NodeList toUpdate;
    friend1Index = personToIndex.getOrDefault(friend1, -1);
    friend2Index = personToIndex.getOrDefault(friend2, -1);
    if (friend1Index != -1) {
        NodeList list = groups.get(friend1Index);
        if (friend2Index != -1) {
            NodeList list2 = groups.get(friend2Index);
            if (list.first == groups.get(friend2Index).first)
                return;
            toUpdate = list.add(list2);
            groups.set(friend2Index, list);
        }
        else {
            toUpdate = list.add(friend2);
            personToIndex.put(friend2, friend1Index);
        }
    }
    else if (friend2Index != -1) {
        toUpdate = groups.get(friend2Index).add(friend1);
        personToIndex.put(friend1, friend2Index);
    }
    else {
        int index = groups.size();
        personToIndex.put(friend1, index);
        personToIndex.put(friend2, index);
        toUpdate = new NodeList(friend1).add(friend2);
        groups.add(toUpdate);
    }
    if (toUpdate.size > largestCircleSize)
        largestCircleSize = toUpdate.size;
}

I have also tried using HashSet but it also has same problem so I think problem is not in data structure.

Anatolii
  • 14,139
  • 4
  • 35
  • 65
ghoul932
  • 192
  • 1
  • 8

1 Answers1

1

As it's not clear what is wrong with the solution exactly (it's not specified by the OP) - wrong answers or timeout for some test cases I'll explain how to solve it.

We can use a disjoint set data structure to represent sets of friend circles.

The basic idea is that in each circle we assign a member that is used to represent a given circle. We can call it a root. Finding a number of members in the circle is always delegated to the root that stores its size.

Each non-root member points to his root member or to a member through whom he can get to the root. In the future, the current root may also lose his root status for the community but then it will point to the new root and so it's always possible to get to it through chained calls.

When 2 circles merge then a new root member is chosen among 2 previous ones. The new size can be set into it because previous roots already contain sizes for both circles. But how is the new root chosen? If the size of circle 1 is not smaller than that of circle 2 then it's picked as a new root.

So, for this problem, first we should define placeholders for circles and sizes:

Map<Integer, Integer> people;
Map<Integer, Integer> sizes;

For non-root members in people, key is a person ID and value is a friend he follows (root or parent that can get him to the root). Root members won't have an entry in the map.

Then, we need a method that will get us to the root for any member:

int findCommunityRoot(int x) {
    if (people.containsKey(x)) {
        return findCommunityRoot(people.get(x));
    }
    return x;
}

Finally, we need a method to build a community for 2 given friends:

int mergeCommunities(int x, int y) {
    //find a root of x
    x = findCommunityRoot(x);
    //find a root of y
    y = findCommunityRoot(y);

    // one-man circle has a size of 1
    if (!sizes.containsKey(x)) {
        sizes.put(x, 1);
    }
    // one-man circle has a size of 1
    if (!sizes.containsKey(y)) {
        sizes.put(y, 1);
    }

    // friends in the same circle so just return its size
    if (x == y) {
        return sizes.get(x);
    }

    sizeX = sizes.get(x);
    sizeY = sizes.get(y);
    if (sizeX >= sizeY) { 
        people.put(y, x);
        sizes.put(x, sizeY + sizeX); 
        return sizes.get(x);
    } else {
        people.put(x, y);
        sizes.put(y, sizeY + sizeX); 
        return sizes.get(y);
    }
}

So, we have everything we need to save a size of the largest circle at each iteration:

List<Integer> maxCircle(int[][] queries) {
    List<Integer> maxCircles = new ArrayList<>();

    int maxSize = 1;
    for (int i = 0; i < queries.length; i++) {
        int size = mergeCommunities(queries[i][0], queries[i][1]);
        maxSize = Math.max(maxSize, size);
        maxCircles.add(maxSize);
    }

    return maxCircles;
}
Anatolii
  • 14,139
  • 4
  • 35
  • 65