As a programming exercise I need to rewrite some existing methods and classes that make up a binary tree. (The method signatures and constructors have to stay the same). Somehow I don't really seem to understand what I have done here.
Does the toString method have to public because it overwrites the Object class's toString method? And how can I avoid the nulls from being returned?
Here is the code I have come to so far:
Tree Class
Node root = null;
void addNode(int val) {
Node newNode = new Node(val);
root = newNode.addNode(root, val);
}
Node Class
Node(int val) {
val = val;
left = null;
right = null;
}
Node addNode(Node focusNode, int newNodeVal) {
if (focusNode == null)
return this;
if (newNodeVal == focusNode.val)
return focusNode;
if (newNodeVal < focusNode.val)
focusNode.left = this.addNode(focusNode.left, newNodeVal);
else
focusNode.right = this.addNode(focusNode.right, newNodeVal);
return focusNode;
}
public String toString() {
return this.left + " " + this.val + " " + this.right;
}
Use a StringBuilder to store the String representation of the node and append the data of the children nodes only in the specific node is not null. Here's an example using infix navigation on the nodes:
public String toString() {
StringBuilder sb = new StringBuilder();
if (this.left != null) {
sb.append(this.left);
sb.append(' ');
}
sb.append(this.val);
if (this.right != null) {
sb.append(' ');
sb.append(this.right);
}
return sb.toString();
}
public String toString() {
if(this.left==null){
return this.val + this.right;
} else if (this.right==null){
return this.left + this.val;
} else if (this.left == null && this.right == null){
return "";
} else {
return this.left + " " + this.val + " " + this.right;
}
}
You assign your nodes to null to start, and your toString method assumes that they have been altered. Imagine a tree where you added 5, then 3. Then called toString on the tree. It will try to print the node, 5 is the value, left is 3, right is null. When you try to call
return this.left + " " + this.val + " " + this.right;
You are saying to print
3 5 NULL
You can initialize left and right with an empty Node object (with no val) and when you print it you will see null as the val of an empty Node:
Node(Integer val) {
this.val = val;
left = new Node(null);
right = new Node(null);
}
This only works if you make val an Integer.
There is also a bug in your code:
val = val will leave this.val untouched. You have to use this.val = val;
toString() is overridden not overwritten. It has to return a String and you can't really avoid nulls if your Node is a leaf.
What you can do is writing toString() in a way it can be meaningful to have a null as val like this:
public String toString() {
return "#{val = " + val + ", left = " + left + ", right = " + right + "}"
}
Please note that this will traverse your tree recursively so to print your tree you only have to call root.toString().
Related
I have to implement binary search tree with method that prints nice diagram with connections like this:
For now I managed to print this:
However I'm struggling to make it better :/
Do you have any hints how to fix that?
It's my code of instance implementing it:
public interface PrintableTree {
class Node {
int data;
Node left, right;
Node(int data) {
this.data = data;
this.left = null;
this.right = null;
}
}
class Trunk {
Trunk prev;
String str;
Trunk(Trunk prev, String str) {
this.prev = prev;
this.str = str;
}
}
Node insert_Recursive(Node root, int key);
void add(int i);
String prettyPrint();
static PrintableTree getInstance() {
return new PrintableTree() {
String stringOfTree = "";
static final int COUNT = 2;
Node root;
#Override
public void add(int i) {
root = insert_Recursive(root, i);
}
#Override
public Node insert_Recursive(Node root, int key) {
if (root == null) {
root = new Node(key);
return root;
}
if (key < root.data)
root.left = insert_Recursive(root.left, key);
else if (key > root.data)
root.right = insert_Recursive(root.right, key);
return root;
}
#Override
public String prettyPrint() {
printTree(root, null, false);
return "";
}
public void showTrunks(Trunk p) {
if (p == null) {
return;
}
showTrunks(p.prev);
System.out.print(p.str);
}
public void printTree(Node root, Trunk prev, boolean isLeft) {
if (root == null) {
return;
}
String prev_str = " ";
Trunk trunk = new Trunk(prev, prev_str);
printTree(root.left, trunk, true);
if (prev == null) {
trunk.str = "";
} else if (isLeft) {
trunk.str = "┌";
prev_str = " │";
} else {
trunk.str = "└";
prev.str = prev_str;
}
showTrunks(trunk);
System.out.println(" " + root.data);
if (prev != null) {
prev.str = prev_str;
}
trunk.str = " │";
printTree(root.right, trunk, false);
}
};
}
}
You could use these functions. They return a string, so it is up to the caller to print it.
I also find it nicer when the right subtree is printed upwards, and the left subtree downwards. That way, the tree is just rotated 90° from how it is usually depicted -- with the root at the top.
Here is the relevant code:
public String pretty() {
return pretty(root, "", 1);
}
private String pretty(Node root, String prefix, int dir) {
if (root == null) {
return "";
}
String space = " ".repeat(("" + root.data).length());
return pretty(root.right, prefix + "│ ".charAt(dir) + space, 2)
+ prefix + "└ ┌".charAt(dir) + root.data
+ " ┘┐┤".charAt((root.left != null ? 2 : 0)
+ (root.right != null ? 1 : 0)) + "\n"
+ pretty(root.left, prefix + " │".charAt(dir) + space, 0);
}
I want to make an in order transversal of a binary tree. I made this method:
public String inorder()
{
String inorder = "";
return recrInorder(this.root, inorder);
}
then i have a helper method:
private String recrInorder(Node curr,String string)
{
if(curr == null)
{
return "";
}
//Go through left
recrInorder(curr.getLeft(), string);
string = string + curr.getData() + ", ";
//Go through right
recrInorder(curr.getRight(), string);
return string;
}
This will only print the root, i want the whole list printed.
In Java parameters are passed by value for object reference so assigning new value to your input parameter named string will not change its value outside of that function.
You need to change your code like this
private String recrInorder(Node curr,String string)
{
if(curr == null)
{
return string; // preserve previously calculated value
}
//Go through left
string = recrInorder(curr.getLeft(), string);
string = string + curr.getData() + ", ";
//Go through right
string = recrInorder(curr.getRight(), string);
return string;
}
public String inorder() {
return recrInorder(this.root);
}
private String recrInorder(Node curr) {
if (curr == null) return "";
return recrInorder(curr.getLeft()) + curr.getData() + ", " + rectInorder(curr.getRight());
}
Given a parent, left child, and right child relationship, the traversal of the tree is dependent on when you visit the parent and access the String value.
Given the root of the tree call it as follows:
String s = traverse(root, "");
System.out.println(s);
public static String traverse(Node n, String s) {
if (n == null) {
return s;
}
// s += n.value; // uncomment this assignment for preorder traversal
s = traverse(n.left,s);
// s += n.value; // uncomment this assignment for inorder traversal
s = traverse(n.right,s);
// s += n.value; // uncomment this assignment for postorder traversal
return s;
}
So in your method, you could pass a flag or enum and caveat the assignment based on that value, thus adapting your method to any traversal.
I am making a recursive decent parser with this grammar.
Expr -> Term ( '+' | '-' ) Expr | Term
Term -> Number ( '*' | '/' ) Term | Number
Number -> any valid Java double
And My getTerm Method looks like this.
private static BTree getTerm(Tokenizer tokens)
{
String tokenHold = "";
BTree result = new BTree(getNumber(tokens).getElement());
System.out.println("VALUE of result : " + result.toString());
while(tokens.hasToken() && ("*/".indexOf(tokens.peekToken()) != -1)){
BTree newTree = null;
boolean isMulti = false;
boolean isDiv = false;
if(tokens.peekToken().equals("*")){
isMulti = true;
}
if(tokens.peekToken().equals("/")){
isDiv = true;
}
if(isMulti) {
newTree = new BTree( "*" );
}
else if(isDiv){
newTree = new BTree( "/" );
}
tokenHold = tokens.nextToken();
newTree.addLeftTree(result);
newTree.addRightTree(getTerm(tokens));
result = newTree;
}
System.out.println("Expression of result : " + result.toString());
return result;
}
It returns to the getExpr method which looks like
private static BTree getExpr(Tokenizer tokens)
{
String tokenHold = "";
BTree result = new BTree(getTerm(tokens).getElement());//consumes term
System.out.println("Expression of result in getExpr: " + result.toString());
while(tokens.hasToken() && ("+-".indexOf(tokens.peekToken()) != -1)){
BTree newTree = null;
boolean isAdd = false;
boolean isSub = false;
if(tokens.peekToken().equals("+")){isAdd = true;}
if(tokens.peekToken().equals("-")){isSub = true;}
if(isAdd){ newTree = new BTree( "+" );}
else if(isSub){ newTree = new BTree( "-" );}
tokenHold = tokens.nextToken();
newTree.addRightTree(result);
newTree.addLeftTree(getTerm(tokens)); // old tree on the right
result = newTree;
}
return result;
}
Constructors for the BTree
public BTree(String element)
{
this.element = element;
left = null;
right = null;
}
public BTree(String element, BTree left, BTree right)
{
this.element = element;
this.left = left;
this.right = right;
}
When I input this syntax 4 / 2 / 2 . The getTerm Method has the correct values being returned " (/ 4 (/ 2 2)) " but the getExpr only see's "/". I have sat and tried to figure out my issue but I think I might have a fundamental miss understanding of how these two methods are passing arguments. I also have a feeling it is because of the recursion. I will answer this question myself if I figure it out myself. Thanks in advance.
Alright I finally figured it out.
in my getExpr method I was using a constructor that I didn't include in the original question for the Binary Tree it looks like this
public BTree(String element)
{
this.element = element;
left = null;
right = null;
}
I should have been using the constructor that had both the left and right child of the tree. This constructor looks like this.
public BTree(String element, BTree left, BTree right)
{
this.element = element;
this.left = left;
this.right = right;
}
Because I was not using the correct constructor when passing this value from the getTerm method to getExpr method I lost some of the information and thus was only getting the root. I am new to binary tree's / Recursion / AST and sometimes forget the BIG picture when working with these tools.
void access(T elem)
This function should determine whether the given element is in the tree. If so, the tree should semi-splay so that the node with the given element is moved towards the root of the tree when the function returns. If the given element is not found in the tree, it should simply be inserted into the tree (with no splaying).
Here is Node class
public class Node<T> {
public T elem = null;
public Node<T> left = null;
public Node<T> right = null;
public Node(T _elem) {
elem = _elem;
}
public String toString() {
String out = elem.toString();
out += " [L: "+ (left == null ? "null" : left.elem) + "] ";
out += " [R: "+ (right == null ? "null" : right.elem) + "] ";
return out;
}
}
I'm having a hard time printing the following tree:
----------t1:----------
tree:(((1),2,(3)),4,((5),6,(7,(8))))
----------t2:----------
tree:(((1),2,((3),4)),5,(6,(7,(8))))
----------t3:----------
tree:((1),2,(3,(4,(5,(6,(7,(8)))))))
----------t4:----------
tree:((((((((1),2),3),4),5),6),7),8)
where "father" have no parentheses and each "son" has a bracket depends on the depth,the picture are the trees with depth.
here is my code:
private String toString(String acc,int length){
if (left != null)
acc =left.toString(acc, length + 1)+")"+",";
// Adding two spaces 'length' times
for (int i = 0; i < length; i++) {
acc +="";
}
// adding the object data and new space
acc += this.data.toString();
if (right != null)
acc = "("+right.toString(acc, length + 1);
return acc;
}
public String toString() {
return "("+toString("", 0)+")";
}
which instead prints:
----------t1:----------
tree:(((((1),23),45),678)
----------t2:----------
tree:(((((1),23),4),5678)
----------t3:----------
tree:(((((((1),2345678)
----------t4:----------
tree:(1),2),3),4),5),6),7),8)
in the added picture, the tree is demonstrated with depth
Actually, every node has an opening and closing parenthesis. As Stefan commented, you don't need the depth. Here's the code:
public String toString() {
String out = "(";
if (this.left != null) {
out += left.toString() + ",";
}
out += this.data;
if (this.right != null) {
out += "," + right.toString();
}
return out + ")";
}