Heterogeneous Binary Search Tree - java

I need to build a heterogeneous(Elements with different types) BST and be able to sort the elements but I do not know how to approach the problem.
I've got the binary tree code right here.
This is the node class
public class Node<T> {
T data;
Node<T> left;
Node<T> right;
Node(T data) {
this.data = data;
left = null;
right = null;
}
}
And this is the tree class.
public class Tree<T extends Comparable<T>> {
private Node<T> root;
StringBuilder result = new StringBuilder();
public Tree() {
root = null;
}
public Node<T> getRoot() {
return root;
}
/**
* Method that inserts nodes into the binary tree. If the tree is empty , a new root node is
* initialized.
*
* #param root A node object.
* #param dataBeingInserted The object to be inserted on the tree.
* #return The root node object
*/
private Node<T> insertNode(Node<T> root, T dataBeingInserted) {
if (root == null) {
root = new Node<>(dataBeingInserted);
return root;
}
if (dataBeingInserted.compareTo(root.data) < 0) {
root.left = insertNode(root.left, dataBeingInserted);
} else if (dataBeingInserted.compareTo(root.data) > 0) {
root.right = insertNode(root.right, dataBeingInserted);
}
return root;
}
public void insertNode(T dataBeingInserted) {
root = insertNode(root, dataBeingInserted);
}
/**
* Method that recursively searches for our element through the tree. If the value is present in
* the root node , or there aren't any nodes in the tree , the method returns the root node. If
* the value we're looking for is smaller than the root node's value , we search for our value in
* the left subtree , otherwise we search for it in the right subtree.
*
* #param root A node object.
* #param dataBeingSearched User's value.
* #return Recursive call of the method.
*/
private Node<T> searchTree(Node<T> root, T dataBeingSearched) {
if (root == null || dataBeingSearched.compareTo(root.data) == 0) {
return root;
}
if ((dataBeingSearched.compareTo(root.data) > 0)) {
return searchTree(root.left, dataBeingSearched);
}
return searchTree(root.right, dataBeingSearched);
}
public Node searchTree(T dataBeingSearched) {
return searchTree(root, dataBeingSearched);
}
/**
* An implementation of the In-order traversal. First the left subtree is visited and printed
* accordingly, then we visit and print the root and after that we visit and print the right
* subtree.
*
* #param root The root node object.
*/
private String inorderTraversal(Node root) {
if (root == null) {
return "";
}
inorderTraversal(root.left);
result.append(root.data).append(" ");
inorderTraversal(root.right);
return result.toString();
}
public void inorderTraversal() {
inorderTraversal(root);
}
}
The problem with my tree right now is that I'm getting ClassCastException whenever any element is different than the root , because there what happens is the root defines the type of the tree and I can't fix that.
P.S
Here is the snippet that gives me the error (I will post the whole main method for convenience.)
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Scanner;
public class Main {
private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
private static final Scanner SCANNER = new Scanner(System.in);
public static void main(String[] args) {
Tree tree = new Tree<>();
tree.insertNode(50);
tree.insertNode("30");
tree.insertNode('b');
tree.insertNode(69.3);
tree.inorderTraversal();
LOGGER.info("{}", tree.result);
}
}
For example there the first insert is an Integer , after which I try to insert a String and right there it's giving me the ClassCastException , saying that String is incomparable with Integer.

I think, the comments thoroughly elaborated that comparing any two objects is not sensibly possible. However, you can still implement such a tree by decoupling the comparison from the tree logic.
On the contrary, every client will be hit with the exact same problem you are facing right now, but some clients might have specific solutions that work for them. We'll look into that later.
First of all, Java already defines a Comparator interface that goes along with Comparable.
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
}
At the same time, let's rethink the tree interface. Basically, the requirements state that it should be able to accept just about any object, so it must have a method like
public void add(Object data);
At this point, there is no reason to use generics, since we can't actually make any restrictions. Even if there are other objects in the tree, it should still be able to accept any object.
Therefore, we could do something like
public class Tree {
private Comparator<Object> comparator;
private Node root;
public Tree(Comparator<Object> comparator) {
this.comparator = Objects.requireNonNull(comparator);
}
public void add(Object data) {
root = insertNode(root, data);
}
private void insertData(Node root, Object dataBeingInserted) {
// see below
}
}
with no major changes to the Node class except that it's not generic anymore as well. Now, when comparing two objects in the insertNode method, we simply consult the Comparator instance instead of doing the comparison ourselves.
if (comparator.compare(dataBeingInserted, root.data) < 0) {
root.left = insertNode(root.left, dataBeingInserted);
} else if (comparator.compare(dataBeingInserted, root.data) > 0) {
root.right = insertNode(root.right, dataBeingInserted);
}
A client can use this Tree implementation with a Comparator that s/he limits to the types s/he knows may occur.
public static void main(String[] args) {
Tree t = new Tree((o1, o2) -> {
if (o1 instanceof Number && o2 instanceof String) {
// numbers before strings
return -1;
}
if (o1 instanceof Integer && o2 instanceof Integer) {
return ((Integer) o1).compareTo((Integer) o2);
}
if (o1 instanceof String && o2 instanceof String) {
return ((String) o1).compareTo((String) o2);
}
throw new ClassCastException("incompatible types: " + o1.getClass().getCanonicalName()
+ ", " + o2.getClass().getCanonicalName());
});
t.add("Hello");
t.add(Integer.valueOf(1337));
}
As indicated by the ClassCastException, this solution is still not able to handle any possible type inherently. However, this Tree implementation can be used to handle every heterogeneous combination of types (as long as a client defines an appropriate Comparator).

I had a similar task in my java course. I used generics to define the tree. Then I created a tree of an interface, which is implemented by every node. The methods of the interface are then implemented in every node and this allowed me to compare and sort the objects. There is a simple example in this topic: simple heterogeneous k-ary tree in java (for creating network simulator)
I thought of the problem as of the products in a grocery store. Every product has id, brand, name, price but depending of the type of product they have to be cooled or have "best before" or need to be frozen or are not edible.

Related

Recursion of Level Order Traversal of N-ary tree

I am currently working on N-ary trees and I stumbled upon Level Order Traversal. It seemed very easy on theory , not so difficult to run on code , but now I want to step it up and add recursion so I can wrap my head around it better. The things is I am now finding it very difficult to do so. There is my code for:
- The node class
import java.util.ArrayList;
import java.util.List;
/**
* Implementation of a generic tree node containing the data and a list of children.
*
* #param <T> Generic type meant to implement reference types into the tree.
*/
public class Node<T> {
private T data;
private List<Node<T>> children;
/**
* Constructor that initializes the data and the list of children of the current node.
*
* #param data The value of the node.
*/
public Node(T data) {
this.data = data;
children = new ArrayList<>();
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public List<Node<T>> getChildren() {
return children;
}
public void setChildren(List<Node<T>> children) {
this.children = children;
}
}
-The tree class
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
/** Implementation of a generic n-ary tree. */
public class Tree<T> {
private Node root;
private List<Node<T>> nodes;
/**
* Constructor that initializes the root node of the tree.
*
* #param data The value of the root node.
*/
public Tree(T data) {
root = new Node<>(data);
}
public Node getRoot() {
return root;
}
/**
* Method that implements the Level Order Traversal algorithm. It's a left to right traverse where
* each level of the tree is being printed out. First the root , then it's children and then each
* child's children etc.
*
* #param root The root node of a tree.
*/
public String levelOrderTraversal(Node root) {
StringBuilder result = new StringBuilder();
if (root == null) {
return "";
}
result.append("\n");
Queue<Node> q = new LinkedList<>();
q.add(root);
while (!q.isEmpty()) {
int queueSize = q.size();
while (queueSize > 0) {
Node node = q.peek();
q.remove();
result.append(node.getData().toString()).append(" ");
for (int i = 0; i < node.getChildren().size(); i++) {
q.add((Node) node.getChildren().get(i));
}
queueSize--;
}
result.append("\n");
}
return result.toString();
}
/**
* This method serves to recursively move through and retrieve the nodes, so they can be printed
* out to the user.
*
* #param root The root node of the tree.
*/
private void walkThroughElements(Node root) {
if (root == null) {
return;
}
nodes.add(root);
for (Object node : root.getChildren()) {
walkThroughElements((Node) node);
}
}
/**
* Implementation of pre-order traversal of a generic tree. This traversal visit the root node
* first , prints it , then visits the whole left sub-tree (the list of every child node), prints
* every node and then traverses the right sub-tree , prints the nodes and ends the algorithm.
*
* #param root The root node of the tree.
* #return The nodes of the tree as a string.
*/
private String preOrderTraversal(Node<T> root) {
nodes = new ArrayList<>();
StringBuilder result = new StringBuilder();
walkThroughElements(root);
for (Node node : nodes) {
result.append(node.getData()).append(" ");
}
result.setLength(result.length() - 1);
return result.toString();
}
public String preOrderTraversal() {
return preOrderTraversal(root);
}
}
Is there an efficient way or does it even make sense to run this level order traversal method recursively?
This is the level order code after some changes
public String levelOrderTraversal(Node root) {
StringBuilder result = new StringBuilder();
if (root == null) {
return "";
}
result.append("\n");
Queue<Node> q = new LinkedList<>();
q.add(root);
collectNodes(root, root.getChildren());
result.append(root.getData().toString()).append(" ");
result.append("\n");
return result.toString();
}
It gives the error on the line where collectNodes is called.
This is what collectNodes() looks like.
private void collectNodes(Node<T> node, List<Node<T>> nodes) {
nodes.add(node);
for (Object child : node.getChildren()) {
collectNodes((Node) child, nodes);
}
}
You can solve those iteration problems using iteration (e.g. via a stack) or recursion. Let's use a method that gathers nodes much like your walkThroughElements():
Depth-first
Recursion
//add the node and then go deeper
void collect(Node node, Collection<Node> nodes) {
nodes.add(node);
for(Node child : node.getChildren()) {
collect(child, nodes);
}
}
Iteration
class Level {
final Node node;
Iterator<Node> childItr;
//constructor, setters, getters
}
void collect(Collection<Node> nodes) {
Stack<Level> levels = new Stack<>();
nodes.add(root);
levels.push(new Level(root, root.getChildren().iterator()));
while( !levels.isEmpty() ) {
Level currentLevel = levels.peek();
//remove the current level as it doesn't have any more children
if( !currentLevel.childItr.hasNext() ) {
levels.pop();
} else {
//get the next child and add it to the result
Node child = currentLevel.childItr.next();
nodes.add(child);
//go down to the child's level
levels.push(new Level(child, child.getChildren().iterator())
}
}
}
Breadth-first
Recursion
//add the children first (i.e. the entire level) and then go deeper
void collectChildren(Node node, Collection<Node> nodes) {
for(Node child : node.getChildren()) {
nodes.add(child);
collectChildren(child, nodes);
}
}
//special case: root node
void collect(Collection<Node> nodes) {
nodes.add(root);
collectChildren(root, nodes);
}
Iteration
void collect(Collection<Node> nodes) {
Queue<Node> nodesToProcess = new LinkedList<>();
nodesToProcess.add(root);
while( !nodesToProcess.isEmpty() ) {
Node node = nodesToProcess.poll();
nodes.add(node);
nodesToProcess.addAll(node.getChildren());
}
}
As you can see, recursion is easier on depth-first than breadth-first but easy to read anyway. Recursion will use the call stack to maintain state so it takes up (non-heap) memory and also has limits to its depth (depends on how much memory there is but the infamous StackOverflowException would tell you there's either a bug or the tree is too deep).
Iteration is easier with breadth first and requires additional constructs like a stack or a queue. It requires some heap memory for those constructs and may be faster due to some optimizations but in general I'd not bother about performance differences here as they should only manifest themselves for really large trees - and in that case recursion might hit the call stack limit already.
Using recursion typically will be slower than iteration and use more stack space as well.But Yes,this can also be solved using recursive way(DFS approach).
For your reference : https://leetcode.com/problems/binary-tree-level-order-traversal/discuss/33562/Java-1ms-DFS-recursive-solution-and-2ms-BFS-iterative-solution
The proper way is to do it in a lazy way
// assume not null Node::data (if so, wrap into Optional or similar)
static <T> Stream<T> levelOrderTraversal(Node<T> tree) {
final List<Node<T>> fifo = new ArrayList<>();
if(tree != null)
fifo.add(tree);
final Function<T, T> next = o -> {
if(fifo.isEmpty())
return null; // End Of Stream
Node<T> x = fifo.remove(0);
System.out.println("++++++ " + x.data);
if(x.children != null)
fifo.addAll(x.children);
return x.data;
};
return Stream.iterate(null, next::apply).skip(1).takeWhile(Objects::nonNull);
}
for example
++++++ foo
foo
++++++ bar1
bar1
++++++ bar2
bar2
++++++ par11
par11
++++++ par12
par12
++++++ par21
par21
++++++ par22
par22
++++++ subA
subA
++++++ subB
subB
where you only mantain the intermediate traversing level (not the whole tree).
For example, if you log every node expansion and filter the tree, not all nodes are logged
levelOrderTraversal(tree)
.takeWhile(x -> !x.equals("bar2"))
.forEach(System.out::println);
only expand the foo, bar1 and bar2 nodes
++++++ foo
foo
++++++ bar1
bar1
++++++ bar2

Constructor Node cannot be applied to given types while working with nodes

I am working on a code that puts new elements on MyStack if they are unique. I had to copy and paste the node starting code, so I'm having a bit of trouble with an issue. I keep getting two error messages, even after trying various workarounds and I'm not really understanding why. I've even tried using some helper functions I've previously made that have worked before so I'm extra confused.
The two errors I consistently get are:
-cannot infer type arguments for MyStack.Node (actual and formal arguments differ in length)
-constructor node cannot be applied to given types. Required, no arguments, found: anything,
Here's my code:
public class MyStack<Anything>
{
private Node first, last;
private class Node<Anything>
{
Anything item;
Node next;
}
public boolean contains(Anything value)
{
for (Node curr = first; curr != null; curr = curr.next)
{
if (value.equals(curr.item)) {
return true;
}
}
return false;
}
public void add(Anything value)
//method that adds a new value to the end of the list
//COMPLETE
{
Node temp = first;
while(temp.next!=null){ //finds the end
temp=temp.next;
}
temp.next=new Node(value, null); //assigns new value
}
public void enqueue(Anything info){
if (this.contains(info)==true) { //if the info is already present
System.out.println("the stack already contains this value");
return;
}
//if we actually need to add the info
if (first == null) { //if there is nothing in the stack
Node temp= first;
first = new Node<>(info,temp);
first = temp;
return;
}
if (first != null) { //if there is already stuff
Node temp = first;
while (temp.next == null)
{ Node newNode= new Node<>(info, temp);
temp.next = newNode;
}
return;
}
}
}
As #Andreas already pointed out, Node needs a constructor.
There are a few other flaws in your Code:
Use Generics
With your Code, you can only store Objects of the class Anything, what strongly limits its reusability. Use a generic instead and you can reuse this class for many more purposes.
Linked List
I suggest, you use the paradigm of a double-linked-list. That way you do not need to find the last Node to add something to the Stack. Node now has a pointer to its previous and next element.
Use the last Object
You have the object last but never use it. To find out, whether the current object is the last one you compare the value to null. This has the effect, that storing a null value will break your List. Instead compare to the Object last, this object is unique and guarantees you, that you are at the end of the list. Both first and last are Nodes that do not contain a value and are simply used to mark the start/end of your List.
Adding elements
Using the changes above, the code in the Method enqueue(T value) becomes significantly simpler: You just check whether contains(value) and decide whether you add the value to the List or not.
All these changes applied result in following code:
public class MyStack<T extends Object> {
private Node first, last;
public MyStack() {
first = new Node(null, null, null);
last = new Node(null, null, first);
first.next = last;
}
private class Node {
T item;
Node next;
Node previous;
public Node(T item, Node next, Node previous) {
this.item = item;
this.next = next;
this.previous = previous;
}
}
public boolean contains(T value) {
for (Node curr = first.next; curr != last; curr = curr.next) {
if (value.equals(curr.item)) {
return true;
}
}
return false;
}
/**
* method that adds a new value to the end of the list
*/
public void add(T value)
{
Node secondLast = last.previous;
Node added = new Node(value, last, secondLast);
secondLast.next = added;
last.previous = added;
}
/**
* only adds value if it is not already contained by the Stack
*/
public void enqueue(T value) {
if (this.contains(value) == true) { // if the info is already present
System.out.println("the stack already contains this value");
}
else {
add(value);
}
}
public static void main(String[] args) {
MyStack<String> test = new MyStack<>();
test.add("foo");
test.add("bar");
test.add("baz");
System.out.println(test.contains("bar"));
System.out.println(test.contains("new"));
test.enqueue("baz");
test.enqueue("MyStack");
}
}
Naming
As you may have noticed, in my explanation I called this class a List. This is because it fulfills more of the characteristics of a List. A Stack usually only provides the methods push to put something at the top of the Stack and pop to remove and return the topmost Object. Optionally peek can return the topmost Object, without removing it from the Stack.
Also consider renaming the method enqueue: enqueue is used in Queues (obviously) and Queues do not forbid to add two equal Objects. So the name is misleading. I would call this method something like addIfNotContaining.
In my Opinion you should name this class to be a List and add a method get(int i) to get a specific element at a position. Naturally adding some other methods like size ect. to comply with a standard List. But I assume you already had, but did not post them because they are not related to your problem.
Multithreading
This Class is far from threadsave. But I let you figure out yourself how to make it threadsave if needed.

How to set up a constructor for a Iterator class with a stack?

I need help setting up this constructor for my Iterator class. The directions are as follows:
The constructor should create a new stack and push its node parameter onto it, followed by
all left children accessible from the parameter. Consider a case in which the tree consists
only of left children (essentially a linked list). The node with the highest value (root) would
be pushed first and be on the bottom of the stack, followed by its left child just above it in the
stack, followed by its left child, and so on until the leaf, which would contain the lowest value
in the tree. When popping nodes from the stack, they would contain values from lowest to
highest… an in-order traversal.
I am not sure how to create a new stack with the node in the parameter being a type BSTNode type.
Here is my code:
public static class Iterator<E>
{
private Stack<BSTNode<E>> stack;
public Iterator(BSTNode<E> node)
{
}
public boolean hasNext()
{
if(stack.peek() != null)
{
return true;
}
else
{
return false;
}
}
public E next()
{
stack.pop();
E value;
value = (E) stack.pop();
return value;
}
}
As of right now, just ignore the other two methods, I just need help with the Iterator method. I'll figure those out later. Thank you.
I found out my problem was in a different class and method. I set it up as this and I want to know if this is the correct way of doing it.
The instructions for this method is
to create and return an instance of the static nested Iterator class that will be used to iterate through the elements in the tree. The tree's root should initially be passed to the iterator constructor.
Here is the following code I did for that method:
public Iterator<E> iterator()
{
return new Iterator<>(root);
}
root is the top of the binary search tree. It is in that class as a private variable.
Here's how I set it up.
This is just the public that is above the class. Not inside the class. I just return a new Iterator with root being the top value.
public Iterator<E> iterator()
{
return new Iterator<>(root);
}
Then inside the class below it, I create a new stack and have that stack push the nodes and the nodes to the left of it as long as it isn't null.
public static class Iterator<E>
{
private Stack<BSTNode<E>> stack;
public Iterator(BSTNode<E> node)
{
this.stack = new Stack<>();
while (node != null)
{
stack.push(node);
node = node.left;
}
}
public boolean hasNext()
{
return !stack.isEmpty();
}
public E next()
{
BSTNode<E> goodDays = stack.pop();
E result = goodDays.data;
if (goodDays.right != null)
{
goodDays = goodDays.right;
while (goodDays != null)
{
stack.push(goodDays);
goodDays = goodDays.left;
}
}
return result;
}
}

Implementation of DFS using iterative approach in java

I have seen a lot of implementations of DFS using a boolean variable named visited, which I don't wish to use in my code. While considering a scene where we have a Node class that holds the reference to left and right nodes corresponding to its children and data which can be any Object, can this method be applicable to Binary Trees to calculate dfs ? I have a scenario where I don't have a adjacency list or matrix.
Is the following code a good implementation of DFS ? Is the time complexity of the code O(n) ?
public void dfsForTree(BSTNode root) {
Stack<BSTNode> s = new Stack<BSTNode>();
BSTNode node;
if (root == null) {
return;
}
s.push(root);
while (!s.isEmpty()) {
node = s.pop();
System.out.println(node.getData());
if (node != null) {
if (node.getRight() != null) {
s.push(node.getRight);
}
if (node.getLeft != null) {
s.push(node.getLeft);
}
}
}
}
BSTNode class implementation:
public class BSTNode {
private BSTNode left;
private BSTNode right;
private int data;
/* Constructor */
public BSTNode(int n) {
left = null;
right = null;
data = n;
}
/* Function to set left node */
public void setLeft(BSTNode n) {
left = n;
}
/* Function to set right node */
public void setRight(BSTNode n) {
right = n;
}
/* Function to get left node */
public BSTNode getLeft() {
return left;
}
/* Function to get right node */
public BSTNode getRight() {
return right;
}
/* Function to set data to node */
public void setData(int d) {
data = d;
}
/* Function to get data from node */
public int getData() {
return data;
}
A sure tell of an iterative tree walk is it requires an "up" link on a node (or saves them) to be able to backtrack. You do just this - only saving not "up" links but directly next links to go after backtracking. On the other hand, there are no interdependencies between steps. See Is this function recursive even though it doesn't call itself? for how to distinguish iterative and disguised recursive.
Also see Iterative tree walking for an overview of the algorithms.
Now, for computational complexity. The principle can be found at Big O, how do you calculate/approximate it?.
You do:
process every node
exactly once
push & pop nodes from the stack
each node is also pushed and popped exactly once
So, indeed, it's O(N).

Inserting an element to a binary tree

This is my implementation of binary Node class:
public class BinaryNode{
int element;
BinaryNode left;
BinaryNode right;
BinaryNode(int theElement,BinaryNode lt,BinaryNode rt){
element=theElement;
left=lt;
right=rt;
}
BinaryNode(int theElement){
this(theElement,null,null);
}
}
Here's my insert method in binaryTree class
public class BinaryTree {
private BinaryNode root;
public BinaryTree(){
root= null;
}
BinaryTree(int nodeValue){
root=new BinaryNode(nodeValue);
}
public void insert(BinaryNode node,int x){
if(node==null){
node=new BinaryNode(x);
}
else if(node.element<x){
insert(node.left,x);
}
else if (node.element>x){
insert(node.right,x);
}
else
System.out.println("Duplicates not allowed");
}
I have two questions.
1) how can I insert elements to this BinaryTree class and thereby create a tree.
public static void main (String args[]){
BinaryTree t=new BinaryTree();
t.insert(t.root,5);
}
But after inserting 5 how can I call on insert method to add integers like 10,12,78,...
2) Also when I looked up at some code for inserting to binary trees I found this code .
/**
Inserts the given data into the binary tree.
Uses a recursive helper.
*/
public void insert(int data) {
root = insert(root, data);
}
/**
Recursive insert -- given a node pointer, recur down and
insert the given data into the tree. Returns the new
node pointer (the standard way to communicate
a changed pointer back to the caller).
*/
private Node insert(Node node, int data) {
if (node==null) {
node = new Node(data);
}
else {
if (data <= node.data) {
node.left = insert(node.left, data);
}
else {
node.right = insert(node.right, data);
}
}
return(node); // in any case, return the new pointer to the caller
}
The code looks similar to mine, but why use a helper method insert() as well?What's the purpose of it?
Can someone please solve help me to understand this
Inserting an element in a binary tree should require only the tree and the element as input. The tree itself should determine which node should be updated. This is achieved by means of a recursive function which starts from root: this is the helper function, which acts on a node.
The first problem is that you won't be able to access t.root directly because it's private. You either need a getter
public BinaryNode getRoot() {
return this.root;
}
or make root public
The helper method is used, so the new root of the BinaryTree can be determined. And because the root should not be returned to the caller. But since it's easier to insert something into a binary tree recursivley the private method is used to do that.
You would use the methods like this:
public static void main(String[] args) {
BinaryTree t = new BinaryTree(5); //Create a new tree with one item
t.insert(12); // Assuming that you used the implementation with the helper method
t.insert(3); //
t.insert(t.getRoot(),12); // Assuming you used your implementation
t.insert(t.getRoot(),3); //
}

Categories

Resources