For one of my projects in my computer science class, I have to implement a directed, weighted graph. Since we are only allowed to use an adjacency list and not an adjacency matrix, I decided to create a Node class that will contain my data, as well as a treeset to hold all of its edges, since the project stipulates that the edges must be sorted using the natural ordering of whatever data my graph is instantiated with. My Node class looks like this:
private class Node<V extends Comparable<V>> {
private V data;
private TreeSet<Edge> edges = new TreeSet<Edge>();
public Node(V data) {
this.data = data;
}
}
My graph itself is also a treeset that contains objects of type Node. I cannot, however, add to the graph using my add method which is as follows:
private boolean addVertex(V vertex) {
Iterator iter = graph.iterator();
Node check;
while (iter.hasNext()) {
check = (Node) iter.next();
if (check.data.compareTo(vertex) == 0)
return false;
}
Node n = new Node(vertex);
graph.add(n);
return true;
}
How can I make it so that my code adds Nodes to my graph class using the natural ordering of the data that the Node class is instantiated with?
**Edit
So based on what Peter said, I was able to come up with a solution (sort of) to my problem. Since my edges, per project stipulation, must be iterated over in the natural order of the data held in the nodes, I created a compareTo method in my edge class that works by using the data's compareTo method. It looks something like this:
private class Edge<V extends Comparable<V>> implements Comparable<V> {
private int weight;
private boolean visited;
//This is the data held in the node the edge ends at
private V endNode;
public Edge(V dest, int weight) {
visited = false;
endNode = dest;
this.weight = weight;
}
public int compareTo(Edge<V> e) {
if (endNode.compareTo((V) e.endNode) < 0)
return -1;
else if (endNode.compareTo((V) e.endNode) == 0)
return 0;
else
return 1;
}
}
Unfortunately, when I try and do this, I get two errors. one of them says "the type parameter V is hiding the type V" and the other says I must implement the Comparable.compareTo(V) method even though I explicitly do it in my code. I was able to find some information on the first error, which said that I could be getting that error because I used V as a concrete type somewhere in my code, however, that does not really help me much since I don't fully understand it. I did that in my Node class and nothing happened, so why am I getting errors in my Edge class? Any help would be greatly appreciated.
Also the class that both Node and Edge are declared in is defined as
public class Graph<V extends Comparable<V>>
if that helps anyone
The wikipedia page http://en.wikipedia.org/wiki/Adjacency_list is a good starting point.
Also, google for this "Goodrich and Tamassia adjacency".
This is a good starting point too.
Because your Graph is weighted and directed, it means you can/should associate with each Vertex the list of its outgoing (or alternatively incoming) Edges. An Edge is then an ordered couple of its two vertices (start vertex and end vertex) and whatever additional information you may want to store in it (e.g. its weight). That's what you need here.
Seems to me the natural ordering you're referring to is about the Edges, not about the Vertices. But you can have some ordering the vertices too.
You can also have a Graph class. The Graph can be just your top-level container class which gives you fast access to e.g.: 1) a Vertex given its id/name (or whatever piece of data you use to identify it), 2) an Edge given e.g. its start and end vertices, other methods, etc.
In general your code so far looks OK btw. I mean you seem to be on the right track.
Related
I'm implementing a Graph in Java.
The Graph class uses a LinkedList for the vertices. And each vertex also contains a LinkedList of adjacent vertices.
I'm still working on my methods. I just need a quick clarification with a getVertex() method which accepts a String label and returns a Vertex that matches that label.
public class Graph
{
private class Vertex
{
private String label;
private LinkedList links;
private boolean visited;
Vertex(String label)
{
this.label = label;
LinkedList links = new LinkedList();
visited = false;
}
private void addEdge(Vertex vertex)
{
links.insertLast(vertex);
}
private String getLabel()
{
return label;
}
private LinkedList getAdjacent()
{
return links;
}
private boolean isVisited()
{
return visited;
}
private void visit()
{
visited = true;
}
private void unvisit()
{
visited = false;
}
}
/* Classfields for Graph Class */
private LinkedList vertices; //Linked List for the vertices in the graph
private int vCount;
private int eCount;
public Graph()
{
LinkedList vertices = new LinkedList();
vCount = 0;
eCount = 0;
}
public void addVertex(String label)
{
Vertex newVertex = new Vertex(label);
vertices.insertLast(newVertex);
vCount++;
}
public int getVertexCount()
{
return vCount;
}
public Vertex getVertex(String label)
{
// what to return?
}
It should be very simple, but I can't understand how I'm going to import this label but return a Vertex, working with a LinkedList. Would appreciate any tips!
If you are working on assignment on an assignment, and you are expected to use LinkedList that's fine, but it's the best choice of collection that serves as a storage of all vertices in the graph and also as the adjacency list of vertex
I suggest you addressing these issues:
Firstly, don't use row types LinkedList links, you should always specify a generic type parameter List<Vertex>.
Write your code against interfaces, not against implementations. I.e. use List<Vertex> instead of LinkedList<Vertex>. It makes your code more flexible.
In order to be able to retrieve a particular vertex by label, you can use a Map<String, Vertex> to store all vertices of the graph. With that time complexity of the getVertex() will be reduced to constant time, it's way faster than iterate over the list. And the code is a single line vertexByLabel.get(label).
Maintaining a variable that hold a count of vertices is redundant because you can check the size of collection of vertices to get this value.
ArrayList performs than LinkedList and has a better memory consumption. For that reason, it considered to be a general purpose implementation of the List interface and it's a preferred choice if you don't expect use cases like removal of elements by the means of Iterator while iterating over the list (which will be done in constant time, here LinkedList really shines). Also, HashSet might be useful in a role of the collection of adjacencent vertices because it will all you to ensure that there will be no duplicates.
So in regard to getVertex() method, if you'll agree with the suggestion to use map, the code will look like this:
private Map<String, Vertex> vertexByLabel = new HashMap<>(); // it is advisable to initialise collections, if you are not passing argument with collection that holds values to the constructor, but just assigning a new collection
public Vertex getVertex(String label) {
return vertexByLabel.get(label);
}
I also advise you to make changes to the methods addVertex() and addEdge(). Firstly, I would rather expect to a method called addVertex() inside the Vertex class (we are adding a new vertex to the adjacency list of this vertex) and a method addEdge() inside the Graph (we are connecting vertices inside the graph).
And if order to connect the vertices method addEdge() of the graph will expect a vertex label as its first argument, and labes of the adjacent vertices as a variable arity argument (varargs).
In case if you have a strong requirement to utilize LinkedLinked exclusively and not allowed to use generic types. But frankly spiking, it doesn't seem a bright idea to disallow student to use generics. It doesn't reduce complexity a lot because instead you have to deal with manual down-casts, and it's a very bad practice.
Your method might look like this:
public Vertex getVertex(String label) {
Vertex result = null;
for (Object next: vertices) { // all elements in the collection of row type a treated by the compiler as being of type Object
Vertex vertex = (Vertex) next; // you need to cast the element into the Vertex type in order to be able to access its field `label`
if (vertex.label.equals(label)) {
result = vertex;
break;
}
}
return result;
}
I am currently working on an assignment for class where I am tasked with creating an empty List that has a Comparator as an argument then creating an add method for that sortedDoublyLinkedList where I am passed an argument and I have to iterate through the list to find where the new node fits. I'm not very familiar with Comparator so I'm a bit clueless as to how to add elements to my DoublyLinkedList because I cannot access the Comparator the way I though I was supposed to. Here is what I have now. Here is what I currently have.
public class SortedDoubleLinkedList<T> extends BasicDoubleLinkedList<T> {
Node<T> head=null;
Node<T> tail=null;
SortedDoubleLinkedList<T> sDLL;
public SortedDoubleLinkedList(Comparator<T> comparator2){
sDLL=new SortedDoubleLinkedList<T>(comparator2);
}
public SortedDoubleLinkedList<T> add(T data){
Node<T> newNode=new Node<T>(data);
//I have to iterate through the list and find where the new element data fits
if(head!=null&&tail!=null) {
Node<T> cursor=head;
while(cursor!=null) {
//the following code doesn't work
if(sDLL.comparator2.compare(data, cursor.getData())==0) {
}
}
}
else {
head=newNode;
tail=newNode;
}
return this; //return the SortedDoubleLinkedList<T>
}
Comparator is an interface. You need to implement a class that will provide that interface.
class Whatever implements Comparator<TYPE> {
int compare(TYPE a, TYPE b) {
... code to decide whether a is less than,
equal to, or greater than b ...
}
}
Where I wrote TYPE, you need an actual type. Just supplying the type variable T is not going to get you to runnable code, which I assume is your goal. Ultimately you've got to say what type will go in your list. So I'd be expecting something like (in your code above)
public class SortedDoubleLinkedList extends BasicDoubleLinkedList<String> {
where you're storing Strings in your list. And then TYPE in my code is also String.
ALTERNATIVELY
You can leave your SortedDoubleLinkedList generic (in terms of T) but ultimately you want to get concrete about it, maybe
SortedDoubleLinkedList<String> = new SortedDoubleLinkedList(new Whatever());
but the Comparator is still going to need to be a Comparator<String> (or whatever type you choose).
Suppose I have this class:
public class Node implements Comparable<Node>{
public float key;
public TreeSet<Node> neighbors;
public Node{
//fill neighbors somehow
}
#Override
public int compareTo(Node n) {
if(this.key == n.key)
return 0;
else if(this.key > n.key)
return 1;
else
return -1;
}
}
So this is a classic node of a graph, where each node is connected to a set of nodes (i.e. its neighbors). I'm using TreeSet because I often (very often) to know all the neighbors with their key bigger (smaller) than a certain value. Now, let's suppose I have this method:
//swap nodes keys
void swapKeys(Node a, Node b){
float ak = a.key;
a.key = b.key;
b.key = ak;
}
Notice that this method changes only the two nodes keys, nothing more.
Do this "break" the structure, or everything will continue to work fine?
If this breaks the structure, what about this simple solution:
//swap nodes keys
void swapKeys(Node a, Node b){
a.remove(b);
b.remove(a);
float ak = a.key;
a.key = b.key;
b.key = ak;
a.add(b);
b.add(a);
}
From the TreeSet documentation :
Note that the ordering maintained by a set (whether or not an explicit
comparator is provided) must be consistent with equals if it is to
correctly implement the Set interface.
Your Node class' Comparable implementation is not consistent with equals. (compareTo can return 0 for two Node instances wich are not equal).
This in itself makes your Node class unfit to be elements of a TreeSet.
Even the proposed workaround is not sufficient.
You may be tempted to fix this by implementing equals() (and hashCode()) to be based upon the value contained in the node. But to no avail, as this would go against a warning on the documention of the general Set interface :
Note: Great care must be exercised if mutable objects are used as set
elements. The behavior of a set is not specified if the value of an
object is changed in a manner that affects equals comparisons while
the object is an element in the set. A special case of this
prohibition is that it is not permissible for a set to contain itself
as an element.
So adding equals and hashCode is still not sufficient : your instances must also be immutable.
However the simplest solution, seems to be to forego the Comparable interface altogether, to not implement equals and hashCode, and to simply use a HashSet instead of a TreeSet. In that case you can change the contents of your nodes without consequences to the proper functioning of the set of neighbours.
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I'm not so sure about what's the best approach to model this problem in the object oriented world.
Suppose I have to represent a graph and its nodes in Java and suppose I want to traverse the graph using a depth first search (DFS).
What is the best approach in Software engineering among these two:
Create a class Node, a class Graph and a class GraphsFunctions where the GraphsFunctions class contains the DFS method which takes the Graph and a Node as parameters. Something like this: public void DFS(Graph g, Node n) and the call new GraphsFunctions().DFS(new Graph(), new Node())
Create a class Node and a class Graph where the class Graph contains the method DFS which only takes a Node as a parameter. Something like this: public void DFS(Node n) and the call new Graph().DFS(new Node())
I would rather use the first of the two options but I can't say why it's the best for me. Can you tell me what makes the best choice, actually the best?
This really depends on mostly two things:
How much encapsulation you want in your project. More encapsulation is usually better, but it can come at the price of over engineering the problem, which can cause a lot more work which you want to avoid for small projects which wont grow over time.
Your own personal style. Each developer will develop his or her own style over time which will be clearly distinguishable from other developers. The most important thing is consistency throughout a project
Assuming you want (only) good encapsulation I would personally do it like the following..
Create an interface GraphSearch and define your search(Graph g, Node n) method in there. Now have a class called DFSearch and possibliy BFSearch.
If - at some point - a method wants to perform searches on a Graph you can specify the search algorithm which should be used.
If you want great encapsulation, I would recommend using the iterator pattern as DFS and BFS are basically just iteration orders.
Start by creating an Interface GraphIterator instead. It simply extends Iterator<Node> which means it can iterate over Nodes in some order.
public interface GraphIterator extends Iterator<Node> {
}
Create as many implementations of this algorithm as you like. An empty construct for DFS looks like this:
public class DFSIterator implements GraphIterator {
private Graph g;
public DFSIterator(Graph g) {
this.g = g;
}
#Override
public boolean hasNext() {
// todo: implement
return false;
}
#Override
public Node next() {
// todo: implement
return null;
}
}
Next you can still create your Interface GraphSearch..
public interface GraphSearch {
Node search(Graph g, Node n);
}
And one simple implementation would be IterationSearch which can only perform searches with a given Iterator.
public class IterationSearch {
public Node search(Graph g, Node n, GraphIterator iter){
Node current = null;
while (iter.hasNext()){
current = iter.next();
if (current.equals(n)){
return n;
}
}
return null;
}
}
It just iterates over the Iterator and compares each element to the searched node. Next create your class DFSearch which is the first actual GraphSearch...
public class DFSearch extends IterationSearch implements GraphSearch{
#Override
public Node search(Graph g, Node n) {
return search(g, n, new DFSIterator(g));
}
}
Now your Graph can extend Itarable<Node> and return one of your iterators as the default iterator. This way you could nicely iterate over your Graph.
public class Graph implements Iterable<Node> {
#Override
public Iterator<Node> iterator() {
return new DFSIterator(this);
}
}
and use it like the following:
Graph g = createGraph();
for (Node n : g) {
// do things...
}
The great thing about this solution is that you can make the Iterators inner classes of your Graph, which allows them to access private members. This often improves performance significantly as you don't have to treat the Graph as a black box.
Best in terms of fast-to-code, easy-to-modify, or some other criterion? Unless you come up with a very precise definition of "what is good", do not expect to find "best".
My first answer would be to use a graph library for Java. Most come with DFS already implemented:
Already implemented and debugged.
Good documentation.
No company will generally want me to reinvent the wheel.
However, since your question is probably part of a do-it-yourself course, I would instead recommend:
Node class, which can provide access to neighbors, and has equals and hashcode methods.
Graphs class, which can build graphs and display them. Includes a Dfs(Graph g, Node start, DfsVisitor visitor) method. This would be a utility class with all-static methods, in the same vein as JDK's Collections or Files.
Graph class, with a list of nodes (and possibly other things, such as the possibility of returning an iterator to a list of edges).
DfsVisitor, an interface for visitors:
Main class, which builds a graph and calls your DFS on the graph with the useful payload in the visitor.
Because DFS by itself is useless - it is just a node-visiting strategy. Its value is in what it does before and after it visits a node. Unless you allow this to be customized, placing a DFS algorithm into GraphsFunctions or Graphs makes no big difference: it is not likely to be reused outside of whatever payload you have built into it.
public interface DfsVisitor() {
void started(Graph g, Node node); // when first encountered
void finished(Graph g, Node node); // when all children processed
}
I have to create an array of linked lists for a class in order to store a graph (adjacency list). We have to use Java. I can create the array and instantiate each linked list, but when I go to add the first elements to each one, every linked list gets changed, not just the one at the index of the array.
Node [] adjList;
for(i=0;i<adjList.length;i++)
adjList[i] = new Node(0,0,null);
this instantiates each new linked list [Node is my own class, with constructor Node(int head, int data, Node next) and extends LinkedList]
then i go to add the first values to each node:
for(i=0;i<adjList.length;i++)
adjList[i].setHead(i+1); // numbers 1 to end are the graph vertices
or
for(i=0;i<adjList.length;i++)
adjList[i].add(new Node(i+1,0,null);
I use print statements to debug the code
at the end of these loop I print off each Linked List, but for each one, the values come out to be the final one
ie. if adjList.length = 2, it would print out
[3,0,null] // adjList[0]
[3,0,null] // adjList[1]
[3,0,null] // adjList[2]
edit: here is the Node class
import java.util.LinkedList;
public class Node extends LinkedList{
private static int head;
private static int data;
private static Node next;
public Node(int h,int d,Node n) {
head = h;
data = d;
next = n;
}
public int getHead(){ // getNext() and getData() are the same
return head;
}
public void setHead(int h){ // setNext() and setData() are basically the same
head = h;
}
}
You have probably declared something within Node as static, so every instance ends up with the same shared value, rather than having its own value. However, this is just a guess - please post the code of Node so we can see what the problem really is...
when I go to add the first elements to each one, every linked list gets changed, not just the one at the index of the array
Although your code snippet doesn't show it, almost definitely you have an aliasing problem. The aliasing problem, which tends to bite beginners in almost all object-oriented languages, is the problem of referring to the same object with two different names i.e. two different variables pointing at the same object.
Now you may be wondering: what about array indices? The problem is with changing a variable at one array index and getting a change across all array indices, not a bunch of named variables. But, as Eric Lippert explains (for C#, which is quite similar to Java), an array really is a bunch of variables that you can refer to with an indexer expression rather than having to define a bunch of individual names. In a sense, int[] foo = new int[3] is like declaring foo0, foo1, and foo2, and indexing into foo just tells the compiler to pick the appropriate variable out of foo0, foo1, and foo2.
You may also be wondering how data could be shared between multiple Node instances, if your array indeed has multiple nodes in it. There are a few ways, and knowing which is pretty much impossible without the code for the Node class. As #DNA points out, there could be static data in the Node class, which is automatically shared across all instances. A Node object may also have a reference to underlying data. If you pass the same reference into all the Node constructors, they are all aliasing the same object in this way as well.