I'm writing a Java program that searches for and outputs cycles in a graph. I am using an adjacency list for storing my graph, with the lists stored as LinkedLists. My program takes an input formatted with the first line as the number of nodes in the graph and each subsequent line 2 nodes that form an edge e.g.:
3
1 2
2 3
3 1
My problem is that when the inputs get very large (the large graph I am using has 10k nodes and I don't know how many edges, the file is 23mb of just edges) I am getting a java.lang.StackOverflowError, but I don't get any errors with small inputs. I'm wondering if it would be better to use another data structure to form my adjacency lists or if there is some method I could use to avoid this error, as I'd rather not just have to change a setting on my local installation of Java (because I have to be sure this will run on other computers that I can't control the settings on as much). Below is my code, the Vertex class and then my main class. Thanks for any help you can give!
Vertex.java:
package algorithms311;
import java.util.*;
public class Vertex implements Comparable {
public int id;
public LinkedList adjVert = new LinkedList();
public String color = "white";
public int dTime;
public int fTime;
public int prev;
public Vertex(int idnum) {
id = idnum;
}
public int getId() {
return id;
}
public int compareTo(Object obj) {
Vertex vert = (Vertex) obj;
return id-vert.getId();
}
#Override public String toString(){
return "Vertex # " + id;
}
public void setColor(String newColor) {
color = newColor;
}
public String getColor() {
return color;
}
public void setDTime(int d) {
dTime = d;
}
public void setFTime(int f) {
fTime = f;
}
public int getDTime() {
return dTime;
}
public int getFTime() {
return fTime;
}
public void setPrev(int v) {
prev = v;
}
public int getPrev() {
return prev;
}
public LinkedList getAdjList() {
return adjVert;
}
public void addAdj(int a) { //adds a vertex id to this vertex's adj list
adjVert.add(a);
}
}
CS311.java:
package algorithms311;
import java.util.*;
import java.io.*;
public class CS311 {
public static final String GRAPH= "largegraph1";
public static int time = 0;
public static LinkedList[] DFS(Vertex[] v) {
LinkedList[] l = new LinkedList[2];
l[0] = new LinkedList();
l[1] = new LinkedList(); //initialize the array with blank lists, otherwise we get a nullpointerexception
for(int i = 0; i < v.length; i++) {
v[i].setColor("white");
v[i].setPrev(-1);
}
time = 0;
for(int i = 0; i < v.length; i++) {
if(v[i].getColor().equals("white")) {
l = DFSVisit(v, i, l);
}
}
return l;
}
public static LinkedList[] DFSVisit(Vertex[] v, int i, LinkedList[] l) { //params are a vertex of nodes and the node id you want to DFS from
LinkedList[] VOandBE = new LinkedList[2]; //two lists: visit orders and back edges
VOandBE[0] = l[0]; // l[0] is visit Order, a linked list of ints
VOandBE[1] = l[1]; // l[1] is back Edges, a linked list of arrays[2] of ints
VOandBE[0].add(v[i].getId());
v[i].setColor("gray"); //color[vertex i] <- GRAY
time++; //time <- time+1
v[i].setDTime(time); //d[vertex i] <- time
LinkedList adjList = v[i].getAdjList(); // adjList for the current vertex
for(int j = 0; j < adjList.size(); j++) { //for each v in adj[vertex i]
if(v[(Integer)adjList.get(j)].getColor().equals("gray") && v[i].getPrev() != v[(Integer)adjList.get(j)].getId()) { // if color[v] = gray and Predecessor[u] != v do
int[] edge = new int[2]; //pair of vertices
edge[0] = i; //from u
edge[1] = (Integer)adjList.get(j); //to v
VOandBE[1].add(edge);
}
if(v[(Integer)adjList.get(j)].getColor().equals("white")) { //do if color[v] = WHITE
v[(Integer)adjList.get(j)].setPrev(i); //then "pi"[v] <- vertex i
DFSVisit(v, (Integer)adjList.get(j), VOandBE); //DFS-Visit(v)
}
}
VOandBE[0].add(v[i].getId());
v[i].setColor("black");
time++;
v[i].setFTime(time);
return VOandBE;
}
public static void main(String[] args) {
try {
// --Read First Line of Input File
// --Find Number of Vertices
FileReader file1 = new FileReader("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + GRAPH);
BufferedReader bReaderNumEdges = new BufferedReader(file1);
String numVertS = bReaderNumEdges.readLine();
int numVert = Integer.parseInt(numVertS);
System.out.println(numVert + " vertices");
// --Make Vertices
Vertex vertex[] = new Vertex[numVert];
for(int k = 0; k <= numVert - 1; k++) {
vertex[k] = new Vertex(k);
}
// --Adj Lists
FileReader file2 = new FileReader("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + GRAPH);
BufferedReader bReaderEdges = new BufferedReader(file2);
bReaderEdges.readLine(); //skip first line, that's how many vertices there are
String edge;
while((edge = bReaderEdges.readLine()) != null) {
StringTokenizer ST = new StringTokenizer(edge);
int vArr[] = new int[2];
for(int j = 0; ST.hasMoreTokens(); j++) {
vArr[j] = Integer.parseInt(ST.nextToken());
}
vertex[vArr[0]-1].addAdj(vArr[1]-1);
vertex[vArr[1]-1].addAdj(vArr[0]-1);
}
for(int i = 0; i < vertex.length; i++) {
System.out.println(vertex[i] + ", adj nodes: " + vertex[i].getAdjList());
}
LinkedList[] l = new LinkedList[2];
l = DFS(vertex);
System.out.println("");
System.out.println("Visited Nodes: " + l[0]);
System.out.println("");
System.out.print("Back Edges: ");
for(int i = 0; i < l[1].size(); i++) {
int[] q = (int[])(l[1].get(i));
System.out.println("[" + q[0] + "," + q[1] + "] ");
}
for(int i = 0; i < l[1].size(); i++) { //iterate through the list of back edges
int[] q = (int[])(l[1].get(i)); // q = pair of vertices that make up a back edge
int u = q[0]; // edge (u,v)
int v = q[1];
LinkedList cycle = new LinkedList();
if(l[0].indexOf(u) < l[0].indexOf(v)) { //check if u is before v
for(int z = l[0].indexOf(u); z <= l[0].indexOf(v); z++) { //if it is, look for u first; from u to v
cycle.add(l[0].get(z));
}
}
else if(l[0].indexOf(v) < l[0].indexOf(u)) {
for(int z = l[0].indexOf(v); z <= l[0].indexOf(u); z++) { //if it is, look for u first; from u to v
cycle.add(l[0].get(z));
}
}
System.out.println("");
System.out.println("Cycle detected! : " + cycle);
if((cycle.size() & 1) != 0) {
System.out.println("Cycle is odd, graph is not 2-colorable!");
}
else {
System.out.println("Cycle is even, we're okay!");
}
}
}
catch (IOException e) {
System.out.println("AHHHH");
e.printStackTrace();
}
}
}
The issue is most likely the recursive calls in DFSVisit. If you don't want to go with the 'easy' answer of increasing Java's stack size when you call the JVM, you may want to consider rewriting DFSVisit to use an iterative algorithm instead of recursive. While Depth First Search is more easily defined in a recursive manner, there are iterative approaches to the algorithm that can be used.
For example: this blog post
The stack is a region in memory that is used for storing execution context and passing parameters. Every time your code invokes a method, a little bit of stack is used, and the stack pointer is increased to point to the next available location. When the method returns, the stack pointer is decreased and the portion of the stack is freed up.
If an application uses recursion heavily, the stack quickly becomes a bottleneck, because if there is no limit to the recursion depth, there is no limit to the amount of stack needed. So you have two options: increase the Java stack (-Xss JVM parameter, and this will only help until you hit the new limit) or change your algorithm so that the recursion depth is not as deep.
I am not sure if you were looking for a generic answer, but from a brief glance at your code it appears that your problem is recursion.
If you're sure your algorithm is correct and the depth of recursive calls you're making isn't accidental, then solutions without changing your algorithm are:
add to the JVM command line e.g. -Xss128m to set a 128 MB stack size (not a good solution in multi-threaded programs as it sets the default stack size for every thread not just the particular thread running your task);
run your task in its own thread, which you can initialise with a stack size specific to just that thread (and set the stack size within the program itself)-- see my example in the discussion of fixing StackOverflowError, but essentially the stack size is a parameter to the Thread() constructor;
don't use recursive calls at all-- instead, mimic the recursive calls using an explicit Stack or Queue object (this arguably gives you a bit more control).
Related
Currently working on my home assignment, a simulation code to make stack with a support of dynamic array.
The method pop() is not working to it is function, I wrote some of the code but I need a completion. My stack should be simulates like this, you insert a numbers to be called top then remove that number(top).
import java.util.NoSuchElementException;
public class MyStack implements IntStack {
int[] heltal;
public MyStack() {
heltal = new int[0];
}
public void push(int tal) {
int[] temp = new int[heltal.length + 1];
for (int x = 0; x < heltal.length; x++) {
temp[x] = heltal[x] + tal;
}
heltal = temp;
for (int i = 0; i < heltal.length; i++) {
heltal[i] = tal;
}
}
#Override
public int pop() {
if (Isempty()) {
throw new NoSuchElementException("The stack is empty, there is nothing to pop");
} else {
int[] temp = new int[heltal.length - 1];
for (int x = 0; x < heltal.length - 1; x++) {
temp[x] = heltal[x];
}
int etttal = heltal[0];
heltal = temp;
return etttal;
}
}
#Override
public int peek() {
if (Isempty()) {
throw new NoSuchElementException("The stack is empty");
} else {
return heltal[0];
}
}
public boolean Isempty() {
return heltal.length == 0;
}
}
You appear to be making this more difficult than required. For a stack implementation, push, pop, peek, and others are nothing more than index manipulators that return or store a value. The stack can be backed by an array or a list. And pushing et al are abstract terms. So when you push a value, you do not need to copy everything down by one. Just add it to the end of your data structure.
pop - check the index and if valid, return the element at the current index, update the index.
push - store the value at the next location. Probably index + 1 but it depends on how you implement it.
peek - return the top value (at the index) but don't update the index.
If you are using an array, you need to add method(s) to increase it's capacity.
For more information, check out Stack
Here is a simple push method, backed by an array called stack and an index field. It presumes ints are being used.
public void push(int v) {
if (index == stack.length-1) {
// no more room, increase array size
// while retaining current values.
}
stack[++index] = v;
}
I'm doing something that produces the right result. However, it is wrong from a design POV.
The point of the program is to list the result of all the powers of a number up to and including the user-defined limit.
I have a constructor which accepts the base and the exponent from the Scanner. Then a method, which utilises a for loop to calculate the power for each exponent.
Now, the problem is that I'm printing the result from each loop iteration directly from this method. This beats the point of private variables and it being void in the 1st place.
Therefore, I want to define a getter method which returns the result of each power to the output. I used to set them just fine for if/switch statements, but I don't know how to do the same for loops. If I assign the result to a variable within the loop and return that variable from the getter then it will return only the output from the final iteration.
Private implementation
package Chapter6Review;
public class Powers {
private int target;
private int power;
public Powers(int target, int power) {
this.target = target;
this.power = power;
}
public void calculatePower() {
for (int i = 0; i <= power; i++) {
System.out.println((int) Math.pow(target, i));
}
}
/*
public int getPower() {
return
}
*/
}
User interface
package Chapter6Review;
import java.util.Scanner;
public class PowersTester {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.print("Enter your base: ");
int target = in.nextInt();
System.out.print("Enter your exponent: ");
int power = in.nextInt();
Powers tester = new Powers(target, power);
tester.calculatePower();
}
}
You can simply use a List ;
public List<Integer> calculatePower() {
int p;
List<Integer> result = new ArrayList<Integer>();
for (int i = 0; i <= power; i++) {
p = (int) Math.pow(target, i);
result.add(p);
}
return result;
}
Then in you main method, you can iterate the list to print the powers like that :
List<Integer> result = new ArrayList<Integer>();
Powers tester = new Powers(target, power);
result = tester.calculatePower();
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
You could store each of the results in a List:
List<Power> list = new ArrayList<>();
and when you call it add it as well
list.add(new Powers(target, power));
At the end you can iterate over the list like this:
for (Power power : list){
// your code
}
You might consider using streams as well
public List<Integer> calculatePower() {
return IntStream
.rangeClosed(0, power). // iterate from 0 till power inclusive
.mapToObj(i -> (int) Math.pow(target,i))
.collect(Collectors.toList()); // get result as list
}
Thanks for all the answers. Using a list seems to be a good choice.
Since I haven't covered lists yet, I resorted to this solution for now. But I don't like having code that can affect the solution in the main. Ideally, the loop should go in the private implementation.
Main
Powers tester = new Powers(target, power);
for (int i = 0; i <= power; i++) {
tester.calculatePower(i);
System.out.println(tester.getPower());
}
Private implementation
public void calculatePower(int iPower) {
result = (int) Math.pow(target, iPower);
}
public int getPower() {
return result;
}
I have a DFS visit recursive method that sometimes throws a StackOverflowError. Since the size of the graph is large (around 20000 vertices), recursive calls are many, and so I tried to run with -Xss10M and everything works.
I'd just like to understand why adding at the beginning of the method a System.out.println, even without -Xss10M, the method doesn't throw any StackOverflowError. How is it possible?
This is the DFS visit method:
private int dfsVisit(Vertex<T> v, int time){
// System.out.println("Hello");
Vertex<T> n;
time++;
v.d = time;
v.color = Vertex.Color.GRAY;
for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
n = a.getKey();
if(n.color == Vertex.Color.WHITE){
n.previous = v;
time = dfsVisit(n, time);
}
}
v.color = Vertex.Color.BLACK;
time++;
v.f = time;
return time;
}
This is the complete code
import java.io.*;
import java.util.*;
class Graph<T> {
private final Map<T, Vertex<T>> graph;
public static class Edge<T>{
public final T v1, v2;
public final float dist;
public Edge(T v1, T v2, float dist) {
this.v1 = v1;
this.v2 = v2;
this.dist = dist;
}
}
public static class Vertex<T> implements Comparable<Vertex>{ // SPOSTARE VAR IST NEL COSTRUTTORE
public enum Color {WHITE, GRAY, BLACK, UNKNOWN};
public final T name;
public float dist;
public Vertex<T> previous;
public final Map<Vertex<T>, Float> neighbours;
public Color color;
public int d, f;
public Vertex(T name) {
this.name = name;
dist = Float.MAX_VALUE;
previous = null;
neighbours = new HashMap<Vertex<T>, Float>(); // adjacency list
color = Color.UNKNOWN;
d = 0;
f = 0;
}
private void printPath() {
if (this == this.previous) {
System.out.print(this.name);
} else if (this.previous == null) {
System.out.print(this.name + " unreached");
} else {
this.previous.printPath();
System.out.print(" -> " + this.name + "(" + this.dist + ")");
}
}
public int compareTo(Vertex other){
if(this.dist == other.dist)
return 0;
else if(this.dist > other.dist)
return 1;
else
return -1;
}
}
// Builds a graph from an array of edges
public Graph(ArrayList<Graph.Edge> edges) {
graph = new HashMap<>(edges.size());
// add vertices
for (Edge<T> e : edges) {
if (!graph.containsKey(e.v1)) graph.put(e.v1, new Vertex<>(e.v1));
if (!graph.containsKey(e.v2)) graph.put(e.v2, new Vertex<>(e.v2));
}
// create adjacency list
for (Edge<T> e : edges) {
graph.get(e.v1).neighbours.put(graph.get(e.v2), e.dist);
graph.get(e.v2).neighbours.put(graph.get(e.v1), e.dist);
}
}
public void dijkstra(T startName) {
if (!graph.containsKey(startName)) {
System.err.println("Graph doesn't contain start vertex " + startName);
return;
}
final Vertex<T> source = graph.get(startName);
NavigableSet<Vertex<T>> q = new TreeSet<>(); // priority queue
// set-up vertices
for (Vertex<T> v : graph.values()) {
v.previous = v == source ? source : null;
v.dist = v == source ? 0 : Float.MAX_VALUE;
q.add(v);
}
dijkstra(q);
}
private void dijkstra(final NavigableSet<Vertex<T>> q) {
Vertex<T> u, v;
while (!q.isEmpty()) {
u = q.pollFirst();
if (u.dist == Float.MAX_VALUE) break; //???????????
for (Map.Entry<Vertex<T>, Float> a : u.neighbours.entrySet()) {
v = a.getKey();
final float alternateDist = u.dist + a.getValue();
if (alternateDist < v.dist) {
q.remove(v);
v.dist = alternateDist;
v.previous = u;
q.add(v);
}
}
}
}
public void printPath(T endName) {
if (!graph.containsKey(endName)) {
System.err.println("Graph doesn't contain end vertex " + "\"" + endName + "\"" );
return;
}
graph.get(endName).printPath();
System.out.println();
}
public void printAllPaths() {
for (Vertex<T> v : graph.values()) {
v.printPath();
System.out.println();
}
}
public Vertex<T> getVertex(T key){
if(graph.containsKey(key))
return graph.get(key);
return null;
}
public void printAdjacencyList(){
System.out.println("Adjacency list:");
for(Vertex<T> v : graph.values()){
System.out.print(v.name + ":\t");
for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
System.out.print(a.getKey().name + "(" + a.getValue() + ") | ");
}
System.out.println();
}
}
/*
P.S. I know that if only used to calculate the connected components of the graph, dfs visit
could be written differently but I preferred to write it in a more general way, so that it
can be reused if necessary.
*/
private int dfsVisit(Vertex<T> v, int time){
// System.out.println("ciao");
Vertex<T> n;
time++;
v.d = time;
v.color = Vertex.Color.GRAY;
for (Map.Entry<Vertex<T>, Float> a : v.neighbours.entrySet()){
n = a.getKey();
if(n.color == Vertex.Color.WHITE){
n.previous = v;
time = dfsVisit(n, time);
}
}
v.color = Vertex.Color.BLACK;
time++;
v.f = time;
return time;
}
/*
Print the size of the connected components of the graph
*/
public void connectedComponents(){
for(Vertex<T> v : graph.values()){
v.color = Vertex.Color.WHITE;
v.previous = null;
}
for(Vertex<T> v : graph.values()){
if(v.color == Vertex.Color.WHITE)
System.out.println(dfsVisit(v, 0)/2);
}
}
}
here's the test class
import java.io.*;
import java.util.*;
public class Dijkstra {
private static ArrayList<Graph.Edge> a = new ArrayList<Graph.Edge>();
private static final String START = "torino";
private static final String END = "catania";
public static void main(String[] args) {
String fileName = "italian_dist_graph.txt";
try{
Scanner inputStream = new Scanner(new File(fileName));
String record;
while(inputStream.hasNextLine()){
record = inputStream.nextLine();
String[] array = record.split(",");
String from = array[0];
String to = array[1];
float dist = Float.parseFloat(array[2]);
a.add(new Graph.Edge(from, to, dist));
}
inputStream.close();
} catch(FileNotFoundException e){
System.out.println("Impossibile trovare il file "+fileName);
}
Graph<String> g = new Graph<String>(a);
g.dijkstra(START);
g.printPath(END);
//System.out.printf("%f\n", g.getVertex(END).dist/1000.0f);
g.connectedComponents();
}
}
N.B. try to comment g.dijkstra(START) and g.printPath(END); everything seems to work.
Here's the link to the data set
https://drive.google.com/open?id=0B7XZY8cd0L_fZVl1aERlRmhQN0k
Some general recommendations:
Your code mixes up attributes of vertices, that are related to a single run of dfs and such that are direct attributes of the vertices. Bad bad bad style. This is quite likely to break any more complex algorithm, can produce unexpected behavior and would require clearing the states after each run, to ensure stability of the code. Instead keep states that are related to a single run of a algorithm only visible to that function. E.g. store the states inside a Map, use the decorator-pattern to create a datastructure that provides additional attributes and that has method-local scope, etc.. As an example: running your code twice on the same graph (same Object) with the same input without clearing all states will lead to a wrong result (1).
In addition: creating an iterative version of DFS isn't exactly hard, so you should give it a try, especially since your graph appears to be pretty large.
As for why your code works (or doesn't) the way it does:
This is hard to tell, since it depends upon quite a lot of factors. You didn't provide full code, so I can't rerun any tests, or verify that everything behaves the way it should. The most likely answers:
Vertex uses the default hash-code provided by Object. This leads to random ordering of the entries in the map of neighbours, thus the order in which specific paths are traversed is random in each run and most likely different. Thus you're traversing the graph using random paths, that quite likely (especially due to the size of your graph) differ for each run. The reason isn't the System.out.println, but the mere fact, that your code generates a different structure (from a ordering-POV, not mathematical), each time it runs plus the coincident, that for some pretty weird reason each build of the graph, that doesn't reach the necessary recursion-depth for a StackOverflow, and the code compiled with System.out.println appeared together.
The Java compiler, or JIT modifies the behavior of the code in a weird way. Modern compilers have the tendency to produce quite weird code in their attempts to optimize everything they can get hold off.
im trying to create a B+ tree from a previous B tree implementation I create, but Im really lost here... the only difference from B to B+ im trying to implement, is storing the keys on the leaves instead of removing them.
Example:
Final B Tree
3 6 false
1 2 true
4 5 true
7 8 9 10 true
Final B+ Tree
3 6 false
1 2 true
3 4 5 true
6 7 8 9 10 true
This is what I have for a B Tree (I really didnt want to post the whole code, but explaining all of it will be harder and confusing). I would appreciate at least some ideas...
MAIN
public class BTreeTest{
public static void main(String[] args) {
Random generator = new Random();
BTree T = new BTree(3);
final int INSERTS = 50; // how many elements are inserted
final int VALUE_LIMIT = 1000; // generated integers up to VALUE_LIMIT
int[] values = new int[INSERTS]; // array can be used to print insert order
// or to test other methods
for (int i=0;i<INSERTS;i++){
int val = generator.nextInt(VALUE_LIMIT);
values[i] = val;
T.insert(val);
}
T.printNodes();
}}
B tree NODE
public class BTreeNode{
public int[] key;
public BTreeNode[] c;
boolean isLeaf;
public int n;
private int T; //Each node has at least T-1 and at most 2T-1 keys
public BTreeNode(int t){
T = t;
isLeaf = true;
key = new int[2*T-1];
c = new BTreeNode[2*T];
n=0;
}
public boolean isFull(){
return n==(2*T-1);
}
public void insert(int newKey){
// Insert new key to current node
// We make sure that the current node is not full by checking and
// splitting if necessary before descending to node
//System.out.println("inserting " + newKey); // Debugging code
int i=n-1;
if (isLeaf){
while ((i>=0)&& (newKey<key[i])) {
key[i+1] = key[i];
i--;
}
n++;
key[i+1]=newKey;
}
else{
while ((i>=0)&& (newKey<key[i])) {
i--;
}
int insertChild = i+1; // Subtree where new key must be inserted
if (c[insertChild].isFull()){
// The root of the subtree where new key will be inserted has to be split
// We promote the mediand of that root to the current node and
// update keys and references accordingly
//System.out.println("This is the full node we're going to break ");
// Debugging code
//c[insertChild].printNodes();
//System.out.println("going to promote " + c[insertChild].key[T-1]);
n++;
c[n]=c[n-1];
for(int j = n-1;j>insertChild;j--){
c[j] =c[j-1];
key[j] = key[j-1];
}
key[insertChild]= c[insertChild].key[T-1];
c[insertChild].n = T-1;
BTreeNode newNode = new BTreeNode(T);
for(int k=0;k<T-1;k++){
newNode.c[k] = c[insertChild].c[k+T];
newNode.key[k] = c[insertChild].key[k+T];
}
newNode.c[T-1] = c[insertChild].c[2*T-1];
newNode.n=T-1;
newNode.isLeaf = c[insertChild].isLeaf;
c[insertChild+1]=newNode;
//System.out.println("This is the left side ");
//c[insertChild].printNodes();
//System.out.println("This is the right side ");
//c[insertChild+1].printNodes();
//c[insertChild+1].printNodes();
if (newKey <key[insertChild]){
c[insertChild].insert(newKey); }
else{
c[insertChild+1].insert(newKey); }
}
else
c[insertChild].insert(newKey);
}
}
public void print(){
//Prints all keys in the tree in ascending order
if (isLeaf){
for(int i =0; i<n;i++)
System.out.print(key[i]+" ");
System.out.println();
}
else{
for(int i =0; i<n;i++){
c[i].print();
System.out.print(key[i]+" ");
}
c[n].print();
}
}
public void printNodes(){
//Prints all keys in the tree, node by node, using preorder
//It also prints the indicator of whether a node is a leaf
//Used mostly for debugging purposes
printNode();
if (!isLeaf){
for(int i =0; i<=n;i++){
c[i].printNodes();
}
}
}
public void printNode(){
//Prints all keys in node
for(int i =0; i<n;i++)
System.out.print(key[i]+" ");
System.out.println(isLeaf);
}
}
B Tree
public class BTree{
private BTreeNode root;
private int T; //2T is the maximum number of childen a node can have
private int height;
public BTree(int t){
root = new BTreeNode(t);
T = t;
height = 0;
}
public void printHeight(){
System.out.println("Tree height is "+height);
}
public void insert(int newKey){
if (root.isFull()){//Split root;
split();
height++;
}
root.insert(newKey);
}
public void print(){
// Wrapper for node print method
root.print();
}
public void printNodes(){
// Wrapper for node print method
root.printNodes();
}
public void split(){
// Splits the root into three nodes.
// The median element becomes the only element in the root
// The left subtree contains the elements that are less than the median
// The right subtree contains the elements that are larger than the median
// The height of the tree is increased by one
//System.out.println("Before splitting root");
//root.printNodes(); // Code used for debugging
BTreeNode leftChild = new BTreeNode(T);
BTreeNode rightChild = new BTreeNode(T);
leftChild.isLeaf = root.isLeaf;
rightChild.isLeaf = root.isLeaf;
leftChild.n = T-1;
rightChild.n = T-1;
int median = T-1;
for (int i = 0;i<T-1;i++){
leftChild.c[i] = root.c[i];
leftChild.key[i] = root.key[i];
}
leftChild.c[median]= root.c[median];
for (int i = median+1;i<root.n;i++){
rightChild.c[i-median-1] = root.c[i];
rightChild.key[i-median-1] = root.key[i];
}
rightChild.c[median]=root.c[root.n];
root.key[0]=root.key[median];
root.n = 1;
root.c[0]=leftChild;
root.c[1]=rightChild;
root.isLeaf = false;
//System.out.println("After splitting root");
//root.printNodes();
}}
I'm back with another similar question. I am currently working on a Java program that will check if a graph is 2-colorable, i.e. if it contains no odd cycles (cycles of odd number length). The entire algorithm is supposed to run in O(V+E) time (V being all vertices and E being all edges in the graph). My current algorithm does a Depth First Search, recording all vertices in the path it takes, then looks for a back edge, and then records between which vertices the edge is between. Next it traces a path from one end of the back edge until it hits the other vertex on the other end of the edge, thus retracing the cycle that the back edge completes.
I was under the impression that this kind of traversing could be done in O(V+E) time for all cycles that exist in my graph, but I must be missing something, because my algorithm is running for a ridiculously long time for very large graphs (10k nodes, no idea how many edges).
Is my algorithm completely wrong? And if so, can anyone point me in the right direction for a better way to record these cycles or possibly tell if they have odd numbers of vertices? Thanks for any and all help you guys can give. Code is below if you need it.
Addition: Sorry I forgot, if the graph is not 2-colorable, I need to provide an odd cycle that proves that it is not.
package algorithms311;
import java.util.*;
import java.io.*;
public class CS311 {
public static LinkedList[] DFSIter(Vertex[] v) {
LinkedList[] VOandBE = new LinkedList[2];
VOandBE[0] = new LinkedList();
VOandBE[1] = new LinkedList();
Stack stack = new Stack();
stack.push(v[0]);
v[0].setColor("gray");
while(!stack.empty()) {
Vertex u = (Vertex) stack.peek();
LinkedList adjList = u.getAdjList();
VOandBE[0].add(u.getId());
boolean allVisited = true;
for(int i = 0; i < adjList.size(); i++) {
if(v[(Integer)adjList.get(i)].getColor().equals("white")) {
allVisited = false;
break;
}
else if(v[(Integer)adjList.get(i)].getColor().equals("gray") && u.getPrev() != (Integer)adjList.get(i)) {
int[] edge = new int[2]; //pair of vertices
edge[0] = u.getId(); //from u
edge[1] = (Integer)adjList.get(i); //to v
VOandBE[1].add(edge);
}
}
if(allVisited) {
u.setColor("black");
stack.pop();
}
else {
for(int i = 0; i < adjList.size(); i++) {
if(v[(Integer)adjList.get(i)].getColor().equals("white")) {
stack.push(v[(Integer)adjList.get(i)]);
v[(Integer)adjList.get(i)].setColor("gray");
v[(Integer)adjList.get(i)].setPrev(u.getId());
break;
}
}
}
}
return VOandBE;
}
public static void checkForTwoColor(String g) { //input is a graph formatted as assigned
String graph = g;
try {
// --Read First Line of Input File
// --Find Number of Vertices
FileReader file1 = new FileReader("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + graph);
BufferedReader bReaderNumEdges = new BufferedReader(file1);
String numVertS = bReaderNumEdges.readLine();
int numVert = Integer.parseInt(numVertS);
System.out.println(numVert + " vertices");
// --Make Vertices
Vertex vertex[] = new Vertex[numVert];
for(int k = 0; k <= numVert - 1; k++) {
vertex[k] = new Vertex(k);
}
// --Adj Lists
FileReader file2 = new FileReader("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + graph);
BufferedReader bReaderEdges = new BufferedReader(file2);
bReaderEdges.readLine(); //skip first line, that's how many vertices there are
String edge;
while((edge = bReaderEdges.readLine()) != null) {
StringTokenizer ST = new StringTokenizer(edge);
int vArr[] = new int[2];
for(int j = 0; ST.hasMoreTokens(); j++) {
vArr[j] = Integer.parseInt(ST.nextToken());
}
vertex[vArr[0]-1].addAdj(vArr[1]-1);
vertex[vArr[1]-1].addAdj(vArr[0]-1);
}
LinkedList[] l = new LinkedList[2];
l = DFSIter(vertex);//DFS(vertex);
System.out.println(l[0]);
for(int i = 0; i < l[1].size(); i++) {
int[] j = (int[])l[1].get(i);
System.out.print(" [" + j[0] + ", " + j[1] + "] ");
}
LinkedList oddCycle = new LinkedList();
boolean is2Colorable = true;
//System.out.println("iterate through list of back edges");
for(int i = 0; i < l[1].size(); i++) { //iterate through the list of back edges
//System.out.println(i);
int[] q = (int[])(l[1].get(i)); // q = pair of vertices that make up a back edge
int u = q[0]; // edge (u,v)
int v = q[1];
LinkedList cycle = new LinkedList();
if(l[0].indexOf(u) < l[0].indexOf(v)) { //check if u is before v
for(int z = l[0].indexOf(u); z <= l[0].indexOf(v); z++) { //if it is, look for u first; from u to v
cycle.add(l[0].get(z));
}
}
else if(l[0].indexOf(v) < l[0].indexOf(u)) {
for(int z = l[0].indexOf(v); z <= l[0].indexOf(u); z++) { //if it is, look for u first; from u to v
cycle.add(l[0].get(z));
}
}
if((cycle.size() & 1) != 0) { //if it has an odd cycle, print out the cyclic nodes or write them to a file
is2Colorable = false;
oddCycle = cycle;
break;
}
}
if(!is2Colorable) {
System.out.println("Graph is not 2-colorable, odd cycle exists");
if(oddCycle.size() <= 50) {
System.out.println(oddCycle);
}
else {
try {
BufferedWriter outFile = new BufferedWriter(new FileWriter("W:\\Documents\\NetBeansProjects\\algorithms311\\src\\algorithms311\\" + graph + "OddCycle.txt"));
String cyc = oddCycle.toString();
outFile.write(cyc);
outFile.close();
}
catch (IOException e) {
System.out.println("Could not write file");
}
}
}
}
catch (IOException e) {
System.out.println("Could not open file");
}
System.out.println("Done!");
}
public static void main(String[] args) {
//checkForTwoColor("smallgraph1");
//checkForTwoColor("smallgraph2");
//checkForTwoColor("smallgraph3");
//checkForTwoColor("smallgraph4");
checkForTwoColor("smallgraph5");
//checkForTwoColor("largegraph1");
}
}
Vertex class
package algorithms311;
import java.util.*;
public class Vertex implements Comparable {
public int id;
public LinkedList adjVert = new LinkedList();
public String color = "white";
public int dTime;
public int fTime;
public int prev;
public boolean visited = false;
public Vertex(int idnum) {
id = idnum;
}
public int getId() {
return id;
}
public int compareTo(Object obj) {
Vertex vert = (Vertex) obj;
return id-vert.getId();
}
#Override public String toString(){
return "Vertex # " + id;
}
public void setColor(String newColor) {
color = newColor;
}
public String getColor() {
return color;
}
public void setDTime(int d) {
dTime = d;
}
public void setFTime(int f) {
fTime = f;
}
public int getDTime() {
return dTime;
}
public int getFTime() {
return fTime;
}
public void setPrev(int v) {
prev = v;
}
public int getPrev() {
return prev;
}
public LinkedList getAdjList() {
return adjVert;
}
public void addAdj(int a) { //adds a vertex id to this vertex's adj list
adjVert.add(a);
}
public void visited() {
visited = true;
}
public boolean wasVisited() {
return visited;
}
}
I was under the impression that this kind of traversing could be done in O(V+E) time for all cycles that exist in my graph
There may be much more cycles than O(V+E) in a graph. If you iterate all of them, you will run long.
Back to your original idea, you could just try to implement a straightforward algorithm to color graph in two colors (mark an arbitrary node as black, all neighbors in white, all their neighbors in black, etc; that would be a breadth-first search). That is indeed done in O(V+E) time. If you succeed, then graph is 2-colorable. If you fail, it's not.
Edit: If you need a cycle that proves graph is not 2-colorable, just record for each node the vertex you traversed into it from. When you happen to traverse from black vertex A to black vertex B (thus needing to color black B into white and proving your graph is not 2-colorable), you get the cycle by looking back to parents:
X -> Y -> Z -> U -> V -> P -> Q -> A
\-> D -> E -> B
Then, A-B-E-D-V-P-Q (the paths up to their common ancestor) is the cycle you needed.
Note that in this version you don't have to check all cycles, you just output a first cycle, where back-edge in the tree has both vertexes colored in the same color.
you are describing a bipartite graph. a bipartite graph is 2 colorable and it contains no odd length cycles. You can use BFS to prove that a graph is bipartite or not. Hope this helps.