2-D array Friends of Friends List - java

2*2 Matrix
Men Friends
A B,C,D
B E,F
C A
E B
F B
G F
I need list of Friends and Friends of Friends for requested men.
Example Like G -> F,B,E,F,B and After removing duplicate F,B,E
I resolved it with loops and recursion but not satisfied
Need better approach/suggestion.. rest i will implement.

Why not try something like this. Of course, i have taken a bit of freedom on the design as you didnt provide any code. Hope this helps!
private static Set<Node> getNodesFollowAllEdges(Node node) {
Set<Node> nodes = getNodesFollowAllEdges(node, new HashSet<>());
// remember to remove the original node from the set
nodes.remove(node);
return nodes;
}
private static Set<Node> getNodesFollowAllEdges(Node node, Set<Node> visited) {
if (node.getConnectedNodes().isEmpty()) {
return visited;
}
for (Node n : node.getConnectedNodes()) {
if (!visited.contains(n)) {
visited.add(n);
getNodesFollowAllEdges(n, visited);
}
}
return visited;
}
Also, it is very easy to provide a maximum search dept. Just add int maxDept and increase it every recursion step.
Given the following example:
Node a = new Node("A");
Node b = new Node("B");
Node c = new Node("C");
Node d = new Node("D");
Node e = new Node("E");
Node f = new Node("F");
Node g = new Node("G");
a.addConnectedNodes(b, c, d);
b.addConnectedNodes(e, f);
c.addConnectedNodes(a);
e.addConnectedNodes(b);
f.addConnectedNodes(b);
g.addConnectedNodes(f);
Set<Node> friends = getNodesFollowAllEdges(a);
friends.forEach(node -> System.out.println(node.getName()));
should give you the correct result of (order neglected)
B
F
E
Note: Remember that, since its a Set, the resulting nodes can be in any order.

Thanks Glains,
My Code is :
public class Demo {
Set<String> freinds = new HashSet<>();
public static void main(String[] args) {
String[][] emailArray = new String[][] {
{ "abc#gmail.com", "abc1#gmail.com,abc2#gmail.com,abc3#gmail.com,1212#gmail.com" },
{ "abc1#gmail.com", "bcs1#gmail.combc2#gmail.com,bcds3#gmail.com" },
{ "bc#gmail.com", "bc1#gmail.combc2#gmail.com,bc3#gmail.com" } };
new Demo().sendMail("#gmail.combc2#gmail.com", "sdsd", emailArray);
}
void sendMail(String email, String message, String[][] freindsArray) {
Map<String, Email> emailsMap = new HashMap<>();
for (int i = 0; i < freindsArray.length; i++) {
for (int j = 0; j < 1; j++) {
Email e = new Email(freindsArray[i][j]);
e.addConnectedNodes(freindsArray[i][j + 1]);
emailsMap.put(e.email, e);
}
}
if (emailsMap.containsKey(email)) {
Demo.getNodesFollowAllEdges(emailsMap.get(email), emailsMap).forEach(e -> {
System.out.println(e);
});
} else {
System.out.println("no emails exist");
}
}
private static Set<String> getNodesFollowAllEdges(Email e, Map<String, Email> emailsMap) {
Set<String> nodes = getNodesFollowAllEdges(e, new HashSet<>(), emailsMap);
nodes.remove(e.email);
return nodes;
}
private static Set<String> getNodesFollowAllEdges(Email node, Set<String> visited, Map<String, Email> emailsMap) {
if (node.getConnectedEmails().isEmpty()) {
return visited;
}
for (String n : node.getConnectedEmails()) {
if (!visited.contains(n)) {
visited.add(n);
if (emailsMap.get(n) != null) {
getNodesFollowAllEdges(emailsMap.get(n), visited, emailsMap);
}
}
}
return visited;
}
}
class Email {
String email;
List<String> freindsEmails = new ArrayList<>();
public List<String> getConnectedEmails() {
return freindsEmails;
}
public Email(String email) {
this.email = email;
}
public void addConnectedNodes(String friendsEmail) {
freindsEmails.addAll(Arrays.asList(friendsEmail.split(",")));
}
}

Related

How to remove duplicate pairs from an ArrayList in Java?

I've an ArrayList which contains pairs of integers( say int i, int j). But it may be contains duplicates pairs (like (int i, int j) and (int j, int i)). Now how can I remove duplicates from it in O(n) time complexity.
Updated code:
class Pair<t1,t2>
{
int i, j;
Pair(int i,int j){
this.i=i;
this.j=j;
}
}
public class My
{
public static void main(String[] args) {
Pair p;
List<Pair<Integer,Integer>> src = Arrays.asList(new Pair(1,2),
new Pair(2,3), new Pair(2,1),new Pair(1,2));
HashSet<String> dest = new HashSet();
for(int i=0; i < src.size(); i++) {
p=src.get(i);
if(dest.contains(p.j+" "+p.i)) {
System.out.println("duplicacy");
}
else {
dest.add(p.i+" "+p.j);
}
}
System.out.println("set is = "+dest);
List<Pair<Integer,Integer>> ans=new ArrayList();
String temp;
int i,j;
Iterator<String> it=dest.iterator();
while(it.hasNext())
{
temp=it.next();
i=Integer.parseInt(temp.substring(0,temp.indexOf(' ')));
j=Integer.parseInt(temp.substring(temp.indexOf('
')+1,temp.length()));
ans.add(new Pair(i,j));
}
for(Pair i_p:ans) {
System.out.println("Pair = "+i_p.i+" , "+i_p.j);
}
}//end of main method
}//end of class My
This code is working fine but I want to know it performance wise, I mean its overall time complexity ?
If you can modify Pair class, just implement equals() and hashCode():
public class Pair {
private int a;
private int b;
public Pair(int a, int b) {
this.a = a;
this.b = b;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Pair pair = (Pair) o;
return (a == pair.a && b == pair.b) || (a == pair.b && b == pair.a);
}
#Override
public int hashCode() {
return Objects.hashCode(new HashSet<>(Arrays.asList(a,b)));
}
#Override
public String toString() {
return "Pair{" +
"a=" + a +
", b=" + b +
'}';
}
}
Then just create a new Set<Pair>:
List<Pair> pairs = Arrays.asList(new Pair(1, 2), new Pair(2, 1), new Pair(3, 2));
Set<Pair> pairSet = new HashSet<>(pairs);
System.out.println(pairSet);
Output:
[Pair{a=1, b=2}, Pair{a=3, b=2}]
If you can't modify Pair class:
List<Pair> pairs = Arrays.asList(new Pair(1, 2), new Pair(2, 1), new Pair(3, 2));
Set<Pair> pairSet = pairs.stream()
.map(pair -> new HashSet<>(Arrays.asList(pair.getA(), pair.getB())))
.distinct()
.map(integers -> {
Iterator<Integer> iterator = integers.stream().iterator();
return new Pair(iterator.next(), iterator.next());
})
.collect(Collectors.toSet());
System.out.println(pairSet);
Output:
[Pair{a=1, b=2}, Pair{a=2, b=3}]
If you want, you can convert your Set back to a list:
List<Pair> list = new ArrayList<>(set);
But it's most likely unnecessary.
Make only one loop throught list of Pairs and collect by the way in HashSet processed pair and it's reversed copy:
List<Pair<Integer,Integer>> lp = Arrays.asList(new Pair(1,2),
new Pair(2,3),
new Pair(1,2),
new Pair(2,1));
Set<Pair<Integer,Integer>> sp = new HashSet<>();
List<Pair<Integer,Integer>> ulp = lp.stream()
.collect(ArrayList::new,
(l,p)-> { Pair<Integer,Integer> p1 = new Pair(p.getValue(), p.getKey());
if (!(sp.contains(p))&&!(sp.contains(p1))){
l.add(p);
sp.add(p);
sp.add(p1);
}} , List::addAll);
System.out.println(ulp);
Since the contains() of HashSet runs in O(1) time (See this and other references) you can use the following method which is the overall O(n):
import java.util.*;
import javafx.util.*;
public class Main
{
public static void main(String[] args) {
List<Pair<Integer,Integer>> src = Arrays.asList(new Pair(1,2), new Pair(2,3), new Pair(2,1));
HashSet<Pair<Integer,Integer>> dest = new HashSet();
for(int i=0; i < src.size(); i++) {
if(dest.contains(src.get(i)) ||
dest.contains(new Pair(src.get(i).getValue(),src.get(i).getKey()))) {
}else {
dest.add(src.get(i));
}
}
System.out.println(dest);
}
}
EDIT 1:
You can use Map.Entry instead of javafx.util.pair. Do the program without Javafx is as follow.
import java.util.*;
public class Main
{
public static void main(String[] args) {
List<Map.Entry<Integer,Integer>> src = Arrays.asList(new AbstractMap.SimpleEntry(1,2),
new AbstractMap.SimpleEntry(2,3), new AbstractMap.SimpleEntry(2,1));
HashSet<Map.Entry<Integer,Integer>> dest = new HashSet();
for(int i=0; i < src.size(); i++) {
if(dest.contains(src.get(i)) ||
dest.contains(new AbstractMap.SimpleEntry(src.get(i).getValue(),src.get(i).getKey()))) {
}else {
dest.add(src.get(i));
}
}
System.out.println(dest);
}
}

Shortest Path with Dijkstra

I using this exact code for this. I modified it a little. So far I added a start and end node index to the calculateShortestDistances() method. Also the path ArrayList for collecting the path node indexes. Also: new to Java...
How do I collect the indexes of nodes in the path ArrayList?
I just can't come up with the solution on a level that I am not even positive this code could do what I want. I only have intuition on my side and little time.
What I tried:
Adding the nextNode value to the list then removing it if it was not
a shorter distance.
Adding the neighbourIndex to the list then removing it if it was not a shorter distance.
I made a Path.java with ArrayList but that was went nowhere (it was a class with a public variable named path) but it went nowhere.
Main.java:
public class Main {
public static void main(String[] args) {
Edge[] edges = {
new Edge(0, 2, 1), new Edge(0, 3, 4), new Edge(0, 4, 2),
new Edge(0, 1, 3), new Edge(1, 3, 2), new Edge(1, 4, 3),
new Edge(1, 5, 1), new Edge(2, 4, 1), new Edge(3, 5, 4),
new Edge(4, 5, 2), new Edge(4, 6, 7), new Edge(4, 7, 2),
new Edge(5, 6, 4), new Edge(6, 7, 5)
};
Graph g = new Graph(edges);
g.calculateShortestDistances(4,6);
g.printResult(); // let's try it !
System.out.println(g.path);
}
}
Graph.java:
This is the Graph.java file. Here I added a sAt and eAt variable, so I can tell it what path I am after. Also I created a public path ArrayList, where I intend to collect the path.
import java.util.ArrayList;
// now we must create graph object and implement dijkstra algorithm
public class Graph {
private Node[] nodes;
private int noOfNodes;
private Edge[] edges;
private int noOfEdges;
private int sAt;
private int eAt;
public ArrayList<Integer> path = new ArrayList<>();
public Graph(Edge[] edges) {
this.edges = edges;
// create all nodes ready to be updated with the edges
this.noOfNodes = calculateNoOfNodes(edges);
this.nodes = new Node[this.noOfNodes];
for (int n = 0; n < this.noOfNodes; n++) {
this.nodes[n] = new Node();
}
// add all the edges to the nodes, each edge added to two nodes (to and from)
this.noOfEdges = edges.length;
for (int edgeToAdd = 0; edgeToAdd < this.noOfEdges; edgeToAdd++) {
this.nodes[edges[edgeToAdd].getFromNodeIndex()].getEdges().add(edges[edgeToAdd]);
this.nodes[edges[edgeToAdd].getToNodeIndex()].getEdges().add(edges[edgeToAdd]);
}
}
private int calculateNoOfNodes(Edge[] edges) {
int noOfNodes = 0;
for (Edge e : edges) {
if (e.getToNodeIndex() > noOfNodes)
noOfNodes = e.getToNodeIndex();
if (e.getFromNodeIndex() > noOfNodes)
noOfNodes = e.getFromNodeIndex();
}
noOfNodes++;
return noOfNodes;
}
public void calculateShortestDistances(int startAt, int endAt) {
// node 0 as source
this.sAt = startAt;
this.eAt = endAt;
this.nodes[startAt].setDistanceFromSource(0);
int nextNode = startAt;
// visit every node
for (int i = 0; i < this.nodes.length; i++) {
// loop around the edges of current node
ArrayList<Edge> currentNodeEdges = this.nodes[nextNode].getEdges();
for (int joinedEdge = 0; joinedEdge < currentNodeEdges.size(); joinedEdge++) {
int neighbourIndex = currentNodeEdges.get(joinedEdge).getNeighbourIndex(nextNode);
// only if not visited
if (!this.nodes[neighbourIndex].isVisited()) {
int tentative = this.nodes[nextNode].getDistanceFromSource() + currentNodeEdges.get(joinedEdge).getLength();
if (tentative < nodes[neighbourIndex].getDistanceFromSource()) {
nodes[neighbourIndex].setDistanceFromSource(tentative);
}
}
}
// all neighbours checked so node visited
nodes[nextNode].setVisited(true);
// next node must be with shortest distance
nextNode = getNodeShortestDistanced();
}
}
// now we're going to implement this method in next part !
private int getNodeShortestDistanced() {
int storedNodeIndex = 0;
int storedDist = Integer.MAX_VALUE;
for (int i = 0; i < this.nodes.length; i++) {
int currentDist = this.nodes[i].getDistanceFromSource();
if (!this.nodes[i].isVisited() && currentDist < storedDist) {
storedDist = currentDist;
storedNodeIndex = i;
}
}
return storedNodeIndex;
}
// display result
public void printResult() {
String output = "Number of nodes = " + this.noOfNodes;
output += "\nNumber of edges = " + this.noOfEdges;
output += "\nDistance from "+sAt+" to "+eAt+":" + nodes[eAt].getDistanceFromSource();
System.out.println(output);
}
public Node[] getNodes() {
return nodes;
}
public int getNoOfNodes() {
return noOfNodes;
}
public Edge[] getEdges() {
return edges;
}
public int getNoOfEdges() {
return noOfEdges;
}
}
Addittionally here are the Edge.java and the Node.java classes.
Node.java:
import java.util.ArrayList;
public class Node {
private int distanceFromSource = Integer.MAX_VALUE;
private boolean visited;
private ArrayList<Edge> edges = new ArrayList<Edge>(); // now we must create edges
public int getDistanceFromSource() {
return distanceFromSource;
}
public void setDistanceFromSource(int distanceFromSource) {
this.distanceFromSource = distanceFromSource;
}
public boolean isVisited() {
return visited;
}
public void setVisited(boolean visited) {
this.visited = visited;
}
public ArrayList<Edge> getEdges() {
return edges;
}
public void setEdges(ArrayList<Edge> edges) {
this.edges = edges;
}
}
Edge.java
public class Edge {
private int fromNodeIndex;
private int toNodeIndex;
private int length;
public Edge(int fromNodeIndex, int toNodeIndex, int length) {
this.fromNodeIndex = fromNodeIndex;
this.toNodeIndex = toNodeIndex;
this.length = length;
}
public int getFromNodeIndex() {
return fromNodeIndex;
}
public int getToNodeIndex() {
return toNodeIndex;
}
public int getLength() {
return length;
}
// determines the neighbouring node of a supplied node, based on the two nodes connected by this edge
public int getNeighbourIndex(int nodeIndex) {
if (this.fromNodeIndex == nodeIndex) {
return this.toNodeIndex;
} else {
return this.fromNodeIndex;
}
}
}
I know it looks like a homework. Trust me it isn't. On the other hand I have not much time to finish it, that is why I do it at Sunday. Also I am aware how Dijkstra algorithm works, I understand the concept, I can do it on paper. But collecting the path is beyond me.
Thanks for Christian H. Kuhn's and second's comments I managed to come up with the code.
I modified it as follows (I only put in the relevant parts)
Node.java
Here I added a setPredecessor(Integer predecessor) and a getPredecessor() methods to set and get the value of the private variable predecessor (so I follow the original code's style too).
[...]
private int predecessor;
[...]
public int getPredecessor(){
return predecessor;
}
public void setPredecessor(int predecessor){
this.predecessor = predecessor;
}
[...]
Graph.java
Here I created the calculatePath() and getPath() methods. calculatePath() does what the commenters told me to do. The getPath() returns the ArrayLists for others to use.
[...]
private int sAt;
private int eAt;
private ArrayList<Integer> path = new ArrayList<Integer>();
[...]
public void calculateShortestDistances(int startAt, int endAt) {
[...]
if (tentative < nodes[neighbourIndex].getDistanceFromSource()) {
nodes[neighbourIndex].setDistanceFromSource(tentative);
nodes[neighbourIndex].setPredecessor(nextNode);
}
[...]
public void calculatePath(){
int nodeNow = eAt;
while(nodeNow != sAt){
path.add(nodes[nodeNow].getPredecessor());
nodeNow = nodes[nodeNow].getPredecessor();
}
}
public ArrayList<Integer> getPath(){
return path;
}
[...]
Main.java so here I can do this now:
[...]
Graph g = new Graph(edges);
g.calculateShortestDistances(5,8);
g.calculatePath();
String results = "";
ArrayList<Integer> path = g.getPath();
System.out.println(path);
[...]
I know it shows the path backwards, but that is not a problem, as I can always reverse it. The point is: I not only have the the distance from node to node, but the path through nodes too. Thank you for the help.

How efficiently sort a list by groups?

I need to group a given sort list by some given "blocks" or "groups" of elements. For example:
Given a list:
[A, B, C, D, E, F, G, H, I, J]
And groups
[A, C, D]
[F, E]
[J, H, I]
the result should be
[A, C, D, B, F, E, G, J, H, I]
The blocks of elements can not be mixed with non-group elements. The blocks should have the same order. The other elements of the list should mantain their order.
I have already found a solution. But it's not the most efficient code as you will see.
I'm using java 6 also...
public static List<CategoryProduct> sortProductsByBlocks(List<CategoryProduct> products, CategoryBlocks categoryBlocks) {
if (!validateCategoryBlocks(categoryBlocks)) {
return products;
}
Map<String, BlockView> mapProductByBlock = mapBlocksByPartnumber(categoryBlocks);
Map<String, BlockView> mapFirstProductByBlock = mapFirstProductByBlock(categoryBlocks);
Map<Integer, Block> blocksById = blocksById(categoryBlocks);
List<CategoryProduct> sortedProduct = Lists.newArrayList();
Map<String, CategoryProduct> productsMapByPartNumber = ProductHelper.getProductsMapByPartNumber(products);
List<CategoryProduct> processedProducts = Lists.newArrayList();
int j = 0;
for (int i = 0; i < products.size(); i++) {
CategoryProduct product = products.get(i);
if (blocksById.isEmpty() && !processedProducts.contains(product)) {
sortedProduct.add(j++, product);
processedProducts.add(product);
}
if (!processedProducts.contains(product) && (mapFirstProductByBlock.get(product.getPartNumber()) != null
|| mapProductByBlock.get(product.getPartNumber()) == null)) {
BlockView blockView = mapProductByBlock.get(product.getPartNumber());
if (blockView != null) {
Block block = blocksById.get(blockView.getBlockId());
if (block == null) {
sortedProduct.add(j++, product);
continue;
}
for (BlockProduct blockProduct : block.getProducts()) {
CategoryProduct categoryProduct = productsMapByPartNumber.get(blockProduct.getPartnumber());
sortedProduct.add(j++, categoryProduct);
processedProducts.add(categoryProduct);
}
blocksById.remove(blockView.getBlockId());
} else {
sortedProduct.add(j++, product);
processedProducts.add(product);
}
}
}
return sortedProduct;
}
Any advice to improve and make it faster will be welcome.
(edit with the improved code)
public static List<CategoryProduct> sortProductsByBlocks2(List<CategoryProduct> products,
CategoryBlocks categoryBlocks) {
if (!validateCategoryBlocks(categoryBlocks)) {
return products;
}
Map<String, Integer> blocksIdByFirstPartnumber = Maps.newHashMap();
List<String> partnumbersInBlocks = Lists.newArrayList();
for (int k = 0; k < categoryBlocks.getBlocks().size(); k++) {
Block block = categoryBlocks.getBlocks().get(k);
if (block != null && block.getProducts() != null) {
for (int i = 0; i < block.getProducts().size(); i++) {
BlockProduct blockProduct = block.getProducts().get(i);
if (i == 0) {
blocksIdByFirstPartnumber.put(blockProduct.getPartnumber(), k);
} else {
partnumbersInBlocks.add(blockProduct.getPartnumber());
}
}
}
}
CategoryProduct[] result = new CategoryProduct[products.size()];
Map<String, Integer> productsIndex = Maps.newHashMap();
Map<String, CategoryProduct> categoryProductByPartnumber = Maps.newHashMap();
int indexResult = 0;
for (CategoryProduct categoryProduct : products) {
String partNumber = categoryProduct.getPartNumber();
if (!partnumbersInBlocks.contains(partNumber)) {
if (blocksIdByFirstPartnumber.get(partNumber) != null) {
Block categoryProductBlock = categoryBlocks.getBlocks()
.get(blocksIdByFirstPartnumber.get(partNumber));
result[indexResult] = categoryProduct;
indexResult++;
for (int i = 1; i < categoryProductBlock.getProducts().size(); i++) {
BlockProduct blockProduct = categoryProductBlock.getProducts().get(i);
if (categoryProductByPartnumber.get(blockProduct.getPartnumber()) != null) {
result[indexResult] = categoryProductByPartnumber.get(blockProduct.getPartnumber());
} else {
productsIndex.put(blockProduct.getPartnumber(), indexResult);
result[indexResult] = null;
}
indexResult++;
}
} else {
result[indexResult] = categoryProduct;
indexResult++;
}
} else {
if (productsIndex.get(partNumber) != null) {
result[productsIndex.get(partNumber)] = categoryProduct;
} else {
categoryProductByPartnumber.put(partNumber, categoryProduct);
}
}
}
return Lists.newArrayList(Arrays.asList(result));
}
Performance:
Elements New algorithm Old algorithm
1200 0.002s 0.129s
12000 0.021s 14.673s
Form the code you submitted, I cannot figure out how your algorithm is fully working.
I can write another algorithm that will do the task.
Mark the first element for each group
[A,C,D] -> A
Remove from list(to_be_sorted) all elements from groups that are not marked
[A,C,D] -> remove [C,D]
perform sort on list
result ([A,B,F,G,J])
place removed element based on Mark
Initial Sorted List [A,B,F,G,J]
A->add [C,D]
List is [A,C,D,B,F,G,J]
B->as it is
F->add [E]
List is [A,C,D,B,F,E,G,J]
G->as it is
J->add [H,I]
Final Sorted List [A,C,D,B,F,E,G,J,H,I]
Time complexity is the same as sorting algorithm
By your definition it isn't entirely clear what the conditions are to merge the results from your given list and 'groups' ( arrays ). However, here is a solution based on your requirements using the assertion
"You want the first element of the list not contained in any of the groups inserted between the groups... "
public class MergeArrays {
private static final List<String> FIRST = new ArrayList<>(Arrays.asList("A", "B", "C", "D", "E", "F", "G", "H", "I", "J"));
private static final List<String> SECOND = new ArrayList<>(Arrays.asList("A", "C", "D"));
private static final List<String> THIRD = new ArrayList<>(Arrays.asList("F", "E"));
private static final List<String> FOURTH = new ArrayList<>(Arrays.asList("J", "H", "I"));
public static List<String> merge(List<String> source, List<String>... lists) {
List<String> result = new ArrayList<>();
for (List<String> list : lists) {
for (String value : list) {
source.remove(value);
}
}
for (List<String> list : lists) {
String value = null;
if (source.size() > 0) {
value = source.get(0);
source.remove(0);
}
result.addAll(merge(value, list));
}
return result;
}
public static List<String> merge(String value, List<String> list) {
List<String> result = new ArrayList<>(list);
if (value != null) {
result.add(value);
}
return result;
}
public static void main(String[] args) {
List<String> result = merge(FIRST, SECOND, THIRD, FOURTH);
System.out.println(result);
}
}
//Results
[A, C, D, B, F, E, G, J, H, I]

How to filter a collection of sets by intersection?

I need to union a collection of sets by intersection of sets and write a function with such signature
Collection<Set<Integer>> filter(Collection<Set<Integer>> collection);
Here is a simple example of sets
1) {1,2,3}
2) {4}
3) {1,5}
4) {4,7}
5) {3,5}
In this example we can see that sets 1, 3, and 5 intersect. We can rewrite it as a new set {1,2,3,5}. Also we have two sets that have intersections as well. They're 2 and 4, and we can create a new set {4,7}. The output result will be a collection of two sets: {1,2,3,5} and {4,7}.
I don't know from which point to start solving this task.
This should solve your use-case. It may be implemented in a more efficient way, but I guess this should give you an idea to start with:
private static Collection<Set<Integer>> mergeIntersections(Collection<Set<Integer>> collection) {
Collection<Set<Integer>> processedCollection = mergeIntersectionsInternal(collection);
while (!isMergedSuccessfully(processedCollection)) {
processedCollection = mergeIntersectionsInternal(processedCollection);
}
return processedCollection;
}
private static boolean isMergedSuccessfully(Collection<Set<Integer>> processedCollection) {
if (processedCollection.size() <= 1) {
return true;
}
final Set<Integer> mergedNumbers = new HashSet<>();
int totalNumbers = 0;
for (Set<Integer> set : processedCollection) {
totalNumbers += set.size();
mergedNumbers.addAll(set);
}
if (totalNumbers > mergedNumbers.size()) {
return false;
}
return true;
}
private static Collection<Set<Integer>> mergeIntersectionsInternal(Collection<Set<Integer>> collection) {
final Collection<Set<Integer>> processedCollection = new ArrayList<>();
// ITERATE OVER ALL SETS
for (final Set<Integer> numberSet : collection) {
for (final Integer number : numberSet) {
boolean matched = false;
// ITERATE OVER ALL PROCESSED SETS COLLECTION
for (final Set<Integer> processedSet : processedCollection) {
// CHECK OF THERE IS A MATCH
if (processedSet.contains(number)) {
matched = true;
// MATCH FOUND, MERGE THE SETS
processedSet.addAll(numberSet);
// BREAK OUT OF PROCESSED COLLECTION LOOP
break;
}
}
// IF NOT MATCHED THEN ADD AS A COLLECTION ITEM
if (!matched) {
processedCollection.add(new HashSet<>(numberSet));
}
}
}
return processedCollection;
}
This is how it executed it:
public static void main(String[] args) {
final Collection<Set<Integer>> collection = new ArrayList<>();
final Set<Integer> set1 = new HashSet<>();
set1.add(1);
set1.add(2);
set1.add(3);
collection.add(set1);
final Set<Integer> set2 = new HashSet<>();
set2.add(4);
collection.add(set2);
final Set<Integer> set3 = new HashSet<>();
set3.add(1);
set3.add(5);
collection.add(set3);
final Set<Integer> set4 = new HashSet<>();
set4.add(4);
set4.add(7);
collection.add(set4);
final Set<Integer> set5 = new HashSet<>();
set5.add(3);
set5.add(5);
collection.add(set5);
System.out.println(mergeIntersections(collection));
}
Here’s my go. It deletes all sets from the input collection, this could be easily fixed by making a copy first. It does not modify each set in the input collection. With my implementation Ajay’s main method prints [[1, 2, 3, 5], [4, 7]].
Collection<Set<Integer>> filter(Collection<Set<Integer>> collection) {
Collection<Set<Integer>> mergedSets = new ArrayList<>(collection.size());
// for each set at a time, merge it with all sets that intersect it
while (! collection.isEmpty()) {
// take out the first set; make a copy as not to mutate original sets
Set<Integer> currentSet = new HashSet<>(removeOneElement(collection));
// find all intersecting sets and merge them into currentSet
// the trick is to continue merging until we find no intersecting
boolean mergedAny;
do {
mergedAny = false;
Iterator<Set<Integer>> it = collection.iterator();
while (it.hasNext()) {
Set<Integer> candidate = it.next();
if (intersect(currentSet, candidate)) {
it.remove();
currentSet.addAll(candidate);
mergedAny = true;
}
}
} while (mergedAny);
mergedSets.add(currentSet);
}
return mergedSets;
}
private static Set<Integer> removeOneElement(Collection<Set<Integer>> collection) {
Iterator<Set<Integer>> it = collection.iterator();
Set<Integer> element = it.next();
it.remove();
return element;
}
/** #return true if the sets have at least one element in common */
private static boolean intersect(Set<Integer> leftSet, Set<Integer> rightSet) {
// don’t mutate, take a copy
Set<Integer> copy = new HashSet<>(leftSet);
copy.retainAll(rightSet);
return ! copy.isEmpty();
}
An elegant way to solve this problem is using Undirected Graphs, where you connect an element from an input set with at least one other element from the same set, and then look for the Connected Components.
So the graph representation of your example is:
And from that we can easily infer the Connected Components: {1, 2, 3, 5} and {4, 7}.
Here is my code:
Collection<Set<Integer>> filter(Collection<Set<Integer>> collection) {
// Build the Undirected Graph represented as an adjacency list
Map<Integer, Set<Integer>> adjacents = new HashMap<>();
for (Set<Integer> integerSet : collection) {
if (!integerSet.isEmpty()) {
Iterator<Integer> it = integerSet.iterator();
int node1 = it.next();
while (it.hasNext()) {
int node2 = it.next();
if (!adjacents.containsKey(node1)) {
adjacents.put(node1, new HashSet<>());
}
if (!adjacents.containsKey(node2)) {
adjacents.put(node2, new HashSet<>());
}
adjacents.get(node1).add(node2);
adjacents.get(node2).add(node1);
}
}
}
// Run DFS on each node to collect the Connected Components
Collection<Set<Integer>> result = new ArrayList<>();
Set<Integer> visited = new HashSet<>();
for (int start : adjacents.keySet()) {
if (!visited.contains(start)) {
Set<Integer> resultSet = new HashSet<>();
Deque<Integer> stack = new ArrayDeque<>();
stack.push(start);
while (!stack.isEmpty()) {
int node1 = stack.pop();
visited.add(node1);
resultSet.add(node1);
for (int node2 : adjacents.get(node1)) {
if (!visited.contains(node2)) {
stack.push(node2);
}
}
}
result.add(resultSet);
}
}
return result;
}
IMHO the best solution is Union-Find algorithm
An implemtation:
public class UnionFind {
Set<Integer> all = new HashSet<>();
Set<Integer> representants = new HashSet<>();
Map<Integer, Integer> parents = new HashMap<>();
public void union(int p0, int p1) {
int cp0 = find(p0);
int cp1 = find(p1);
if (cp0 != cp1) {
int size0 = parents.get(cp0);
int size1 = parents.get(cp1);
if (size1 < size0) {
int swap = cp0;
cp0 = cp1;
cp1 = swap;
}
parents.put(cp0, size0 + size1);
parents.put(cp1, cp0);
representants.remove(cp1);
}
}
public int find(int p) {
Integer result = parents.get(p);
if (result == null) {
all.add(p);
parents.put(p, -1);
representants.add(p);
result = p;
} else if (result < 0) {
result = p;
} else {
result = find(result);
parents.put(p, result);
}
return result;
}
public Collection<Set<Integer>> getGroups() {
Map<Integer, Set<Integer>> result = new HashMap<>();
for (Integer representant : representants) {
result.put(representant, new HashSet<>(-parents.get(representant)));
}
for (Integer value : all) {
result.get(find(value)).add(value);
}
return result.values();
}
public static Collection<Set<Integer>> filter(Collection<Set<Integer>> collection) {
UnionFind groups = new UnionFind();
for (Set<Integer> set : collection) {
if (!set.isEmpty()) {
Iterator<Integer> it = set.iterator();
int first = groups.find(it.next());
while (it.hasNext()) {
groups.union(first, it.next());
}
}
}
return groups.getGroups();
}
}

String Pathing BFS

I am trying to find the shortest path between two strings, and return an int of how many steps were taken. Given that I have a HashMap in which each String(key) has a String[](object) containing all of that strings neighbours.
This code is what I whipped up. I just took a basic BFS and tried to copy that, but I can't quite figure out a way to progress.
public class Main {
private static HashMap<String, String[]> list;
private static int makePath(String from, string to) {
int path = 0;
PriorityQueue<String> queue = new PriorityQueue<>();
queue.add(from);
while (!queue.isEmpty()) {
String u = queue.poll();
if (u == to) {
return path;
}
else {
for (String r : list.get(u)) {
...
}
return path;
}
}
return 0;
}
}
This is just an example of what my HashMap might look like:
Goat, adj[] {Fish, Cow, Chicken}
Cow, adj[] {Pig, Pigeon}
Fish, adj[] {Goat, Bulbasaur, Dolphin, Eagle}
From Fish to Cow I need two steps. From Fish to Goat and Goat to Fish.
So if you got any ideas feel free to share :)
I am thinking of using 2 Queues. I will enqueue the from word to the firstQueue, and while firstQueue is not empty, I will perform an alternating logic to store the neighbors in the other queue if that neighbor is still not equal to to.
It'll be clearer if I give the code,
private static int makePath(final HashMap<String, String[]> tree, final String from, final String to) {
int path = 0;
Queue<String> firstQueue = new PriorityQueue<>();
Queue<String> secondQueue = new PriorityQueue<>();
firstQueue.add(from);
while (!firstQueue.isEmpty()) {
String key = firstQueue.poll();
String[] neighbors = tree.get(key);
if (neighbors != null) {
path++;
for (String neighbor : neighbors) {
if (neighbor.equals(to)) {
return path;
} else {
secondQueue.add(neighbor);
}
}
}
while (!secondQueue.isEmpty()) {
key = secondQueue.poll();
neighbors = tree.get(key);
if (neighbors != null) {
path++;
for (String neighbor : neighbors) {
if (neighbor.equals(to)) {
return path;
} else {
firstQueue.add(neighbor);
}
}
}
}
}
return 0;
}

Categories