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!
Related
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.
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 2 years ago.
The code below shows a data structure in leetcode. We can see the node1 store a list with node2 and node3, node2 will store a list with node1 and node4. In this case I think node1 and node2 will store the object of each others, which will cause an infinite recursion. How does java store the data structure like this, doesn't it cause a memory exceed?
class Node {
public int val;
public List<Node> neighbors;
public Node() {
val = 0;
neighbors = new ArrayList<Node>();
}
public Node(int _val) {
val = _val;
neighbors = new ArrayList<Node>();
}
public Node(int _val, ArrayList<Node> _neighbors) {
val = _val;
neighbors = _neighbors;
}
}
public static void main(String[] args) {
Node node1 = new Node(1, new ArrayList<>());
Node node2 = new Node(2, new ArrayList<>());
Node node3 = new Node(3, new ArrayList<>());
Node node4 = new Node(4, new ArrayList<>());
node1.neighbors.add(node2);
node1.neighbors.add(node4);
node2.neighbors.add(node1);
node2.neighbors.add(node3);
node3.neighbors.add(node2);
node3.neighbors.add(node4);
node4.neighbors.add(node1);
node4.neighbors.add(node3);
Solution solution = new Solution();
solution.cloneGraph(node1);
}
You would be correct about this code causing memory to be exceeded if each node's list of neighbours contained copies of those neighbours. But that's not how Java works. Instead the list contains references to the neighbour nodes.
As an analogy, if each time you wrote down someone's address you need a complete copy of their house then you'd run out of space quickly. But you don't - you just need a reference to their house which can itself contain a reference to yours.
Note that's it's pretty easy to write code that causes a stack overflow with objects that contain references to themselves. For example, if your class had a method:
class Node {
public int sumVals() {
return val + neighbours.stream().mapToInt(Node::sumVals).sum();
}
}
calling node1.sumVals() will cause infinite recursion.
My teacher has assigned a program where I am to create a linked list of some random numbers. I am to create it from a list and then the second part of the assignment is to reverse it. The actual quote is
Write a Java method called reverseLinkedList() that will generate a
reversed linked-list from the linked-list that you create in problem
1. Your method should accept a linked-list as an input and return another linked list that has the node references in the reversed
order. Please do not print the original list in reverse. The idea is
to manipulate the node references so that the nodes are preserved in
same in order as they were originally created.
The code I have generated so far looks like
import java.util.*;
public class progassignment2
{
public static void main(String args[])
{
List<Integer> myList = new ArrayList<Integer>();
Random ran = new Random();
int ranNum;
for(int x = 0;x<5;x++)
{
ranNum = ran.nextInt(500);
myList.add(x,ranNum);
}
LinkedList<Integer> mylinklist = createLinkedList(myList);
System.out.println(mylinklist);
LinkedList<Integer> mylinklistrev = reverseLinkedList(mylinklist);
}
public static LinkedList createLinkedList(List<Integer> integerList)
{
LinkedList<Integer> linkedlist = new LinkedList<Integer>();
linkedlist.addAll(integerList);
return linkedlist;
}
public static LinkedList reverseLinkedList(LinkedList inputList)
{
for(int y = 0;y < inputList.size();y++)
{
inputList.addLast(inputList.pollFirst());
}
return inputList;
}
}
However I don't think I'm doing the assignment correctly, or that I understand what he is asking of me and unfortunately won't answer any questions and just cites "Read the assignment". Any help is greatly appreciated
What about:
public static LinkedList reverseLinkedList(List<Integer> inputList) {
LinkedList<Integer> reversedLinkedlist = new LinkedList<Integer>(inputList);
Collections.reverse(reversedLinkedlist);
return reversedLinkedlist;
}
Usually, exercises on linked lists do not make use of any built-in Java collection (like ArrayList, LinkedList, etc), but are instead meant to make you build your own collection type.
Your teacher probably wants you to build a very basic element, which would then become the building block of your own collection type: imagine an object where you can store a value and a reference to the following value in the list. In code:
class Node {
private int value;
private Node next;
public Node(int value){
this.value = value;
}
public int getValue(){
return value;
}
public Node getNext(){
return next;
}
public void setNext(Node next){
this.next = next;
}
}
Each element points to the next one, and the end of the list is marked by the last node's next element being null.
By using objects like this, you'll be able to define your own linked list, without using any pre-defined Collection offered by Java.
You've surely heard about the stack data structure: by reading all the elements in your linked list and putting them inside a stack, once the list will be over, you're going to fetch the elements inside the stack; creating a linked list in the order of the elements pulled from the stack will solve your problem of inverting the linked list.
The idea is to manipulate the node references so that the nodes are
preserved in same in order as they were originally created.
You should create your own LinkedList. You are not allowed to use common ways of reversing like using recursion, stack ,modifications or any collections interface methods.
here is the link includes LinkedList reversing ways and solution:
class LinkedList {
Node head; // head of list
/* Linked list Node */
class Node {
int data;
Node next;
Node(int d) {
data = d;
next = null;
}
}
/* Function to print reverse of linked list */
void printReverse(Node head) {
if (head == null)
return;
// print list of head node
printReverse(head.next);
// After everything else is printed
System.out.print(head.data + " ");
}
/* Inserts a new Node at front of the list. */
public void push(int new_data) {
Node new_node = new Node(new_data);
new_node.next = head;
head = new_node;
}
public static void main(String args[]) {
LinkedList llist = new LinkedList();
llist.push(4);
llist.push(3);
llist.push(2);
llist.push(1);
llist.printReverse(llist.head);
}
}
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
so I have a list of basic nodes, for example nodes A B C.
each component can see what it is attached to for example:
a->b
b->c
c->a
I want a way that I can get a list of all the nodes in the graph. However, I'm running into trouble as my current system can't detect if it has already reached a point. EG in the above example it will go a->b->c->a->b etc. How can I detect this or how can I solve this problem.
My current "solution" getList() in the Node class:
ArrayList<Node> tempList = new ArrayList<Node>();
tempList.add(this);
for(int i = 0 ; i < nodesAttachedTo.size();i++){
tempList.addAll(nodesAttachedTo.get(i).getList());
}
return tempList;
You can use a HashSet. It will not allow one element to be added twice.
Here's an example code that first creates the graph similar to your example, then starts at some point in the graph and goes through it.
import java.util.HashSet;
public class Node
{
private HashSet<Node> nextNodes = new HashSet<Node>();
public Node()
{
}
public void addNextNode(Node node)
{
nextNodes.add(node);
}
public static void main(String[] args)
{
// this builds the graph of connected nodes
Node a = new Node();
Node b = new Node();
Node c = new Node();
a.addNextNode(b);
b.addNextNode(c);
c.addNextNode(a);
//this is the set that will lsit all nodes:
HashSet<Node> allNodes = new HashSet<Node>();
// this goes through the graph
a.listAllNodes(allNodes);
System.out.println(allNodes);
}
private void listAllNodes (HashSet<Node> listOfNodes)
{
// try to put all next nodes of the node into the list:
for(Node n : nextNodes)
{
if (listOfNodes.add(n)) // the set returns true if it did in fact add it.
n.listAllNodes(listOfNodes); // recursion
}
}
}
This goes from one node to all the nodes this node knows. (say that really fast three times)
Until it hits a dead end (= a node it already visited)
I chose to use a HashSet in the Node itself to store all the nodes it knows.
This could also be an ArrayList or whatever. But as I don't think there should be a connection twice, a HashSet seems to be a good choice in this situation, too.
I'm not familiar with your notation, but you could use two pointers to solve your issue. Start with two pointers that point to the same node. Increment one pointer until it returns to the start. Some pseudocode is below.
ArrayList<Node> tempList = new ArrayList<Node>();
Node head = nodesAttachedTo.get(0); //get the head of the list
tempList.add(head);
Node runner = head;
runner = runner.next;
while (!runner.equals(head)) {
tempList.add(runner);
runner = runner.next;
}
A hashmap is probably the way to go here. It allows constant time access (some overhead required, but I'm assuming you want a solution that scales well) to any element in the map.
HashMap<String, String> specificSolution = new HashMap<String, String>();
specificSolution.put("a", "b");
specificSolution.put("b", "c");
specificSolution.put("c", "a");
// To get all nodes in the graph
Set<String> nodes = specificSolution.keySet();
I implemented with String here because you don't provide a definition for the Node Class in your question, but it can be easily swapped out.
There are many different ways to represent a graph and each has their own limitations/advantages. Maybe another might be more appropriate but we would need more information about the problem.