Behaviour of ArrayList in recursive method - java

I have an ArrayList of vectors which I am trying to find all possible distinct paths (without repeating the same node). But the ArrayList keeping record of the path seems to be shared and therefore leaking across all paths. My question is, is there an alternative list type that I should be using which prevents this? Thanks.
The available paths are (A->B, B->C, C->D, D->A, A->C)
For a start, I decided to deal with just paths with starting node A.
The output should be:
ABCD
ACD
I've written the code below with additional output to check what is going on as it is not working right. The output for the code below is:
Previous vector: SA
vector history size: 1
Next vector: AB
Previous vector: AB
vector history size: 2
Next vector: BC
Previous vector: BC
vector history size: 3
Next vector: CD
Previous vector: CD
vector history size: 4
Next vector: DA
Looped - ABCDA
ABCDA
ABC
AB
vector history size: 4
Next vector: AC
Looped - AC
AC
As you can see the last 4 lines from the second iteration of the while loop are wrong because the vector history size should be 1 (SA only) and "C" should not have been visited before but somehow the ArrayList for vector history from the first while loop's recursion has leaked over. Is this suppose to happen and what alternatives are there?
public static void main(String[] args) {
ArrayList<vector> vectorList = new ArrayList();
vectorList.add(new vector("A", "B"));
vectorList.add(new vector("B", "C"));
vectorList.add(new vector("C", "D"));
vectorList.add(new vector("D", "A"));
vectorList.add(new vector("A", "C"));
//to record vector history and initialize the start vector
ArrayList<vector> vectorHistory = new ArrayList();
//record the path
String path = "";
//method call
pathFinder(new vector("S", "A"), vectorHistory, vectorList, path);
}
//Recursive method. moves one node forward until there is no more nodes OR the next node is the same as a previously taken node
public static void pathFinder(vector prevVector, ArrayList<vector> vectorHistory, ArrayList<vector> vectorList, String path) {
vectorHistory.add(prevVector);
//add the current node to the path
path = path + prevVector.child;
System.out.println("Previous vector: "+ prevVector.parent+prevVector.child);
// search if there is a next node. looped to search all possible paths
while (vectorList.contains(prevVector)) {
System.out.println("vector history size: "+ vectorHistory.size());
//retrieve the next vector
vector nextVector = vectorList.get(vectorList.indexOf(prevVector));
System.out.println("Next vector: " + nextVector.parent + nextVector.child);
//remove current node so while loop can move to another possible path
vectorList.remove(vectorList.indexOf(prevVector));
//check if the next node has already been visited before
if (vectorHistory.contains(nextVector)) {
path=path+nextVector.child;
System.out.println("Looped - " + path);
} else {
pathFinder(nextVector, vectorHistory, vectorList, path);
}
}
System.out.println(path);
}
/*object vector */
public static class vector {
String parent, child;
public vector(String parent, String child) {
this.parent = parent;
this.child = child;
}
#Override
public boolean equals(Object o) {
vector x = (vector) o;
if (x.parent.equalsIgnoreCase(child)) {
return true;
} else {
return false;
}
}
}

Java is "pass by value" so it passes a copy of the reference of the actual object. But it is a bit strange to understand when using a collection because the copy of the reference that is sent points to the same memory as the original one!
So if you pass a list to a method and you modify the method in the list it modifies the original list.
For example:
method b(List aList){
aList.add(new Object());
}
method c(List aList){
aList=new ArrayList ();
aList.add(new Object());
}
List a=new ArrayList();
b(a); -> it will add an object to a;
c(a); -> it will not add an object to a or modify it in any way
So in your case when you call
pathFinder(nextVector, vectorHistory, vectorList, path); you don't get that "stack" behaviour you expect with recursion because the successor calls of path finder modify the lists for the previous ones.
You can modify your call like that:
pathFinder(nextVector, new ArrayList<>(vectorHistory), new ArrayList<>(vectorList), path);
to avoid that problem but it will lose some additional memory copying the whole list every time ;) and it still won't get the results you want because I guess you have another error in the algorithm.
Your program seems very strange ;) The magic you are doing with vector's equal is not great because you cannot actually compare two equal objects. For example with your code AB is different than AB (which is not the case). So for places you have been you don't need vectors but points. So here is a bit modified program just to illustrate what I mean. It is still far from perfect:
import java.util.ArrayList;
import java.util.List;
public class MainClass {
public static void main(String[] args) {
List<MyVector> vectorList = new ArrayList<MyVector>();
vectorList.add(new MyVector("A", "B"));
vectorList.add(new MyVector("B", "C"));
vectorList.add(new MyVector("C", "D"));
vectorList.add(new MyVector("D", "A"));
vectorList.add(new MyVector("A", "C"));
List<String> pointsHistory=new ArrayList<String>();
//to record points that have been visited
//record the path
String path = "";
//method call
pathFinder(new MyVector(null, "A"), pointsHistory, vectorList, path);
}
//Recursive method. moves one node forward until there is no more nodes OR the next node is the same as a previously taken node
public static void pathFinder(MyVector prevVector, List<String> pointsHistory, List<MyVector> vectorList, String path) {
pointsHistory.add(prevVector.child);
//add the current node to the path
path = path + prevVector.child;
// search if there is a next node. looped to search all possible paths -> no need to do magic with equals
for(MyVector vector:vectorList)
if(vector.parent.equals(prevVector.child)) {
System.out.println("Next vector: " + vector.parent + vector.child);
if (pointsHistory.contains(vector.child)) {
System.out.println("Result " + path); //You get the end result here -> if we have reached a loop
} else {
pointsHistory.add(vector.child);
pathFinder(vector, new ArrayList<>(pointsHistory), vectorList, path);
}
}
}
/*object vector */
public static class MyVector {
String parent, child;
public MyVector(String parent, String child) {
this.parent = parent;
this.child = child;
}
}
}
You will get the results you want like that. See how I copy the visited points here: pathFinder(vector, new ArrayList<>(pointsHistory), vectorList, path); in order for that the work. And please name your classes with a Capital letter.

Related

Print all possible routes between C to D in Graph only visiting once

Working on a problem for graph traversal given a start and end.
Example if given edge routes:
("A","B")
("A","C")
("A","D")
("B","C")
("B","D")
Answer would be:
(C,B,D)
(C,A,D)
(C,A,B,D)
(C,B,A,D)
I am implementing this with two methods addRoute and printRoutes that take a start and des(the source and destination).
I created addRoute to add them, but having trouble trying to find a good solution to print all unique routes.
Solution so far is to convert it to an adjacency list and to run DFS or BFS on it. I need some help creating the BFS/DFS algoritum for this.
public class Graph{
List<List<String>> edges= new ArrayList<>();
Map<String, Set<String>> adjList= new HashMap<>();
void addRoutes(String start, String des) {
List<String> temp1 = new ArrayList<>();
temp1.add(start);
temp1.add(des);
edges.add(temp1);
adjList.putIfAbsent(start, new HashSet<>());
adjList.putIfAbsent(des, new HashSet<>());
adjList.get(start).add(des);
adjList.get(des).add(start);
}
void printRoutes(String start, String des) {
Set<String> visited = new HashSet<>();
// Mark the current node as visited and enqueue it
// Create a queue for BFS
Queue<List<String> > queue = new LinkedList<>();
// Path vector to store the current path
List<String> path = new ArrayList<>();
path.add(start);
queue.offer(path);
while (!queue.isEmpty()) {
path = queue.poll();
String last = path.get(path.size() -1);
if (last == des) {
int size = path.size();
for(String v : path) {
System.out.print(v + " ");
}
}
Set<String> lastNode = adjList.get(last);
for (String neig : adjList.get(start)) {
if (!visited.contains(neig)) {
List<String> newpath = new ArrayList(path);
visited.add(start);
queue.offer(newpath);
}
}
}
}
public static void main(String[] args) {
Graph g = new Graph();
g.addRoutes("A","B");
g.addRoutes("A","C");
g.addRoutes("A","D");
g.addRoutes("B","C");
g.addRoutes("B","D");
System.out.println(g.edges);
System.out.println(g.adjList);
}
}
Yes, using BFS (or DFS) is right idea for getting all simple paths between vertices.
You just need to modify these algorithms a bit: BFS uses global visited marks data. Instead make visited data structure local to every branch - as parameter of recursive function in case of recursive implementation.

How would this Breadth-First-Search be converted into a static method in Java?

I wrote this static method in Python to do breadth first search. However, I mainly use Java, and I want to get a sense of how the data structures convert to Java, given generics, etc. My code is:
def bfs(graph, start_vertex, target_value):
path = [start_vertex] #a list that contains start_vertex
vertex_and_path = [start_vertex, path] #a list that contains start_vertex and path
bfs_queue = [vertex_and_path]
visited = set() #visited defined as an empty set
while bfs_queue: #while the queue is not empty
current_vertex, path = bfs_queue.pop(0) #removes element from queue and sets both equal to that first element
visited.add(current_vertex) #adds current vertex to visited list
for neighbor in graph[current_vertex]: #looks at all neighboring vertices
if neighbor not in visited: #if neighbor is not in visited list
if neighbor is target_value: #if it's the target value
return path + [neighbor] #returns path with neighbor appended
else:
bfs_queue.append([neighbor, path + [neighbor]]) #recursive call with path that has neighbor appended
a graph I'd use this on would be:
myGraph = { //I'm not sure how to structure this in Java
'lava': set(['sharks', 'piranhas']),
'sharks': set(['lava', 'bees', 'lasers']),
'piranhas': set(['lava', 'crocodiles']),
'bees': set(['sharks']),
'lasers': set(['sharks', 'crocodiles']),
'crocodiles': set(['piranhas', 'lasers'])
}
and I would call it like
public static void main(String[] args){
System.out.println(bfs(myGraph, "crocodiles", "bees"));
}
So far, here's the Java I have:
public class BreadthFirstSearch{
///NOT DONE YET
public static ArrayList<String> BFS(Map<String, String[]> graph, String start, String target) {
List<String> path = new ArrayList<>();
path.add(start);
List<String> vertexAndPath = new ArrayList<>();
vertexAndPath.add(start);
vertexAndPath.add(path.get(0));
ArrayList<String> queue = new ArrayList<>();
queue.add(vertexAndPath.get(0));
queue.add(vertexAndPath.get(1));
Set<String> visited = new HashSet<String>();
while(!queue.isEmpty()) {
String currentVertex = queue.remove(0);
String curVerValue = currentVertex;
path.add(currentVertex);
.
.
.
}
}
}
Good effort on the translation. Let me offer my code, then an explanation:
import java.util.*;
class BreadthFirstSearch {
public static ArrayList<String> BFS(
Map<String, String[]> graph, String start, String target
) {
Map<String, String> visited = new HashMap<>();
visited.put(start, null);
ArrayDeque<String> deque = new ArrayDeque<>();
deque.offer(start);
while (!deque.isEmpty()) {
String curr = deque.poll();
if (curr.equals(target)) {
ArrayList<String> path = new ArrayList<>();
path.add(curr);
while (visited.get(curr) != null) {
curr = visited.get(curr);
path.add(curr);
}
Collections.reverse(path);
return path;
}
for (String neighbor : graph.get(curr)) {
if (!visited.containsKey(neighbor)) {
visited.put(neighbor, curr);
deque.offer(neighbor);
}
}
}
return null;
}
public static void main(String[] args) {
Map<String, String[]> myGraph = new HashMap<>();
myGraph.put(
"lava", new String[] {"sharks", "piranhas"}
);
myGraph.put(
"sharks", new String[] {"lava", "bees", "lasers"}
);
myGraph.put(
"piranhas", new String[] {"lava", "crocodiles"}
);
myGraph.put(
"bees", new String[] {"sharks"}
);
myGraph.put(
"lasers", new String[] {"sharks", "crocodiles"}
);
myGraph.put(
"crocodiles", new String[] {"piranhas", "lasers"}
);
System.out.println(BFS(myGraph, "crocodiles", "bees"));
System.out.println(BFS(myGraph, "crocodiles", "crocodiles"));
System.out.println(BFS(myGraph, "crocodiles", "zebras"));
}
}
Output
[crocodiles, lasers, sharks, bees]
[crocodiles]
null
Explanation
I made the design decision to avoid copying a path ArrayList on every node in the graph in favor of a visited hash that stores nodes in childNode => parentNode pairs. This way, once I've located the destination node, I retrace my steps to create the path in one shot, instead of building a path for every node, most of which ultimately lead nowhere. This is more efficient in space and time; Python makes it too easy to ruin your time complexity with the [] + [] O(n) list concatenation operator.
Using a child => parent visited HashMap is also simpler to code in Java, which doesn't have a light weight Pair/Tuple/struct that can conveniently store different types as nodes in the queue. To do what you're doing in Python in passing a 2-element list into the queue, you'd either have to write your own Pair class, use two ArrayDeques, or avoid generics and use casting, all of which are ugly (especially the last, which is also unsafe).
Another issue I noticed in your code is usage of an ArrayList as a queue. Insertion and removal on the front of a list is a O(n) operation as all elements in the list must be shifted forward or backward in the underlying array to maintain sequencing. The optimal queue structure in Java is an ArrayDeque, which offers O(1) add and removal at both ends and is not thread safe, unlike the Queue collection.
Similarly, in Python, you'll find performance is best using the deque collection which offers a fast popleft operation for all your queuing needs. Additionally, in your Python implementation, each key in your hash points to a set, which is okay, but seems like an unnecessary structure when a list would do (you've switched to a primitive array in Java). If you're not manipulating the graph and only iterating over neighbors, this seems ideal.
Note that this code also presumes that every node has a key in the hash that represents the graph, as your input does. If you plan to input graphs where nodes may not have keys in the hash, you'll want to make sure that graph.get(curr) is wrapped with a containsKey check to avoid crashing.
Another assumption worth mentioning: ensure your graph doesn't contain nulls since the visited hash relies on null to indicate that a child has no parent and is the start of the search.
You would need to create a separate class to hold nodes of a graph. Those nodes could not be static, as they all have unique vertexes. From there the rest is very similar.
public class Node {
public String name;
public ArrayList<Node> vertices;
public void addEdge(Node node) {
edges.add(node);
}
}

How can I display which elements were removed from list in custom OrderedLinkedList class?

I am implementing a custom Ordered LinkedList class with a nested Ordered ListNode class. Everything is working fine, but I am trying to expand on it by accessing the elements that are removed.
This is not a requirement, but I am curious how this would work since I can only use the methods I was instructed to create, which are boolean add(), boolean remove(), and clear().
I am also keeping track of each modification, which is incremented with each successful addition, removal, or call to clear(). I can simply create another OrderedLinkedList, and add the removed elements to it, but I feel like I'm adding an unnecessary modification count.
Again, this part is just for fun and not required. I feel this will give me a deeper understanding of creating custom classes.
I'll show the remove and main methods. The remove method signature cannot be changed.
public boolean remove(Comparable obj) {
for(OrderedListNode element = head.next; element != tail; element = element.next) {
if(obj.equals(element.dataItem)) { //if element being removed is at the cursor
OrderedListNode previousNode = element.before;
OrderedListNode nextNode = element.next;
nextNode.before = previousNode; //places next element that's after before to the element after current element [prev -> current -> next]
previousNode.next = nextNode; //places prev of next element to the element before current
element.dataItem = (Comparable)NOT_FOUND; //removed element is now null
modCount++; //another modification
theSize--; //reduce the size by 1
return true; //if remove is successful
}
}
return false; //otherwise, not successful removal
}
Main method:
public static void main(String[] args) {
OrderedLinkedList list = new OrderedLinkedList();
OrderedLinkedList removedList = new OrderedLinkedList();
modCount = 0;
list.add("Dog");
list.add("Bird");
list.add("dog");
list.add("bird");
list.add("Cat");
System.out.println("Before removal of element");
System.out.println(list);
list.remove("Dog");
removedList.add("Dog"); //not what I'm wanting to do
System.out.println("Removed " + removedList);
System.out.println("After removal of element");
System.out.println(list);
System.out.println("Total modifications = " + modCount);
System.out.println();
}
Output:
Before removal of element
Bird, Cat, Dog, bird, dog
Removed Dog //not actually accessing the element originally removed. just printing a new list
After removal of element
Bird, Cat, bird, dog
Total modifications = 7 //unnecessary modification due to additional add
If you just want to store the elements that you have removed without increasing your modification count, you can use ArrayList and put your removed elements into it. This way your modification count will not be impacted.
You can store the removed values by implementing an additional pop method. The return type should be Comparable and when the object to be removed found, store it in a temporary object and return that, instead of returning a boolean true. When the object is not found, simply return null.
If the Comparable object is found which is to be removed, the method will return that object so that you can store it. If not, a null will return so that you can use an if-check for the pop method to get that if remove is successful or not.
Here is a sample method I've just written for you;
Sample Pop Method
public Comparable pop(Comparable obj) {
for (OrderedListNode element = head.next; element != tail; element = element.next) {
Comparable temp = null; // declaration of the temporary object
if (obj.equals(element.dataItem)) { // if element being removed is
// at the cursor
temp = obj; // store obj in temp
OrderedListNode previousNode = element.before;
OrderedListNode nextNode = element.next;
nextNode.before = previousNode; // places next element that's
// after before to the element
// after current element [prev
// -> current -> next]
previousNode.next = nextNode; // places prev of next element to
// the element before current
element.dataItem = (Comparable) NOT_FOUND; // removed element is
// now null
modCount++; // another modification
theSize--; // reduce the size by 1
return temp; // if remove is successful
}
}
return null; // otherwise, not successful removal
}
Test Demo
Your test code should be like this;
public static void main(String[] args) {
OrderedLinkedList list = new OrderedLinkedList();
OrderedLinkedList removedList = new OrderedLinkedList();
modCount = 0;
list.add("Dog");
list.add("Bird");
list.add("dog");
list.add("bird");
list.add("Cat");
System.out.println("Before removal of element");
System.out.println(list);
// list.remove("Dog"); // not needed anymore
// removedList.add("Dog"); //not what I'm wanting to do
// pop returns the removed object
removedList.add(list.pop("Dog"));
System.out.println("Removed " + removedList);
System.out.println("After removal of element");
System.out.println(list);
System.out.println("Total modifications = " + modCount);
System.out.println();
}

BFS is not working correctly

I'm trying to implement a BFS to find all prerequisites required before a certain course can be taken. My public List<Node> computeAllPrereqs(String courseName) method is where my code is messing up. Can someone look at my method and help me find the issue? I figured the finish node would be none, because my the courses in my graph all lead to none. The graph is not cyclic.
This is the text file that I'm passing into the constructor, the first column is the course and followed by the course are its prerequisites:
CS1 None
CS2 CS1
CS3 CS2
CS4 CS1
CS5 CS3 CS4
CS6 CS2 CS4
.
public class Graph {
private Map<String, Node> graph;
public Graph(String filename) throws FileNotFoundException {
// open the file for scanning
File file = new File(filename);
Scanner in = new Scanner(file);
// create the graph
graph = new HashMap<String, Node>();
// loop over and parse each line in the input file
while (in.hasNextLine()) {
// read and split the line into an array of strings
// where each string is separated by a space.
Node n1, n2;
String line = in.nextLine();
String[] fields = line.split(" ");
for(String name: fields){
if (!graph.containsKey(fields[0]))
{
n1 = new Node(name);
graph.put(name, n1);
}
else{
n2 = new Node(name);
graph.get(fields[0]).addNeighbor(n2);
}
}
}
in.close();
}
public List<Node> computeAllPrereqs(String courseName){
// assumes input check occurs previously
Node startNode;
startNode = graph.get(courseName);
// prime the dispenser (queue) with the starting node
List<Node> dispenser = new LinkedList<Node>();
dispenser.add(startNode);
// construct the predecessors data structure
Map<Node, Node> predecessors = new HashMap<Node,Node>();
// put the starting node in, and just assign itself as predecessor
predecessors.put(startNode, startNode);
// loop until either the finish node is found, or the
// dispenser is empty (no path)
while (!dispenser.isEmpty()) {
Node current = dispenser.remove(0);
if (current == new Node(null)) {
break;
}
// loop over all neighbors of current
for (Node nbr : current.getNeighbors()) {
// process unvisited neighbors
if(!predecessors.containsKey(nbr)) {
predecessors.put(nbr, current);
dispenser.add(nbr);
}
}
}
return constructPath(predecessors, startNode, null);
}
private List<Node> constructPath(Map<Node,Node> predecessors,
Node startNode, Node finishNode) {
// use predecessors to work backwards from finish to start,
// all the while dumping everything into a linked list
List<Node> path = new LinkedList<Node>();
if(predecessors.containsKey(finishNode)) {
Node currNode = finishNode;
while (currNode != startNode) {
path.add(0, currNode);
currNode = predecessors.get(currNode);
}
path.add(0, startNode);
}
return path;
}
}
Here's the node class I used to make the nodes and neighbors in the graph:
public class Node {
/*
* Name associated with this node.
*/
private String name;
/*
* Neighbors of this node are stored as a list (adjacency list).
*/
private List<Node> neighbors;
public Node(String name) {
this.name = name;
this.neighbors = new LinkedList<Node>();
}
public String getName() {
return name;
}
public void addNeighbor(Node n) {
if(!neighbors.contains(n)) {
neighbors.add(n);
}
}
public List<Node> getNeighbors() {
return new LinkedList<Node>(neighbors);
}
#Override
public String toString() {
String result;
result = name + ": ";
for(Node nbr : neighbors) {
result = result + nbr.getName() + ", ";
}
// remove last comma and space, or just spaces in the case of no neighbors
return (result.substring(0, result.length()-2));
}
#Override
public boolean equals(Object other) {
boolean result = false;
if (other instanceof Node) {
Node n = (Node) other;
result = this.name.equals(n.name);
}
return result;
}
#Override
public int hashCode() {
return this.name.hashCode();
}
}
Here's my test class:
public class Prerequisite {
/**
* Main method for the driver program.
*
* #param args the name of the file containing the course and
* prerequisite information
*
* #throws FileNotFoundException if input file not found
*/
public static void main(String[] args) throws FileNotFoundException {
// Check for correct number of arguments
if(args.length != 1) {
String us = "Usage: java Prerequisite <input file>";
System.out.println(us);
return;
}
// create a new graph and load the information
// Graph constructor from lecture notes should
// be modified to handle input specifications
// for this lab.
Graph graph = new Graph(args[0]);
// print out the graph information
System.out.println("Courses and Prerequisites");
System.out.println("=========================");
System.out.println(graph);
// ASSUMPTION: we assume there are no cycles in the graph
// Part I:
// compute how many (and which) courses must be taken before it is
// possible to take any particular course
System.out.println("How many courses must I take "
+ "before a given course?!?!?");
for(String name : graph.getAllCourseNames()) {
List<Node> allPrereqs = graph.computeAllPrereqs(name);
System.out.print(String.valueOf(allPrereqs.size()));
System.out.print(" courses must be taken before " + name + ": ");
for(Node el : allPrereqs) {
System.out.print(el.getName() + " ");
}
System.out.println();
}
}
When I run this test my output is:
0 courses must be taken before CS1:
0 courses must be taken before CS3:
0 courses must be taken before CS2:
0 courses must be taken before CS5:
0 courses must be taken before CS4:
0 courses must be taken before CS6:
it should be:
0 courses must be taken before CS1:
2 courses must be taken before CS3: CS1 CS2
1 courses must be taken before CS2: CS1
4 courses must be taken before CS5: CS1 CS3 CS2 CS4
1 courses must be taken before CS4: CS1
3 courses must be taken before CS6: CS1 CS2 CS4
I know I'm posting a lot of code but I don't want to have to edit in more code in later if it is needed to help fix my bug.
As a note, prerequisites are more effectively determined with depth first search (DFS) such that a topological ordering can be realized.
During graph construction, when linking neighbors, "lookalikes" are being linked rather than existing graph nodes themselves, so the resulting graph is actually unconnected. To resolve that issue, link the actual nodes of the graph to each other.
if (!graph.containsKey(fields[0]))
{
n1 = new Node(name);
graph.put(name, n1);
}
else{
if(graph.containsKey(name)) {
n2 = graph.get(name);
}
else {
n2 = new Node(name);
graph.put(name, n2);
}
graph.get(fields[0]).addNeighbor(n2);
}
An added benefit of the above code snippet is that it adds the terminal "None" node to the graph.
A single line path can't be constructed because a course may have more than one prerequisite. For instance, CS6 depends on both CS2 and CS4. In the constructPath method, the terminal condition would fire after only following one of those paths. So given the current structure of the program, about the best you can achieve is to output the set of prerequisite courses rather than the single line path.
private List<Node> constructPath(Map<Node,Node> predecessors,
Node startNode, Node finishNode) {
/*
// use predecessors to work backwards from finish to start,
// all the while dumping everything into a linked list
List<Node> path = new LinkedList<Node>();
if(predecessors.containsKey(finishNode)) {
Node currNode = finishNode;
while (currNode != startNode) {
path.add(0, currNode);
currNode = predecessors.get(currNode);
}
path.add(0, startNode);
}
*/
Set<Node> prereqs = new HashSet<Node>(predecessors.keySet());
prereqs.remove(graph.get("None"));
return new ArrayList<Node>(prereqs);
}
Given this approach, neither arguments startNode nor finishNode are necessary and the creation of the predecessor map is redundant.
Lastly, a course is not a prerequisite of itself so it's incorrect to assign itself as a predecessor.
// put the starting node in, and just assign itself as predecessor
// predecessors.put(startNode, startNode);
Given these modifications, this was the output
0 courses must be taken before CS1:
1 courses must be taken before CS2: CS1
2 courses must be taken before CS3: CS2 CS1
1 courses must be taken before CS4: CS1
4 courses must be taken before CS5: CS4 CS2 CS3 CS1
3 courses must be taken before CS6: CS4 CS2 CS1
To improve on the code, a TreeSet (http://docs.oracle.com/javase/7/docs/api/java/util/TreeSet.html) can be used instead of a HashSet to output the prerequisites in a uniform order. To use TreeSet however, the Node class will have to be augmented to implement Comparable (How to implement the Java comparable interface?).
And again, if outputting the set of prerequisites is not satisfactory, consider using DFS instead to generate a topological ordering (http://www.personal.kent.edu/~rmuhamma/Algorithms/MyAlgorithms/GraphAlgor/topoSort.htm).
if (current == new Node(null)) {
break;
}
You should have used continue instead of break.
Even if you have encountered null-node, you can have more normal nodes on the queue, which have longer path to the null-node.
You can realize it when you analyze graph starting from CS5

Simple Java objects not deeply copying

This question should be rather easy for any Java developer. I swear I looked it up after spending ~2 hours on it, but I can't really understand what's wrong with this code.
Basically, I am implementing Karger's minimum cuts algorithm. It requires me to keep merging nodes in a graph and then compute the number of crossing edges at the end (an int value). This algorithm must be repeated n times, always from the starting graph. My problem is that I am unable to create a deep copy of my Graph object, and I can't find the mistake.
I have cropped the code to just show the problem and no more, but I am still unable to figure out what's wrong. Here the code is.
Class Node:
public class Node {
public Integer Data;
public Node() {
Data = 0;
}
public Node(Node rhs) {
Data = rhs.Data.intValue();
}
public Node(Integer rhs) {
Data = rhs.intValue();
}
public void setNode(Integer rhs) {
Data = rhs;
}
Class Graph:
public class Graph {
public ArrayList<ArrayList<Node>> AdjList;
public ArrayList<Node> NodeSet; // This contains all the nodes
public Graph() {
AdjList = new ArrayList<ArrayList<Node>>();
NodeSet = new ArrayList<Node>();
}
public Graph(Graph G) {
AdjList = new ArrayList<ArrayList<Node>>();
for (ArrayList<Node> L : G.AdjList) {
ArrayList<Node> Lcopy = new ArrayList<Node>();
for (Node N : L) {
Node copy = new Node(N);
Lcopy.add(copy);
}
AdjList.add(L);
}
}
public void addNewAdjList(ArrayList<Node> NodeAdjList) {
// Input is the adjacency list of a new node
// The first element in the NodeAdjList is the node itself, the rest is the adj nodes
AdjList.add(NodeAdjList);
}
public static void printAdjList(ArrayList<Node> Adjlist) {
Node start = Adjlist.get(0);
System.out.print(start.Data + " : ");
for (int j=1; j < Adjlist.size(); ++j) {
System.out.print(Adjlist.get(j).Data + ", ");
}
System.out.print("\n");
}
Main:
public class Main {
/**
* #param args
*/
public static void main(String[] args) {
Node Five = new Node(5);
Node Seven = new Node(7);
Node One = new Node(1);
Graph G = new Graph();
ArrayList<Node> L = new ArrayList<Node>();
L.add(Five);
L.add(Seven);
L.add(One);
G.addNewAdjList(L);
Graph R = new Graph(G);
R.AdjList.get(0).get(1).setNode(19); // Gets node #1 in the first adj list, i.e. 7
Graph.printAdjList(G.AdjList.get(0));
Graph.printAdjList(R.AdjList.get(0));
}
}
Output:
5 : 19, 1,
5 : 19, 1,
This kind of puzzles me to be honest. I understand that Java is pass by value only, but objects are always represented by their reference. As far as I understand, my copy constructor for G should always make a deep copy: I am moving through every adjacency list and then I am making a deep copy of the Node. I don't understand why invoking .setNode() on the copied object modifies also the original object (that has a different reference).
Previous answers like 1 seem to go the same direction I am going, what am I missing here? :S
Your error is here:
ArrayList<Node> Lcopy = new ArrayList<Node>();
for (Node N : L) {
Node copy = new Node(N);
Lcopy.add(copy);
}
AdjList.add(L);
You created a copy of L (called Lcopy) but then you added the original L to your cloned graph. To fix it the last line should be this:
AdjList.add(Lcopy);
Note: If you have used a sensible name for your variable instead of L this error would probably never have happened!

Categories