find a smaller group of friends from the circle? - java

I am working on the following problem:
In a room with people, we will define two persons are friends if they
are directly or indirectly friends. If A is a friend with B, and B is
a friend with C, then A is a friend of C too. A group of friends is a
group of persons where any two persons in the group are friends. Given
the list of persons that are directly friends, find the smallest group
of friends..
Example:
Input:
1<->6
2<->7
3<->8
4<->9
2<->6
3<->5
Groups:
1-6-2-7
3-8-5
4-9
The number of people in the smallest group is 2 i.e. 4-9 so we should return 2.
I came up with the code below but I don't understand how to use this holder map now to get the required output. I am kinda confused here. What is the best way to solve this problem?
private static int findGroups(final List<List<Integer>> inputs) {
if (inputs == null || inputs.isEmpty()) {
return 0;
}
int count = Integer.MAX_VALUE;
Map<Integer, List<Integer>> holder = new HashMap<>();
for (List<Integer> input : inputs) {
// storing it in bidirectional way in the map
List<Integer> l =
holder.containsKey(input.get(0)) ? holder.get(input.get(0)) : new ArrayList<Integer>();
l.add(input.get(1));
holder.put(input.get(0), l);
List<Integer> l1 =
holder.containsKey(input.get(1)) ? holder.get(input.get(1)) : new ArrayList<Integer>();
l1.add(input.get(0));
holder.put(input.get(1), l1);
}
System.out.println(holder);
// use holder map to get the smaller group here?
return count;
}
Looks like I need to use recursion here to get smaller groups?

Is there any better way to solve this problem?
A better approach is to use a disjoint-set data structure:
First, compute the "groups of friends" as a disjoint-set data structure:
Initialize a disjoint-set data structure where the elements of the sets will be the people in the room.
For each person p in the room:
Call MakeSet(p) to initialize a "group of friends" containing just that person.
For each direct friendship p1↔p2:
Call Union(p1, p2) to unify the "group of friends" that contains p1 with the one that contains p2.
Next, compute the sizes of the "groups of friends" as a map:
Initialize a map where the keys will be some of the people in the room (namely, the representatives of their respective "groups of friends") and the values will be numbers (namely, the sizes of the various "groups of friends").
For each person p1 in the room:
Call Find(p1) to find the representative p2 of that person's "group of friends".
If the map does not already contain a value for the key p2, insert the value 0 for that key.
Increment the value for the key p2.
Lastly, compute the result:
Initialize a result to a large value, e.g. the number of people in the room.
For each value (= size of a "group of friends") in the map:
If this value is less than the result, set the result equal to this value.
Return the result.
By the way, the technical name for the operation you're performing is transitive closure: the "are friends" relation is the transitive closure of the "are directly friends" relation. (However, the algorithms described at the Wikipedia article for that are not optimal for your problem, because they don't take advantage of the fact that your relation is symmetric.)

Here's some code, I've wrote out of curiousity about your problem. I don't know much about graphs, but it uses recursion, like you requested.
Basically you go through the input and for every person, you create an ArrayList<Integer>. In that array are the people who are that person's immediate friends. For instance, for 1: {6}, for 2: {7, 6}, for 3: {8, 5}. Then, to get all friends of person 2, you create a compoud ArrayList<Integer> array by getting together arrays for person 7 and 6 (excluding duplicates). So, the recursion could be used in a way that a function getAllFriends(Integer person) would have to return the getAllFriends(Integer person2) for that person's immediate friends as well.
So, the code could look like something like this:
public class Test {
public static void main(String[] args) throws Exception {
String input = "1<->6\n" +
"2<->7\n" +
"3<->8\n" +
"4<->9\n" +
"2<->6\n" +
"3<->5";
HashMap<Integer, ArrayList<Integer>> friends = processInput(input); //getting data from the input string and storing it in a structured way
System.out.println(getAllFriends(1, friends, new ArrayList<Integer>(){{add(1);}})); //output: [1, 6, 2, 7]. Double brackets create an anonymous inner class, you add to the result the id of a person whose friends you're collecting
}
public static HashMap<Integer, ArrayList<Integer>> processInput(String input) throws Exception {
HashMap<Integer, ArrayList<Integer>> result = new HashMap<>();
BufferedReader bufReader = new BufferedReader(new StringReader(input));
String line=null;
while( (line=bufReader.readLine()) != null )
{
Integer personLeft = Integer.valueOf(line.substring(0, line.indexOf("<")));
Integer personRight =Integer.valueOf(line.substring(line.indexOf(">")+1, line.length()));
System.out.println(personLeft + ": " + personRight);
if (!result.containsKey(personLeft)) {
result.put(personLeft, new ArrayList<Integer>());
}
result.get(personLeft).add(personRight);
if (!result.containsKey(personRight)) {
result.put(personRight, new ArrayList<Integer>());
}
result.get(personRight).add(personLeft);
}
return result;
}
public static ArrayList<Integer> getAllFriends(Integer person, HashMap<Integer, ArrayList<Integer>> friends, ArrayList<Integer> result) {
for (Integer personFriend: friends.get(person)) {
if (!result.contains(personFriend)) {
result.add(personFriend); //add a person, if it wasn't added before
getAllFriends(personFriend, friends, result); //check out that person's friends
}
}
return result;
}
}

Related

How can I check for duplicate values of an object in an array of objects, merge the duplicates' values, and then remove the duplicate?

Right now I have an array of "Dragon"s. Each item has two values. An ID and a Count. So my array would look something like this:
Dragon[] dragons = { new Dragon(2, 4),
new Dragon(83, 199),
new Dragon(492, 239),
new Dragon(2, 93),
new Dragon(24, 5)
};
As you can see, I have two Dragons with the ID of 2 in the array. What I would like to accomplish is, when a duplicate is found, just add the count of the duplicate to the count of the first one, and then remove the duplicate Dragon.
I've done this sort of successfully, but I would end up with a null in the middle of the array, and I don't know how to remove the null and then shuffle them.
This is what I have so far but it really doesn't work properly:
public static void dupeCheck(Dragon[] dragons) {
int end = dragons.length;
for (int i = 0; i < end; i++) {
for (int j = i + 1; j < end; j++) {
if (dragons[i] != null && dragons[j] != null) {
if (dragons[i].getId() == dragons[j].getId()) {
dragons[i] = new Item(dragons[i].getId(), dragons[i].getCount() + dragons[j].getCount());
dragons[j] = null;
end--;
j--;
}
}
}
}
}
You should most probably not maintain the dragon count for each dragon in the dragon class itself.
That aside, even if you are forced to use an array, you should create an intermeditate map to store your dragons.
Map<Integer, Dragon> idToDragon = new HashMap<>();
for (Dragon d : yourArray) {
// fetch existing dragon with that id or create one if none present
Dragon t = idToDragon.computeIfAbsent(d.getId(), i -> new Dragon(i, 0));
// add counts
t.setCount(t.getCount() + d.getCount());
// store in map
idToDragon.put(d.getId(), t);
}
Now the map contains a mapping between the dragons' ids and the dragons, with the correct counts.
To create an array out of this map, you can just
Dragon[] newArray = idToDragon.values().toArray(new Dragon[idToDragon.size()]);
You may be force to store the result in an array but that doesn't mean that you're force to always use an array
One solution could be using the Stream API, group the items adding the count and save the result into an array again. You can get an example of how to use the Stream API to sum values here. Converting a List<T> into a T[] is quite straightforward but anyways, you have an example here
The size of an array cannot be changed after it's created.
So you need to return either a new array or list containing the merged dragons.
public static Dragon[] merge(Dragon[] dragonArr) {
return Arrays.stream(dragonArr)
// 1. obtain a map of dragon IDs and their combined counts
.collect(groupingBy(Dragon::getId, summingInt(Dragon::getCount)))
// 2. transform the map entries to dragons
.entrySet().stream().map(entry -> new Dragon(entry.getKey(), entry.getValue()))
// 3. collect the result as an array
.toArray(Dragon[]::new);
}

Pairing among the members in same list in java

In problem statement, I have 'n' number of families with 'n' number of family members.
eg:
John jane (family 1)
tiya (family 2)
Erika (family 3)
I have to assign all members in such a way that person should not pair with the his family member.
and output should be:
John => tiya
jane => Erika
tiya => jane
Erika => john
I have created the object Person(name ,familyID, isAllocated).
Created the list and added personName_id in this this.
I am thinking to use the map for association. So that john_1 will be key and tiya_2 will be value.
I am failing to associate those pairs through map. How can I shuffle the members it the list.
Also, It would be nice if anyone could suggest me the better solution.
Code:
Getting person:
public static List getperson()
{
Scanner keyboard = new Scanner(System.in);
String line = null;
int count = 0;
List <Person> people = new ArrayList<>();
while(!(line = keyboard.nextLine()).isEmpty()) {
String[] values = line.split("\\s+");
//System.out.print("entered: " + Arrays.toString(values) + "\n");
int familyid = count++;
for(String name :values)
{
Person person = new Person();
person.setFamilyId(familyid);
person.setName(name);
person.setAllocated(false);
people.add(person);
}
}
return people;
}
Mapping:
public static List mapGifts(List pesonList)
{
Map<String , String> personMap = new HashMap<String , String>();
Iterator<Person> itr = pesonList.iterator();
Iterator<Person> itr2 = pesonList.iterator();
List<String> sender = new ArrayList<>();
while(itr.hasNext())
{
Person p = itr.next();
sender.add(p.getName()+"_"+p.getFamilyId());
personMap.put(p.getName()+"_"+p.getFamilyId(), "");
// p.setAllocated(true);
}
while(itr2.hasNext())
{
/*if(p.isAllocated())
{*/
// Separate Sender name and id from sender list
//check this id match with new p1.getFamilyId()
for(String sendername :sender)
{
// System.out.println("Sender "+sendername);
personMap.put(sendername, "");
String[] names = sendername.split("_");
String part1 = names[0]; // 004
String familyId = names[1]; // 004
Person p2 = itr2.next();
System.out.println(p2.getFamilyId() +" "+familyId +" "+p2.isAllocated());
if(p2.isAllocated())
{
for ( String value: personMap.values()) {
if ( value != sendername) {
}
}
}
if( p2.getFamilyId() != Integer.parseInt(familyId))
{
// add values in map
}
}
break;
// Person newPerson = personLists.get(j);
}
for (Iterator it = personMap.entrySet().iterator(); it.hasNext();)
{
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println("Gifts "+key+"=>"+value);
}
return pesonList;
}
Thanks
From what I've read, you only care that you match people. How they match doesn't matter. That said, I'll assume you have a list of FamilyID's, and a list of names of everyone, and that you can sort the list of people according to family IDs.
Let's call them:
List<FamilyID> families; and
LinkedList<Person> people;, respectively. (You can make FamilyID an enumerated class)
We need two hashmaps. One to generate a list (essentially an adjacency list) of family members given a familyID:
HashMap<FamilyID, List<Person>> familyMembers; ,
and one to generate a list of sender(key) and receiver(value) pairs:
HashMap<Person, Person> pairs;
A useful function may be that, when given a person and their family ID, we can find the next available person who can receive from them.
String generateReceiver(Person newSender, FamilyID familyID);
Implementation of this method should be pretty straightforward. You can iterate through the list of people and check to see if the current person is not a family member. If that condition passes, you remove them from the "people" list so you don't try to iterate through them again. If you're using a linked list for this, removal is O(1) since you'll already have the reference. Worst case on traversals the list is n + n - 1 + ... + 2 times to get O(n^2) time efficiency (i.e. you have one large family and many small ones). Work around that would be to ditch the LinkedList, use an Array-based list, and keep a separate array of index values corresponding to each "currently available receiver of a specified family". You'd initialize these values as you added the people from each family to the people list (i.e. start of family 1 is index "0"; if family 1 has 2 people, start of family 2 would be index "2"). This would make the function O(1) time if you just incremented the current available receiver index everytime you added a sender-receiver pair. (Message me if you want more details on this!)
Last but not least, the loop is doing this for all people.
for (List<Person> family : familyMembers)
for (Person person : family)
{
// get the next available receiver who is not a family member
// add the family member and its receiver to "pairs" hash
}
Note that the above loop is pseudocode. If you're wondering if you would generate conflicting receiver/senders with this method, you won't. The list of people is essentially acting as a list of receivers. Whichever way you implement the people list, the generateReceiver(...)eliminates the chance that the algorithm would see a faulty-receiver. Per efficiency, if you do the array based implementation then you're at O(N) time for generating all pair values, where N is the total number of people. The program itself would be O(N) space as well.
Of course, this is all based on the assumption you have enough people to match for sender-receiver pairs. You'd need to add bells and whistles to check for special cases.
Hope this helps! Good luck!

How to calculate the rank of a player from a list

Say I have following simple data structure.
public class Player {
private Long id;
private String name;
private Integer scores;
//getter setter
}
So far so good. Now question is , how do I get what's a players rank?
I have another data structure for ranking, that is-
public class Ranking {
private Integer score;
private Integer rank;
//getter/setter
}
So I have a list of player and i want to compute a list of ranking and I would like to use java8 stream api.
I have a service named PlayerService as following
public class PlayerService {
#Autowired
private PlayerRepository playerRepository;
public List<Ranking> findAllRanking(Long limit) {
List<Player> players = playerRepository.findAll();
// calculation
return null;
}
The calculation is simple, whoever has most score has most ranking.
If I have 5,7,8,9,3 scores then ranking would be
rank score
1 9
2 8
3 7
4 5
5 3
Any help would be appreciated.
Try this:
List<Player> players = new ArrayList<Player>() {{
add(new Player(1L, "a", 5));
add(new Player(2L, "b", 7));
add(new Player(3L, "c", 8));
add(new Player(4L, "d", 9));
add(new Player(5L, "e", 3));
add(new Player(6L, "f", 8));
}};
int[] score = {Integer.MIN_VALUE};
int[] no = {0};
int[] rank = {0};
List<Ranking> ranking = players.stream()
.sorted((a, b) -> b.getScores() - a.getScores())
.map(p -> {
++no[0];
if (score[0] != p.getScores()) rank[0] = no[0];
return new Ranking(rank[0], score[0] = p.getScores());
})
// .distinct() // if you want to remove duplicate rankings.
.collect(Collectors.toList());
System.out.println(ranking);
// result:
// rank=1, score=9
// rank=2, score=8
// rank=2, score=8
// rank=4, score=7
// rank=5, score=5
// rank=6, score=3
The variables score, no and rank are free variables in the lambda function in .map(). So they must not be reassigned. If their types are int instead of int[], you cannot compile the code.
The problem with the accepted answer is that once you turn the stream in parallel (players.parallelStream()), you'll get unexpected results, because of race conditions when you read/update the value from the singletons arrays.
Maybe you could decompose your task into multiple steps. First sort the list of scores into reverse order, and then generate a stream of indices. From there you map each index to its corresponding rank.
You need to test multiple conditions into the mapToObj statement if you need to have the same rank for a same score. It makes the code not very pretty but you can always extract this in a helper method.
List<Integer> scores = players.stream().map(Player::getScores).sorted(reverseOrder()).collect(toList());
List<Ranking> rankings =
IntStream.range(0, scores.size())
.mapToObj(i -> i == 0 ? new Ranking(1, scores.get(i)) :
scores.get(i - 1).equals(scores.get(i)) ? new Ranking(i, scores.get(i)) :
new Ranking(i + 1, scores.get(i)))
.collect(toList());
That said, if you don't plan to parallelize this step, I would go with the good old for-loop.
If you don't need to have the same rank for the same score, you can check this thread Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip), for example with proton-pack:
List<Ranking> rankings = StreamUtils.zip(IntStream.rangeClosed(1, players.size()).boxed(),
players.stream().map(Player::getScores).sorted(reverseOrder()),
Ranking::new)
.collect(toList());
You will get a better solution, if you rethink your prerequisites first. As a side note, I don’t understand, why you are using Integer throughout your code. If you really consider null a possible value for rank or scores, the solution will become much more complicated. Since I doubt that this is what you want, I recommend using int instead.
The biggest obstacle is your requirement of producing this rather superfluous Ranking instances. All you need, are the scores as once sorted, their rank is implied by their positions:
List<Integer> rankAndScore = players.stream().map(Player::getScores)
.sorted(Comparator.reverseOrder()).collect(toList());
this resulting list contains the scores in descending order and the rank is implied by the position, the first item has the first rank, the second item the second, and so on. There is no need to explicitly store the rank numbers. The only thing to care is that the collection indices start with zero whereas the ranks start with one.
One way to print the values as in your question is:
System.out.println("rank\tscore");
IntStream.range(0, rankAndScore.size())
.forEachOrdered(r->System.out.println(r+1+"\t"+rankAndScore.get(r)));
Alternatively, you could simply use an int[] array to represent rank and score:
int[] rankAndScore = players.stream().mapToInt(Player::getScores).sorted().toArray();
System.out.println("rank\tscore");
IntStream.rangeClosed(1, rankAndScore.length)
.forEachOrdered(r->System.out.println(r+"\t"+rankAndScore[rankAndScore.length-r]));
The IntStream doesn’t support sorting in descending order but as shown, it can be simply fixed by sorting to ascending order and adapting the processing of the array.
I believe you want the Player to have as an attribute, a Ranking, right? Why not add a member variable (or instance variable) in the player's class. I believe we call this "aggregation" or "Object composition" in object oriented programming. https://en.wikipedia.org/wiki/Object_composition
Consider this...
public class Player {
private Long id;
private String name;
private Integer scores;
private Ranking rank;
...
//You can add a constructor that include the Ranking, you can also add setter and getters to set the Ranking object for this player.
public int getRank()
{
return rank.getRank(); //assuming class Ranking has getRank() method
}
public void setRank(int rank)
{
rank.setRank(rank); //assuming class Ranking has setRank()
}
}
Why not sort the payers according to their score?
so.. something along those lines:
public class PlayerService {
#Autowired
private PlayerRepository playerRepository;
public List<Ranking> findAllRanking(Long limit) {
List<Player> players = playerRepository.findAll();
// --- sorting players according to their scores
Collections.sort(players, new Comparator<Player>(){
#Override
public int compare(Players thiz, Player that){
return new Integer(thiz.scores).compareTo(that.scores);
});
List<Ranking> rankingList = new ArrayList<>();
int i = 0;
for(Player p : players){
rankingList.add(new Ranking(p.score, ++i));
}
return rankingList;
}
I leave that as an exercise to you to determine if that produces your wanted order or the exact reverse.

Split up ArrayList<Object> to multiple depending on int value in Object

I have an ArrayList containing Objects, these objects have multiple values.
Now I would like to split up this list in to mulptiple lists depeding on an int value in the objects.
So if for example:
2 Objects have an int with value 1
3 Objects have an int with the value 3
So the arraylist has 5 objects, and I'd like to get:
2 Arraylists, 1 with the first objects en 1 with the second objects (more if there are more different int values)
Sorry if it is confusing..
First create a cache like this: Map<Integer, List<YourObjectType>>
Then cycle through each of your objects, and use your integer to access the above Map, if the value is null, create a new List and put it in the Map, then add your object to the List.
The end result would be a map with two entries, each containing a list of entries with the integer from your object being the discriminator.
Here's the code:
Map<Integer, List<YourObject>> cache = new HashMap<Integer, List<YourObject>>();
for (YourObject yo : yourObjectListArrayWhatever) {
List<YourObject> list = cache.get(yo.getIntegerValue());
if (list == null) {
list = new ArrayList<YourObject>();
cache.put(yo.getIntegerValue(), list);
}
list.add(yo);
}
How do you store an int value in Object? I'm sure you have an implementation that is derived from Object and in that case you should use generecity at a lower point of the hierarchy.
Say you have a class Person with an int value and then subclasses Man extends Person and Woman extends Person and you populate this ArrayList with men and women, you would do so like so:
List<Person> pList = new ArrayList<Person>();
Now, in your Person class you should have a get-method for the int value. For example if the int value is the person's age:
public int getAge() { return age; }
Then, to finally answer your question I would go about like so:
List<Person> firstList = new ArrayList<Person>();
List<Person> secondList = new ArrayList<Person>();
for (Person person:pList) {
if (person.getAge()==1) {
firstList.add(person);
}
else if (person.getAge()==3) {
secondList.add(person);
}
}//for
I hope I answered your question adequately.

Is there an object like a "Set" that can contain only unique string values, but also contain a count on the number of occurrences of the string value?

In Java is there an object like a "Set" that can contain only unique string values, but also contain a count on the number of occurrences of the string value?
The idea is simple
With a data set ala...
A
B
B
C
C
C
I'd like to add each line of text to a Set-like object. Each time that a non-unique text is added to the set I'd like to also have a numeric value associated with the set to display how many times it was added. So if I ran it on the above data set the output would be something like:
A : 1
B : 2
C : 3
any ideas?
You want a "Bag", like the Bag in Apache Commons Collections or the Multiset in Google Collections. You can add the same value to it multiple times, and it'll record the counts of each value. You can then interrogate the counts.
You'd do something like this with Apache Commons' Bag:
Bag myBag = new HashBag();
myBag.add("Orange");
myBag.add("Apple", 4);
myBag.add("Apple");
myBag.remove("Apple", 2);
int apples = myBag.getCount("Apple"); // Should be 3.
int kumquats = myBag.getCount("Kumquat"); // Should be 0.
And this with Google Collections' Multiset.
Multiset<String> myMultiset= HashMultiset.create();
myMultiset.add("Orange");
myMultiset.add("Apple", 4);
myMultiset.add("Apple");
myMultiset.remove("Apple", 2);
int apples = myMultiset.count("Apple"); // 3
int kumquats = myMultiset.count("Kumquats"); // 0
The problem with Apache Collections in general is that it isn't being very actively maintained, and it doesn't yet support Java Generics. To step into this gap, Google's written their own Collections which are extremely powerful. Be sure to evaluate Google Collections first.
Update: Google Collections also offers Multimap, a "collection similar to a Map, but which may associate multiple values with a single key".
Map<String, Integer> would be the best bet, to put in words what you want to do is to Map the amount of occurrences of a string. Basically have something like this:
public void add(String s) {
if (map.containsKey(s)) {
map.put(s, map.get(s) + 1);
} else {
map.put(s, 1);
}
}
Yeap, not directly in the core, but can be built easily with a Map.
Here's a naive implementation:
import java.util.Map;
import java.util.HashMap;
public class SetLike {
private Map<String, Integer> map = new HashMap<String,Integer>();
public void add( String s ) {
if( !map.containsKey( s ) ){
map.put( s, 0 );
}
map.put( s, map.get( s ) + 1 );
}
public void printValuesAndCounts() {
System.out.println( map );
}
public static void main( String [] args ){
String [] data = {"A","B","B","C","C","C"};
SetLike holder = new SetLike();
for( String value : data ) {
holder.add( value );
}
holder.printValuesAndCounts();
}
}
Test it
$ javac SetLike.java
$ java SetLike
{A=1, C=3, B=2}
Of course you can improve it much more. You can implement the Set interface, or a List, or a Collection, etc, you can add the iterators, implement Iterable and so on, it depends on what you want and what you need.
This will be helpful..
List<String> myList=new ArrayList<String>();
myList.add("A");
myList.add("B");
myList.add("B");
myList.add("C");
myList.add("C");
myList.add("C");
Set<String> set=new HashSet<String>(myList);
for (String value : set)
{
int occurance=Collections.frequency(myList, value);
System.out.println(value +" occur "+occurance + " times ");
}
Result :
A occur 1 times
B occur 2 times
C occur 3 times

Categories