Dijkstra adjacency list - java

I have run into a problem converting pseudocode of Dijkstras algorithm into actual code. I was given and adjacency list such as "Location - adjacent location - distance to location," example for one node: AAA AAC 180 AAD 242 AAH 40.
My task was to read a file organized as adjacency list as described, and compute the shortest path from one node to another.
Here is the Dijkstra pseudocode:
void dijkstra( Vertex s )
{
for each Vertex v
{
v.dist = INFINITY;
v.known = false;
}
s.dist = 0;
while( there is an unknown distance vertex )
{
Vertex v = smallest unknown distance vertex;
v.known = true;
for each Vertex w adjacent to v
if( !w.known )
{
DistType cvw = cost of edge from v to w;
if( v.dist + cvw < w.dist )
{
// Update w
decrease( w.dist to v.dist + cvw );
w.path = v;
}
}
}
}
im having the most trouble with the line "for each Vertex w adjacent to v"
Here is my nonworking code:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
public class Dijkstra {
public static boolean isInteger(String s) {
return isInteger(s, 10);
}
public static boolean isInteger(String s, int radix) {
if (s.isEmpty())
return false;
for (int i = 0; i < s.length(); i++) {
if (i == 0 && s.charAt(i) == '-') {
if (s.length() == 1)
return false;
else
continue;
}
if (Character.digit(s.charAt(i), radix) < 0)
return false;
}
return true;
}
public static void dijkstra(Vertex[] a, Vertex s, int lineCount) {
int i = 0;
while (i < (lineCount)) // each Vertex v
{
a[i].dist = Integer.MAX_VALUE;
a[i].known = false;
i++;
}
s.dist = 0;
int min = Integer.MAX_VALUE; //
while (!(a[0].known == true && a[1].known == true && a[2].known == true && a[3].known == true
&& a[4].known == true && a[5].known == true && a[6].known == true && a[7].known == true
&& a[8].known == true && a[9].known == true && a[10].known == true && a[11].known == true
&& a[12].known == true)) {
System.out.println("here");
for (int b = 0; b < lineCount; b++) {
if (a[b].dist < min && a[b].known == false) {
min = a[b].dist;
}
}
int c = 0;
while (c < lineCount) {
if (a[c].dist == min && a[c].known == false) {
break;
}
c++;
}
System.out.println(min);
a[c].known = true;
int adjSize = a[c].adj.size();
int current = 0;
System.out.println(adjSize);
while (current < adjSize - 1) {
String currentAdjacent = (String) a[c].adj.get(current);
int p = 0;
while (p < lineCount) {
if (a[p].name.equals(currentAdjacent)) {
if (!a[p].known) {
String cvwString = (String) a[c].distance.get(current);
int cvw = Integer.parseInt(cvwString);
System.out.println(" This is cvw" + cvw);
System.out.println("Here2");
if (a[c].dist + cvw < a[p].dist) {
a[p].dist = a[c].dist + cvw;
a[p].path = a[c];
}
}
}
p++;
}
current++;
}
}
}
public static class Vertex {
public List adj; // Adjacency list
public List distance;
public boolean known;
public int dist; // DistType is probably int
public Vertex path;
public String name;
// Other fields and methods as needed
}
public static void printPath(Vertex v) {
if (v.path != null) {
printPath(v.path);
System.out.print(" to ");
}
System.out.print(v);
}
public static void main(String[] args) throws IOException {
int lineCounter = 0;
BufferedReader br = new BufferedReader(new FileReader("airport.txt"));
try {
StringBuilder sb = new StringBuilder();
String line = br.readLine();
while (line != null) {
sb.append(line);
sb.append(System.lineSeparator());
line = br.readLine();
lineCounter = lineCounter + 1;
}
Vertex[] arr = new Vertex[lineCounter];
for (int i = 0; i < lineCounter; i++) {
arr[i] = new Vertex();
arr[i].adj = new LinkedList<String>();
arr[i].distance = new LinkedList<Integer>();
}
;
//
int arrayCounter = 0;
String everything = sb.toString();
String[] lines = everything.split("\\s*\\r?\\n\\s*");
for (String line1 : lines) {
arr[arrayCounter] = new Vertex();
arr[arrayCounter].adj = new LinkedList<String>();
arr[arrayCounter].distance = new LinkedList<Integer>();
String[] result = line1.split("\\s+");
for (int x = 0; x < result.length; x++) {
if (x == 0) {
arr[arrayCounter].name = result[0];
continue;
} else if (isInteger(result[x])) {
arr[arrayCounter].distance.add(result[x]);
continue;
} else {
arr[arrayCounter].adj.add(result[x]);
continue;
}
}
arrayCounter++;
}
for (int i = 0; i < 12; i++) {
System.out.println(arr[i].name);
}
System.out.println(lineCounter);
dijkstra(arr, arr[3], lineCounter - 1);
printPath(arr[11]);
} finally {
br.close();
}
}
}
Using my vertex class as is I was using a series of while loops to first, traverse the adjacency strings stored in a linked list while comparing to see which vertex is equivalent to the adjacency list string. Is there a better way to code "for each Vertex w adjacent to v" using my Vertex class? And apologies ahead for messy code and any others style sins i may have committed. Thanks!

To solve this problem you need a bunch of "Node" objects, stored in a HashMap, keyed on Source Location.
In the node, you need a collection of references to adjacent "Node" objects (or at least their "key" so you can write logic against it. The "Node" also needs to know it's location and distance to each "adjacent" node. Think Lundon Underground Tube Maps - each station connects to at least one other station. Usually two or more. Therefore, adjacent nodes to tube stations are the immediate next stops you can get to from that station.
Once you have that data structure in place, you can then use a recursive routine to iterate through each individual node. It should then iterate through each child node (aka adjacent node), and track distances from the initial (source) node to the current node by storing this data in a HashMap and using the current accumulated distance whilst recursing (or "walking" the graph"). This tracking information should be part of your method signature when recursing. You will also need to track the current path you have taken when recursing, in order to avoid circular loops (which will ultimately and ironically cause a StackOverflowError). You can do this by using a HashSet. This Set should track the source and current node's location as the entry key. If you see this present during your recursion, then you have already seen it, so don't continue processing.
I'm not going to code the solution for you because I suspect that you ask more specific questions as you work your way through understanding the answer, which are very likely answered elsewhere.

Related

Breadth-First Search implementation not Working

I have a problem with the implementation of the breadth-first search algorithm, I have a method that gives me an array of integers from 0-8, in random order. I also have an integer m that tells me which number is blank. Here are the rules:
I get a block of numbers, like:
456
782
301
And lets say that 8 is the blank value, I can swap it with 5, 7, 2, and 0. since they are directly next to it. I have to use breadth-first search to solve this puzzle. Here is the code I have written so far:
package application;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Vector;
public class Solution {
/******************************************
* Implementation Here
***************************************/
/*
* Implementation here: you need to implement the Breadth First Search
* Method
*/
/* Please refer the instruction document for this function in details */
public static LinkedHashSet<int[]> OPEN = new LinkedHashSet<int[]>();
public static HashSet<int[]> CLOSED = new HashSet<int[]>();
public static boolean STATE = false;
public static int empty;
public static void breadthFirstSearch(int[] num, int m, Vector solution1) {
int statesVisited = 0;
for(int i : num) {
if(num[i] == m) {
empty = i;
}
}
int[] start = num;
int[] goal = {0,1,2,3,4,5,6,7,8};
int[] X;
int[] temp = {};
OPEN.add(start);
while (OPEN.isEmpty() == false && STATE == false) {
X = OPEN.iterator().next();
OPEN.remove(X);
int pos = empty; // get position of ZERO or EMPTY SPACE
if (compareArray(X,goal)) {
System.out.println("SUCCESS");
STATE = true;
} else {
// generate child nodes
CLOSED.add(X);
temp = up(X, pos);
if (temp != null)
OPEN.add(temp);
temp = left(X, pos);
if (temp != null)
OPEN.add(temp);
temp = down(X, pos);
if (temp != null)
OPEN.add(temp);
temp = right(X, pos);
if (temp != null)
OPEN.add(temp);
if(OPEN.isEmpty())
System.out.println("Ending loop");
}
}
}
public static boolean compareArray(int[] a, int[] b) {
for(int i: a)
if(a[i] != b[i])
return false;
return true;
}
public static int[] up(int[] s, int p) {
int[] str = s;
if (p > 3) {
int temp = str[p-3];
str[p-3] = str[p];
str[p] = temp;
}
// Eliminates child of X if its on OPEN or CLOSED
if (!OPEN.contains(str) && CLOSED.contains(str) == false)
return str;
else
return null;
}
public static int[] down(int[] s, int p) {
int[] str = s;
if (p < 6) {
int temp = str[p+3];
str[p+3] = str[p];
str[p] = temp;
}
// Eliminates child of X if its on OPEN or CLOSED
if (!OPEN.contains(str) && CLOSED.contains(str) == false)
return str;
else
return null;
}
public static int[] left(int[] s, int p) {
int[] str = s;
if (p != 0 && p != 3 && p != 6) {
int temp = str[p-1];
str[p-1] = str[p];
str[p] = temp;
}
// Eliminates child of X if its on OPEN or CLOSED
if (!OPEN.contains(str) && CLOSED.contains(str) == false)
return str;
else
return null;
}
public static int[] right(int[] s, int p) {
int[] str = s;
if (p != 2 && p != 5 && p != 8) {
int temp = str[p+1];
str[p+1] = str[p];
str[p] = temp;
}
// Eliminates child of X if its on OPEN or CLOSED
if (!OPEN.contains(str) && CLOSED.contains(str) == false)
return str;
else
return null;
}
public static void print(String s) {
System.out.println(s.substring(0, 3));
System.out.println(s.substring(3, 6));
System.out.println(s.substring(6, 9));
System.out.println();
}
}
This code just immediately ends, and never finds an answer. Perhaps I have done something wrong? Please help.
Please Note: This is my first question on StackOverFlow, so if anyone has any criticisms please tell me and I will fix them right away.
First of all, you have a parameter which isn't doing anything, Vector solution in:
public static void breadthFirstSearch(int[] num, int m, Vector solution1)
Also you are passing in the position of the zero element which you are representing as m, then assigning a local variable to that position, seems a little pointless to me there's no need to pass in the zero position if you're going to search for it anyway.
Updated breadth first search method:
public static void breadthFirstSearch(int[] num) {
for (int i : num) {
if (num[i] == 0) {
empty = i;
}
}
int[] start = num;
int[] goal = {1, 2, 3, 4, 5, 6, 7, 8, 0};
int[] X;
int[] temp = {};
OPEN.add(start);
while (OPEN.isEmpty() == false && STATE == false) {
X = OPEN.iterator().next();
OPEN.remove(X);
int pos = empty; // get position of ZERO or EMPTY SPACE
if (Arrays.equals(X, goal)) {
System.out.println("SUCCESS");
STATE = true;
} else {
// generate child nodes
CLOSED.add(X);
temp = up(X, pos);
if (temp != null) {
OPEN.add(temp);
}
temp = left(X, pos);
if (temp != null) {
OPEN.add(temp);
}
temp = down(X, pos);
if (temp != null) {
OPEN.add(temp);
}
temp = right(X, pos);
if (temp != null) {
OPEN.add(temp);
}
if (OPEN.isEmpty()) {
System.out.println("Ending loop");
}
}
}
}
The main issue with your program was that within your movement methods up(), down(), left(), right(). You weren't creating complete copies of the arrays, thus resulting in modifications happening to the original array.
Thus this assignment:
int[] str = s;
must be changed to:
int[] str = new int[s.length];
System.arraycopy(s, 0, str, 0, s.length);
Here's an example of a completed method:
public static int[] up(int[] s, int p) {
int[] str = new int[s.length];
System.arraycopy(s, 0, str, 0, s.length);
if (p > 3) {
int temp = str[p - 3];
str[p - 3] = str[p];
str[p] = temp;
}
// Eliminates child of X if its on OPEN or CLOSED
if (!OPEN.contains(str) && !CLOSED.contains(str)) {
return str;
} else {
return null;
}
}
SIDE NOTE (Not essential):
There are certain permutations of the array which won't result in the goal state. This puzzle itself can have a total number of 9! configurations, but actually only 9!/2 of these are solvable.
I wrote an algorithm for checking the parity of the puzzle, which can be done as a kind of preprocessing, I used it in order to create random instances for testing the data.
public boolean isSolvable(int[] puzzle) {
boolean parity = true;
int gridWidth = (int) Math.sqrt(puzzle.length);
boolean blankRowEven = true; // the row with the blank tile
for (int i = 0; i < puzzle.length; i++) {
if (puzzle[i] == 0) { // the blank tile
blankRowEven = (i / gridWidth) % 2==0;
continue;
}
for (int j = i + 1; j < puzzle.length; j++) {
if (puzzle[i] > puzzle[j] && puzzle[j] != 0) {
parity = !parity;
}
}
}
// even grid with blank on even row; counting from top
if (gridWidth % 2 == 0 && blankRowEven) {
return !parity;
}
return parity;
}
For the vector
You want to be able to print out the path that has been taken to get to the goal state, I would recommend having a class for the State such:
private State previousState;
private int[] current;
public State(int[] current, State previousState) {
this.current = current;
this.previousState = previousState
}
public State getPreviouState(){
return previousState;
}
public int[] getCurrentState(){
return currentState;
}
Then when you have the goal State you can loop through all the previous States to see the path it took.
State current = GOAL;
while(current != null){
System.out.println(Arrays.toString(current));
current = current.getPreviousState();
}
The method up(...) has an error:
You have:
str[p] = str[p-3];
Which I'm guessing should be:
str[p] = temp;

Fastest way to check if a haystack contains set of needles

I have a haystack string and I would like to check if it contains any of the needle strings. Currently I do it that way:
Set<String> needles = ...;
...
String [] pieces = haystack.split(" ");
for (String piece: pieces) {
if (needles.contains(piece) {
return true;
}
}
return false;
It works, but it is relatively slow.
Question: Is there a faster way to accomplish the task?
Example.
Haystack: I am a big tasty potato .
Needles: big, tasty
== RUN ==
I am a big tasty potato .
|
[tasty] got a match, we are good!
You should take a look at Aho-Corasick algorithm. This suits your problem because it build an automaton of all words(needles) and traverse the text(haystack) over the built automaton to find all matching words. Its basically constructs a finite state machine that resembles a trie.
The time complexity is O(n + m + z) where
z is the total number of occurrences of words in text, n is the length of text and m is the total number characters in all words.
Edit 2
Here is a straight-forward implementation which stop traversing after finding first occurrence of any needle.
import java.util.*;
class AhoCorasick {
static final int ALPHABET_SIZE = 256;
Node[] nodes;
int nodeCount;
public static class Node {
int parent;
char charFromParent;
int suffLink = -1;
int[] children = new int[ALPHABET_SIZE];
int[] transitions = new int[ALPHABET_SIZE];
boolean leaf;
{
Arrays.fill(children, -1);
Arrays.fill(transitions, -1);
}
}
public AhoCorasick(int maxNodes) {
nodes = new Node[maxNodes];
// create root
nodes[0] = new Node();
nodes[0].suffLink = 0;
nodes[0].parent = -1;
nodeCount = 1;
}
public void addString(String s) {
int cur = 0;
for (char ch : s.toCharArray()) {
int c = ch;
if (nodes[cur].children[c] == -1) {
nodes[nodeCount] = new Node();
nodes[nodeCount].parent = cur;
nodes[nodeCount].charFromParent = ch;
nodes[cur].children[c] = nodeCount++;
}
cur = nodes[cur].children[c];
}
nodes[cur].leaf = true;
}
public int suffLink(int nodeIndex) {
Node node = nodes[nodeIndex];
if (node.suffLink == -1)
node.suffLink = node.parent == 0 ? 0 : transition(suffLink(node.parent), node.charFromParent);
return node.suffLink;
}
public int transition(int nodeIndex, char ch) {
int c = ch;
Node node = nodes[nodeIndex];
if (node.transitions[c] == -1)
node.transitions[c] = node.children[c] != -1 ? node.children[c] : (nodeIndex == 0 ? 0 : transition(suffLink(nodeIndex), ch));
return node.transitions[c];
}
// Usage example
public static void main(String[] args) {
AhoCorasick ahoCorasick = new AhoCorasick(1000);
ahoCorasick.addString("big");
ahoCorasick.addString("tasty");
String s = "I am a big tasty potato";
int node = 0;
for (int i = 0; i < s.length(); i++) {
node = ahoCorasick.transition(node, s.charAt(i));
if (ahoCorasick.nodes[node].leaf) {
System.out.println("A match found! Needle ends at: " + i); // A match found! Needle ends at: 9
break;
}
}
}
}
However currently this code will find the end position of any occurrences in text. If you need the starting position and/or the needle, you can trace back from the ending position until finding a space to get the matched word.
This doesn't guaranty speed in worst-case, but should work better on average and best cases.
You can use java8 plus with parallel streams with anymatch function
boolean hi=Arrays.stream(pieces).parallel().anyMatch(i->needle.contains(i));
You should make sure needless is an instance of a HashSet which makes contains a "fast", constant time operation. Next, don't process all of haystack if you don't have to... Try this:
int i, j, l = haystack.length();
for(i = 0; i < l; i = j + 1) {
j = haystack.indexOf(' ', i + 1);
if(j == -1) {
j = l - 1;
}
String hay = haystack.s substring(i, j - 1).trim();
if(hay.length() > 0 && needles.contains(hay)) {
return true;
}
}
return false;
*note: this is untested and indexes might be off by +-1, as well as some edge cases might exist. use at your own risk.
Generally most of your slowdown is the split command. You are way better off searching the one string you have than allocating a crap ton of objects. You'd be better off doing regex, and avoiding new object construction. And using Aho would be quite effective. Assuming your lists are big enough to be troublesome.
public class NeedleFinder {
static final int RANGEPERMITTED = 26;
NeedleFinder next[];
public NeedleFinder() {
}
public NeedleFinder(String haystack) {
buildHaystack(haystack);
}
public void buildHaystack(String haystack) {
buildHaystack(this,haystack,0);
}
public void buildHaystack(NeedleFinder node, String haystack, int pos) {
if (pos >= haystack.length()) return;
char digit = (char) (haystack.charAt(pos) % RANGEPERMITTED);
if (digit == ' ') {
buildHaystack(this,haystack,pos+1);
return;
}
if (node.next == null) node.next = new NeedleFinder[RANGEPERMITTED];
if (node.next[digit] == null) node.next[digit] = new NeedleFinder();
NeedleFinder nodeNext = node.next[digit];
buildHaystack(nodeNext,haystack,pos+1);
}
public boolean findNeedle(String needle) {
return findNeedle(this, needle,0);
}
private boolean findNeedle(NeedleFinder node, String needle, int pos) {
if (pos >= needle.length()) return true;
char digit = (char) (needle.charAt(pos) % RANGEPERMITTED);
if (node.next == null) return false;
if (node.next[digit] == null) return false;
return findNeedle(node.next[digit],needle,pos+1);
}
}
On success, check the contains to make sure it's not a false positive. But, it's fast. We're talking 1/5th the speed of binary search.
Speaking of, binary search is a great idea. It's in the right time complexity alone. Just sort your silly list of haystack strings then when you look through the needles do a binary search. In java these are really basic and items in Collections. Both the .sort() and the .binarySearch() commands. And it's going to be orders of magnitude better than brute.
value = Collections.binarySearch(haystackList, needle, strcomp);
If value is positive it was found.
Collections.sort(words, strcomp);
With the strcomp.
public Comparator<String> strcomp = new Comparator<String>() {
#Override
public int compare(String s, String t1) {
if ((s == null) && (t1 == null)) return 0;
if (s == null) return 1;
if (t1 == null) return -1;
return s.compareTo(t1);
}
};
If it's really all about speed, and you want to search through a list of items instead of a solid string, you could divide the work into different threads (I'm not sure how many items you're checking with, but if it's not taking minutes, this might not be the way to go)
If you don't need to make the haystack into an array, you could instead iterate through needles, and test haystack via String.contains();

Creating a balanced binary tree with a string of letters

I'm trying to create a balanced tree with a string of letters. If you put "ABCDE" for string, your code is expected to give an output something like this.
INPUT : "ABCDE"
OUTPUT :
.......................................................
+
+ E
+ + -- --
A B C D -- -- -- --
.......................................................
The book suggests me to first create an array of one-node tree whose root will be each character of the string. Then, make a three-node tree out of each pair of one-node trees, making a new + node for the root which will result in a forest of three - node trees.
I know this problem is a stepping-stone to ultimately write the Huffman tree.
I'm having trouble putting the three-node tree back into the array with one-node trees and then make a 7-node tree by combining the two three-node trees and so on.
Below is my code,
import java.util.*; // for Stack class
class StringNode {
public char iData; // data item (key)
public StringNode leftChild; // this node's left child
public StringNode rightChild; // this node's right child
StringNode(char d) {
iData = d;
}
public void displayNode() // display ourself
{
System.out.print('{');
System.out.print(iData);
System.out.print("} ");
}
} // end class Node
class STree {
private StringNode root; // first node of tree
public String sequence;
// -------------------------------------------------------------
public STree() // constructor
{
root = null;
} // no nodes in tree yet
public void makeBalanceTree() // creating a balanced tree
{
StringNode array[] = new StringNode[sequence.length()];
for (int i = 0; i < sequence.length(); i++)
array[i] =
new StringNode(sequence.charAt(i)); //fill array with node holding each character as key
STree forest[] = new STree[array.length]; //make a forest of trees
for (int j = 0; j < array.length; j++) { //store each node as the root of the tree
forest[j] = new STree();
forest[j].root = array[j];
}
int count = sequence.length();
while (count == 0) {}
}
public void displayTree() {
Stack globalStack = new Stack();
globalStack.push(root);
int nBlanks = 32;
boolean isRowEmpty = false;
System.out.println("......................................................");
while (isRowEmpty == false) {
Stack localStack = new Stack();
isRowEmpty = true;
for (int j = 0; j < nBlanks; j++) System.out.print(' ');
while (globalStack.isEmpty() == false) {
StringNode temp = (StringNode) globalStack.pop();
if (temp != null) {
System.out.print(temp.iData);
localStack.push(temp.leftChild);
localStack.push(temp.rightChild);
if (temp.leftChild != null || temp.rightChild != null) isRowEmpty = false;
} else {
System.out.print("--");
localStack.push(null);
localStack.push(null);
}
for (int j = 0; j < nBlanks * 2 - 2; j++) System.out.print(' ');
} // end while globalStack not empty
System.out.println();
nBlanks /= 2;
while (localStack.isEmpty() == false) globalStack.push(localStack.pop());
} // end while isRowEmpty is false
System.out.println("......................................................");
} // end displayTree()
} // end class Tree
public class StringTreeApp {
public static void main(String[] args) {
int value;
STree theTree = new STree();
theTree.sequence = "ABCDE";
theTree.makeBalanceTree();
theTree.displayTree();
} // end main()
} // end class TreeApp
I think that the book is making it harder for you than necessary by recommending creating an array of StringNodes up front.
If you have a String, you know that the "middle" character is going to be the iData; the characters preceding it are going to be in the left tree; the characters following it are going to be in the right tree.
As such, you should be able to construct a StringNode as follows:
StringNode buildStringNode(String sequence) {
if (sequence.isEmpty()) return null;
int middlePos = (sequence.length() + 1) / 2;
char iData = sequence.charAt(middlePos);
StringNode result = new StringNode(iData);
result.leftChild = buildStringNode(sequence.substring(0, middlePos));
result.rightChild = buildStringNode(sequence.substring(middlePos + 1));
return result;
}
This "automatically" combines the child trees with the parent tree. Your makeBalanceTree() method is then simply:
void makeBalanceTree() {
root = buildStringNode(sequence);
}

store a binary search tree graph into an array using recursion and print it out

I already build a binary search tree. The primitive data type I store in the tree is integer. I try to store it on a 2-D char array and then print it out as the graph shown below(the numbers represent row numbers and column numbers and I do not need to print it, ignoring "-" symbol please, I only use it to indicate the exact position)
-----0---1---2---3---4---5---6---7---8---9---10---11---12---13---14---15---16
0---------------------------------------12
1--------------------------------/-------------------\
2----------------------8--------------------------------------14
3-----------------/----------\ -----------------------------------------\
4-------------5----------------9-------------------------------------------34
5--------/-------------------------------------------------------------/-------------\
6---2---------------------------------------------------------24------------------------35
number 12 need to store on location [0][8], the middle of the first row.
number 4 store on[2][4], number 14=[2][12], 5=[4][2], 9=[4][9] and so on.
row number 1 which is second row, "/" is on position[1][6] and "\" is on position[1][10] etc.they are also on the middle between two numbers
following is my code
public class MainClass {
public static void main(String[] args) {
//level represents row number;
// start indicates the column I am going to
//store number in, and end is a fixed column number
// BinarySearchTree is a BinaryTree type instance,
// I already story integers on it and follow with the format
// of binary search trees, and I did tested it.
int level=0; int start=0; int end=80;
BinaryTree.plot(BinarySearchTree, level, start, end);
}
private static class BinaryTree {
private BinaryNode root;
static char[][] offset = new char [10][20];
public BinaryTree(){
root = null;
}
public BinaryTree(Object x){
root = new BinaryNode(x);
}
public boolean isEmpty(){
return root == null;
}
public Object getRootobj() throws BinaryTreeException{
if(root == null)
throw new BinaryTreeException("Empty Tree");
else
return root.element;
}
public BinaryTree getLeft() throws BinaryTreeException{
if(root == null)
throw new BinaryTreeException("Empty Tree");
else {
BinaryTree t = new BinaryTree();
t.root = root.left;
return t;
}
}
public BinaryTree getRight() throws BinaryTreeException{
if(root == null)
throw new BinaryTreeException("Empty Tree");
else {
BinaryTree t = new BinaryTree();
t.root = root.right;
return t;
}
}
public static void plot(BinaryTree t, int level, int start, int end){
if(!t.isEmpty()){
plot(t.getLeft(), level+2, start/2, end/2);
String string = Integer.toString((Integer)t.getRootobj());
for(char c: string.toCharArray())
offset[level][start++]=c;
if(!(t.getLeft().isEmpty()))
offset[++level][start/4*3] = '/';
if(!(t.getRight().isEmpty()))
offset[++level][((start+end)/2+start)/2] = '\\';
plot(t.getRight(), level+2, end/2, end);
}
for(int i = 0; i<10; i++){
for(int j= 0; j<20; j++)
System.out.print(offset[i][j]);
}
}
}
private static class BinaryNode {
Object element;
BinaryNode left,right;
BinaryNode() {
this(0);
}
BinaryNode(Object e) {
this(e, null, null);
}
BinaryNode(Object e, BinaryNode ln, BinaryNode m){
element=e;
left=ln;
right=m;
}
}
}
Question: the method plot I used to store and print out binarysearchtree did not work, which causes a java.lang.ArrayIndexOutOfBoundsException:
can anyone take a look at it. appreciated for the help.
Your fixed-size char-Array cannot cope with your dynamic sized BinaryTree. For your given example alone you need way more then 20 characters per line! That's where your Exception is coming from.
But to give you an idea of an alternative approach - even though it took a while, made the following additions to your code:
First, I added a method to the BinaryNode class:
int getDepth() {
int subTreeDepth;
if (left == null && right == null) {
subTreeDepth = 0;
} else if (left == null) {
subTreeDepth = right.getDepth();
} else if (right == null) {
subTreeDepth = left.getDepth();
} else {
subTreeDepth = Math.max(left.getDepth(), right.getDepth());
}
return 1 + subTreeDepth;
}
Second, I removed your fixed char-Array and replaced the whole plotting algorithm in your BinaryTree (I just couldn't wrap my head around all those relative array-index manipulations):
public void plot() {
if (root == null) {
throw new BinaryTreeException("Empty Tree");
}
int lineCount = 2 * root.getDepth() - 1;
StringBuilder[] lines = new StringBuilder[lineCount];
for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) {
lines[lineIndex] = new StringBuilder();
}
// get the right most node (which contains the largest element value)
BinaryNode rightMostNode = root;
while (rightMostNode.right != null) {
rightMostNode = rightMostNode.right;
}
// check how many characters we have to reserve for a single node element
int maxElementLength = String.valueOf(rightMostNode.element).length();
plot(root, 0, 0, maxElementLength, lines);
for (StringBuilder singleLine : lines) {
System.out.println(singleLine.toString());
}
}
private void plot(BinaryNode subTreeRoot, int offset, int lineIndex, int elementLength, StringBuilder[] lines) {
int actualOffset;
if (subTreeRoot.left == null) {
actualOffset = offset;
} else {
actualOffset = offset + (int) Math.pow(2, subTreeRoot.left.getDepth() - 1) * elementLength;
}
StringBuilder currentLine = lines[lineIndex];
String elementValue = String.valueOf(subTreeRoot.element);
for (int lineFillIndex = currentLine.length() + elementValue.length() / 2; lineFillIndex < actualOffset; lineFillIndex++) {
currentLine.append(' ');
}
currentLine.append(elementValue);
if (subTreeRoot.left != null) {
// draw connection to left sub tree
int connectPosition = (actualOffset - offset) * 3 / 4 + offset;
StringBuilder connectLine = lines[lineIndex + 1];
for (int lineFillIndex = connectLine.length(); lineFillIndex < connectPosition; lineFillIndex++) {
connectLine.append(' ');
}
connectLine.append('/');
// insert the left part of the next value line
plot(subTreeRoot.left, offset, lineIndex + 2, elementLength, lines);
}
if (subTreeRoot.right != null) {
// draw connection to right sub tree
int connectPosition = actualOffset + elementLength - elementValue.length() / 2;
if (subTreeRoot.right.left != null) {
connectPosition += (int) Math.pow(2, subTreeRoot.right.left.getDepth() - 1) * elementLength / 2;
}
StringBuilder connectLine = lines[lineIndex + 1];
for (int lineFillIndex = connectLine.length(); lineFillIndex < connectPosition; lineFillIndex++) {
connectLine.append(' ');
}
connectLine.append('\\');
// insert the right part of the next value line
plot(subTreeRoot.right, actualOffset + elementLength, lineIndex + 2, elementLength, lines);
}
}
For a tree similar to the one, you included in your question:
BinaryTree binarySearchTree = new BinaryTree(
new BinaryNode(12,
new BinaryNode(8,
new BinaryNode(5,
new BinaryNode(3),
null),
new BinaryNode(9)),
new BinaryNode(14,
null,
new BinaryNode(34,
new BinaryNode(24),
new BinaryNode(35)))));
binarySearchTree.plot();
I get the following output:
12
/ \
8 14
/ \ \
5 9 34
/ / \
3 24 35

Java Binary Tree entered in a specific order

I am trying to complete an assignment where I need to write a Java program to take a string from the command line, and implement it as a Binary Tree in a specific order, then get the depth of the binary tree.
For example: "((3(4))7((5)9))"
would be entered as a tree with 7 as the root, 3 and 9 as the children, and 4 as a right child of 3, and 5 as a left child of 9.
My code is below.. The problem I am having is that, because I am basing my checks off of finding a right bracket, I am unsure how to get the elements correctly when they are not directly preceding the brackets, such as the 3 in the above string. Any direction would be greatly appreciated..
class Node {
int value;
Node left, right;
}
class BST {
public Node root;
// Add Node to Tree
public void add(int n) {
if (root == null) {
root = new Node( );
root.value = n;
}
else {
Node marker = root;
while (true) {
if (n < marker.value) {
if (marker.left == null) {
marker.left = new Node( );
marker.left.value = n;
break;
} else {
marker = marker.left;
}
} else {
if (marker.right == null) {
marker.right = new Node( );
marker.right.value = n;
break;
} else {
marker = marker.right;
}
}
}
}
} // End ADD
//Find Height of Tree
public int height(Node t) {
if (t.left == null && t.right == null) return 0;
if (t.left == null) return 1 + height(t.right);
if (t.right == null) return 1 + height(t.left);
return 1 + Math.max(height(t.left), height(t.right));
} // End HEIGHT
// Check if string contains an integer
public static boolean isInt(String s) {
try {
Integer.parseInt(s);
}
catch(NumberFormatException e) {
return false;
}
return true;
} // End ISINT
public int elementCount(String[] a) {
int count = 0;
for (int i = 0; i < a.length; i++) {
if (isInt(a[i])) count++;
}
return count;
}
} // End BST Class
public class Depth {
public static void main(String[] args) {
String[] a = args[0].split(" ");
BST tree = new BST();
int[] bcount = new int[10];
int[] elements = new int[10];
int x = 0, bracketcount = 0;
// Display entered string
System.out.print("Entered Format: ");
for (int j=0; j < a.length; j++) {
System.out.print(a[j]);
}
for (int i=0; i < a.length; i++) {
char c = a[i].charAt(0);
switch (c)
{
case '(':
bracketcount++;
break;
case ')':
if (isInt(a[i-1])) {
bcount[x] = bracketcount--;
elements[x++] = Integer.parseInt(a[i-1]);
}
break;
case '1':
case '7':
default : // Illegal character
if ( (a[i-1].charAt(0) == ')') && (a[i+1].charAt(0) == '(') ) {
bcount[x] = bracketcount;
elements[x++] = Integer.parseInt(a[i]);
}
break;
}
}
System.out.println("\nTotal elements: " + tree.elementCount(a));
// Display BracketCounts
for (int w = 0; w < x; w++) {
System.out.print(bcount[w] + " ");
}
System.out.println(" ");
// Display Elements Array
for (int w = 0; w < x; w++) {
System.out.print(elements[w] + " ");
}
System.out.println("\nDepth: " + tree.height(tree.root));
// Build the tree
for (int y = 0; y < x-1; y++) {
for (int z = 1; z < tree.height(tree.root); z++) {
if (bcount[y] == z) {
tree.add(elements[y]);
}
}
}
} // End Main Function
public static boolean isInt(String s) {
try {
Integer.parseInt(s);
}
catch(NumberFormatException e) {
return false;
}
return true;
}
} // End Depth Class
I would do a couple of statements to get access to a tree with that kind of shape:
For input string : input= "((3(4))7((5)9))"
You could do :
public class Trial {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String input = "((3(4))7((5)9))";
String easier = input.replaceAll("\\(\\(", "");
String evenEasier = easier.replaceAll("\\)\\)", "");
System.out.println(evenEasier);
int firstVal = Integer.parseInt(evenEasier.substring(0, 1));
int firstBracketVal = Integer.parseInt(evenEasier.substring(2, 3));
int middleVal = Integer.parseInt(evenEasier.substring(3, 4));
int secondBracketVal = Integer.parseInt(evenEasier.substring(4,5));
int lastVal = Integer.parseInt(evenEasier.substring(6));
System.out.println("First Val:"+firstVal);
System.out.println("First bracket Val:"+firstBracketVal);
System.out.println("Middle Val:"+middleVal);
System.out.println("Second Bracket Val:"+secondBracketVal);
System.out.println("Last Val:"+lastVal);
}
}
This however would only ever work for entries in that specific format, if that were to change, or the length of the input goes up - this would work a bit or break.....If you need to be able to handle more complicated trees as input in this format a bit more thought would be needed on how to best handle and convert into your internal format for processing.
pseudocode:
function getNode(Node)
get one char;
if (the char is "(")
getNode(Node.left);
get one char;
end if;
Node.value = Integer(the char);
get one char;
if (the char is "(")
getNode(Node.right);
get one char;
end if;
//Now the char is ")" and useless.
end function
Before calling this function, you should get a "(" first.
In this method, the framwork of a Node in string is "[leftchild or NULL] value [rightchild or NULL])".
"("is not belong to the Node, but ")" is.

Categories

Resources