A similar question was asked here on SO: [BST with duplicates
User Sazzadur Rahaman posted the three scenarios for accomplishing BST with duplicates, but I need to know how to implement the third situation he mentioned which looks something like this:
Assume we are using the input: "RABSAB."
The tree with the counter variable in brackets would look like this:
R(1)
/ \
/ \
A(2) S(1)
\
\
B(2)
So basically, I want each element(node) to have a specific counter variable.
Is what I'm trying to do possible to implement in just my insert method? Or would I need some sort of other method in my BSTTree/Node class?
****EDIT** my BSTNode class, made changes from Compass's recommendations.
public class BSTNode {
String key;
BSTNode left, right;
int count=1;
public BSTNode(String key) {
this.key = key;
}
public void insert(String x) {
if (x.compareTo(this.key) > 0) {
if (right != null) {
right.insert(x);
} else {
right = new BSTNode(x);
}
} //Go right
else if (x.compareTo(this.key) < 0) {
if (left != null) {
left.insert(x);
} else {
left = new BSTNode(x);
}
} //Go left
else if (x.compareTo(this.key) == 0) {
count++;
} // It's a duplicate, update the count
}
}
EDIT, updated my output incrementing a counter like that doesn't seem to give the correct output, I'm inserting "RABSAB" and trying to count the number of duplicate nodes.
Inserting as follows:
String line = "R A B S A B";
String[] words = line.split(" ");
for (int i = 0; i < words.length; i++) {
t1 = t1.Insert(words[i], t1);
System.out.println(words[i] + " : " + t1.count);
}
I get the following output:
R : 1
A : 1
B : 1
S : 1
A : 1
B : 1
Thanks for your time everyone.
Inside your BSTNode class, outside of the Insert method, declare int counter = 1;
Then, within your else, you would do counter++;
So on creating a node, you'd have 1 of the element (since you created it, you know it exists).
As additional keys that match are found, you would increment the counter.
I do feel like there is an implementation issue with your Insert method (which should be lower-case insert to follow conventions). You're passing a Node t which I assume is the child of the Node. That should probably be declared as a field just like counter is.
Sample Pseudocoded Integer-based Node with Duplicates
public class Node() {
int value; //node's value
int counter = 1; //how many items of same type at node
Node leftChild = null;
Node rightChild = null;
public Node(int value) {
this.value = value;
}
public void insert(int newValue) {
if(newValue > value) {
if(rightChild != null)
rightChild.insert(newValue); //we tell the child to deal with it
else
rightChild = new Node(newValue); //we make it a child
}
else if(newValue < value) {
if(leftChild != null)
leftChild.insert(newValue);
else
leftChild = new Node(newValue);
}
else if(newValue == value) {
counter++; // we found a duplicate, increase count
}
}
}
This will work.
else {
t.count++;
}
return t;
Related
I am creating a program that inserts a character (number/letter) into a binary tree. So far, I'm able to produce an output but it's not what I expected. These are the problems I'm encountering:
The insert method is not able to print the correct height of the tree. I am not sure where I should insert my height++; statement to get the correct output.
The insert method is only able to add nodes to the right.
Expected Output: ht=3 [K=3 L=[K=1 R=[K=2]] R=[K=5 L=[K=4]]]
My Output: ht=4 [K=3 R=[K=1 R=[K=2 R=[K=5 R=[K=4]]]]
(all nodes are only added to the right 'R')
Here are my classes for reference:
Main Class
BST<Character> bst = new BST<>();
bst.insert('3');
bst.insert('1');
bst.insert('2');
bst.insert('5');
bst.insert('4');
System.out.println("ht=" + bst.height + " " + bst.toString());
BST Class - where the insert method is declared
public class BST<T> extends BT<T> {
// insert() method
public void insert(char k)
{
if (root == null) {
root = new BTNode(k);
return;
}
BTNode<T> n = root;
BTNode<T> p = null; // parent
while (n != null) {
p = n;
if (k < n.value) {
n = n.left;
} else {
n = n.right;
}
}
if (k < p.value) {
p.left = new BTNode(k);
} else {
p.right = new BTNode(k);
height++; // adds 1 to height when a new level is made
}
}
}
BTNode Class
public class BTNode<T> {
T info;
int value, level;
BTNode<T> left, right;
public BTNode(T el) {
this(el, null, null);
}
public BTNode(T el, BTNode<T> l, BTNode<T> r) {
info = el;
left = l;
right = r;
}
}
BT Class - where the toString method is declared
public class BT<T> {
BTNode<T> root = null;
int height = 0;
public BT() {
BTNode<T> node = new BTNode("");
}
// other methods
// toString()
public String toString() {
return toString(root);
}
public String toString(BTNode<T> n) {
String s = "";
if (n == null) {
return "";
}
if (n != null) {
s = "[K=" + n.info;
if (n.left != null) {
s = s + " L=" + toString(n.left) + "]";
}
if (n.right != null) {
s = s + " R=" + toString(n.right) + "]";
}
}
return s;
}
}
Hope you can help me out, thanks!
You have quite a few issues in your code. I'll list a few immediate items but you really will need to learn to use an interactive debugger and unit testing to resolve the sorts of issues you are seeing.
You refer to the value field in BTNode in your comparison but it is never set. You should really be referring to info (which is the actual data in the node).
But given info is a generic type you can't use standard comparison operators. Instead you'll need to define it as <T extends Comparable<T>> and then use n.info.compareTo(k) > 0.
The key passed into insert should also be of type T
Which means the other classes also need to ensure T extends Comparable.
Height is only incremented when nodes are added to the right which makes no sense.
Height needs to be increased only when a node is inserted further from the root than the current maximum. Something like the following:
int depth = 0;
while (n != null) {
depth++;
p = n;
...
}
depth++;
if (depth > height)
height = depth;
You should get used to making your fields private and accessing them through getters. In your case a compareValue method would likely make sense.
(java)
I have class called Node, which has following fields:
value (integer)
connectedNodes (array of Node objects, always has same size = 2)
combination (object of Combination class)
Combination class has one field called messageContext, let's just say that it's a message which needs to be shown on the screen when something happens (described later).
Also, we have one Tree object, which has only one field: root (Node object)
Now, let's say that we have one String called combinationStr = "1121". Now, we use Tree's method called addCombination:
public void addCombination(Combination finalCombination, Node current, String combination, int counter) {
if(counter==combination.length()) {
return;
}
int value = combination.charAt(counter)-48;
if(current.connectedNodes[value-1]==null) {
current.connectedNodes[value-1] = new Node(value);
}
if(counter==combination.length()-1) {
current.combination = finalCombination;
return;
}
addCombination(finalCombination,current.connectedNodes[value-1],combination,counter+1);
}
finalCombination object is an object that is going to be assigned to the last Node's combination field, added to the Tree for one combinationStr. So, we use this function to create the Tree-like structure that has path: -1 (root) -> 1 -> 1 -> 2 -> 1
When we come to the last one, traversing the Tree, we should see message appear. This is the messageContext of finalCombination.
Okay so, now let's use while(true) loop that will let us input a number, which will be like a path-chooser. If we input 1, we will go to node 1 and have other options to choose.
While loop looks like this:
Scanner scanner = new Scanner(System.in);
Node currentNode = tree.root;
while(true) {
for(Node node: currentNode.connectedNodes) {
if(node!=null) {
System.out.print(node.value + " ");
continue;
}
System.out.print("nullnode ");
}
System.out.println("");
if(currentNode.combination!=null) {
System.out.println(currentNode.combination.messageContext);
}
if(currentNode.connectedNodes[0]==null && currentNode.connectedNodes[1]==null) {
currentNode = tree.root;
System.out.println("root");
}
int x = scanner.nextInt();
currentNode = tree.takeStep(currentNode,x);
}
So, what are we doing here is actually printing the value of current Node, then printing values of Node's we can go to. If Node doesn't exist, we print nullnode.
The takeStep() method looks like this:
public Node takeStep(Node current, int value) {
if(current.connectedNodes[value-1]!=null) {
return current.connectedNodes[value-1];
}
return this.root;
}
It just checks if there is a node we want to go to and returns that node, if it does. If it doesn't exist, it will return us to root.
But, what's the problem with this code ?
Well, look at the whole main class:
Tree tree = new Tree(new Node(-1));
String[] combination = {"1121","11","2212"};
for(String s: combination) {
Combination tempCombination = new Combination();
tempCombination.messageContext = s + " ova poruka";
tree.addCombination(tempCombination,tree.root,s,0);
tree.traverse(tree.root);
System.out.println("END");
}
Scanner scanner = new Scanner(System.in);
Node currentNode = tree.root;
while(true) {
System.out.println(currentNode.value);
for(Node node: currentNode.connectedNodes) {
if(node!=null) {
System.out.print(node.value + " ");
}
else {
System.out.print("nullnode ");
}
}
int x = scanner.nextInt();
if(currentNode.combination!=null) {
System.out.println(currentNode.combination.messageContext);
if(currentNode.connectedNodes[0]==null && currentNode.connectedNodes[1]==null) {
currentNode = tree.root;
break;
}
}
currentNode = tree.takeStep(currentNode,x);
}
When we enter number x, we will call takeStep and check if that node exists connected to current one. But the problem is: When we input 1, it prints everything normally, when we input 1 again, it prints everything normally, when we input 2, it prints everything normally... but when we input 1 again, it says there are 2 nullnodes, and for some reason it doesn't change to root. Can anyone help me please? Here are the full classes:
NODE:
public class Node {
int value;
Node[] connectedNodes = {null,null};
Combination combination;
public Node(int value) {
this.value = value;
this.combination = null;
}
}
TREE:
public class Tree {
Node root;
public Tree(Node root) {
this.root = root;
}
public void addCombination(Combination finalCombination, Node current, String combination, int counter) {
if(counter==combination.length()) {
return;
}
int value = combination.charAt(counter)-48;
if(current.connectedNodes[value-1]==null) {
current.connectedNodes[value-1] = new Node(value);
}
if(counter==combination.length()-1) {
current.combination = finalCombination;
return;
}
addCombination(finalCombination,current.connectedNodes[value-1],combination,counter+1);
}
public void traverse(Node current) {
System.out.print(current.value+ " ");
for(Node node: current.connectedNodes) {
if(node!=null) {
traverse(node);
}
}
}
public Node takeStep(Node current, int value) {
if(current.connectedNodes[value-1]!=null) {
return current.connectedNodes[value-1];
}
return this.root;
}}
COMBINATION:
public class Combination {
String messageContext;
}
Can you please help me ? I just want to reset to root when it hasn't anywhere to go else ? Thank you in advance!
I ran your code and found out that you are storing the message context in the parent node instead of the actual node which marks the end of the combination. So I changed this piece of code in addCombination.
public void addCombination(Combination finalCombination, Node current, String combination, int counter) {
if (counter == combination.length()) {
//Storing at the original node.
current.combination = finalCombination;
return;
}
int value = combination.charAt(counter) - 48;
if (current.connectedNodes[value - 1] == null) {
current.connectedNodes[value - 1] = new Node(value);
}
addCombination(finalCombination, current.connectedNodes[value - 1], combination, counter + 1);
}
And changed following in the main code.
while (true) {
System.out.println(currentNode.value);
//Moved it up now as the node it self has the message context.
if (currentNode.combination != null) {
System.out.println(currentNode.combination.messageContext);
if (currentNode.connectedNodes[0] == null && currentNode.connectedNodes[1] == null) {
currentNode = tree.root;
continue;
}
}
for (Node node : currentNode.connectedNodes) {
if (node != null) {
System.out.print(node.value + " ");
} else {
System.out.print("nullnode ");
}
}
int x = scanner.nextInt();
currentNode = tree.takeStep(currentNode, x);
}
Now try the code it is resetting to root as expected.
I've had a go, and it works for the left subtree but not the right.
I'm close but my logic is wrong, can anyone help correct and explain the logic to this.
public static MyNode preOrderNumbering(MyNode n) {
if (n != null) {
n.obj = 0; // Set root decoration to 0;
preOrderHelper(n, 1); // Set decorations according to preorder.
}
return n;
}
public static MyNode preOrderHelper(MyNode n, int counter) {
if (n != null) {
if (n.left != null) {
n.left.obj = counter++; // Set the left object decoration to current count + 1;
preOrderHelper(n.left, counter);
}
if (n.right != null) {
n.right.obj = counter++; // Set the left object decoration to current count + 1;
preOrderHelper(n.right, counter);
}
}
return n;
}
Before:
After:
You need to update the counter with everything that's discovered on the left before going to the right.
Something like this:
public static int preOrderNumbering(MyNode n, int count){
if(n != null){
n.obj = ++count;
count = preOrderNumbering(n.left, count);
count = preOrderNumbering(n.right, count);
}
return count;
}
You're passing counter by value, not by reference (because that's how Java works), so when the recursion unwinds, so will the counter.
You could update the counter by returning the current value from the recursive call.
So here is the Node class:
public class Node
{
private int _info;
private Node _left;
private Node _right;
public Node()
{
//this._info = Integer.MIN_VALUE;
this._left = null;
this._right = null;
}
public int getInfo()
{
return _info;
}
public void setInfo(int _info)
{
this._info = _info;
}
public Node getLeft()
{
return _left;
}
public void setLeft(Node _left)
{
this._left = _left;
}
public Node getRight()
{
return _right;
}
public void setRight(Node _right)
{
this._right = _right;
}
}
How I create the tree:
public class BalancedBinaryTree
{
private ArrayList<Integer> _numbers;
private Node _root;
public BalancedBinaryTree(ArrayList<Integer> numbers)
{
this._numbers = new ArrayList<>();
this._numbers.addAll(numbers);
Collections.sort(this._numbers);
this._root = new Node();
this.create(this._root, 0, this._numbers.size());
}
private void create(Node tree, int i, int j)
{
if (i < j)
{
int m = i + (j - i) / 2;
tree.setInfo(this._numbers.get(m));
tree.setLeft(new Node());
create(tree.getLeft(), i, m);
tree.setRight(new Node());
create(tree.getRight(), m + 1, j);
}
}
This method computes the depth:
public static int getDepth(Node node)
{
if (node == null)
{
return 0;
}
else
{
int max = 0;
if (getDepth(node.getLeft()) > getDepth(node.getRight()))
{
max = getDepth(node.getLeft());
}
else
{
max = getDepth(node.getRight());
}
return max + 1;
}
}
And these two combined should print the tree by its levels:
public static void printLevel(Node node, int levelToDisplay, int currentLevel)
{
if (node != null)
{
printLevel(node.getLeft(), levelToDisplay, currentLevel);
if (currentLevel == levelToDisplay)
{
System.out.print(node.getInfo() + " ");
}
currentLevel++;
printLevel(node.getRight(), levelToDisplay, currentLevel);
}
}
public static void printLevels(Node node)
{
for (int i = 0; i < getDepth(node); i++)
{
System.out.println("Level :" + i);
printLevel(node, i, 0);
System.out.println();
}
}
In a test class I have:
testNumbers.add(15);
testNumbers.add(20);
testNumbers.add(25);
testNumbers.add(30);
testNumbers.add(35);
testNumbers.add(40);
testNumbers.add(45);
BalancedBinaryTree tree = new BalancedBinaryTree(testNumbers);
BalancedBinaryTree.printLevels(tree.getRoot());
And I get this output:
Level :0
0 15 20 30
Level :1
0 0 25 0 35 40
Level :2
0 0 0 45
Level :3
0
I should get
Level :0
30
Level :1
20 40
Level :2
15 25 35 45
What's wrong with the getDepth method because it seems that it returns 4 levels instead of 3?
Why are there additional nodes? (those zeroes)
I'm pretty sure I solved the problems but I will need an explanation for the following:
This is the modified printlevel method:
public static void printLevel(Node node, int levelToDisplay, int currentLevel)
{
if (node.getLeft() != null && node.getRight() != null)
{
printLevel(node.getLeft(), levelToDisplay, currentLevel+1);
if (currentLevel == levelToDisplay)
{
System.out.print(node.getInfo() + " ");
}
printLevel(node.getRight(), levelToDisplay, currentLevel+1);
}
}
As you can see I test now if the current node has childs instead of checking if the current node exists and this is why those zeroes appeard because the traversal reached the leafs that had no info assigned on their right and left childs.
The thing I want to understand is the difference between incrementing currentLevel and then passing it to the call of printLevel and simply passing currentLevel+1 to the call. Shouldn't it be the same thing ?
And the getDepth function:
public static int getDepth(Node node)
{
if (node.getLeft() == null && node.getRight() == null)
{
return 0;
}
else
{
int max = 0;
if (getDepth(node.getLeft()) > getDepth(node.getRight()))
{
max = getDepth(node.getLeft());
}
else
{
max = getDepth(node.getRight());
}
return 1 + max;
}
}
Same thing here: traversal reached the leafs and got one more call for its childs thus returning one additional level so again, the solution is to test if the current node has childs instead of checking if the current node exits.
What's wrong with the getDepth method because it seems that it returns 4 levels instead of 3?
From your print method it seems, that you number the levels from 0 to n (the root of a tree beeing 0). Your getDepth method however will never return 0.
Two things: if (node != null) this check does not seem to make very much sense. Null does not seem to be an allowed input (as the root is constructed on construction of a Tree). If this is the case (and you do want to check it) an exception might be more appropriate.
The main problem seems to be this: return max + 1;
So the minimal value returned is 0 + 1, which is 1.
As a small sidenote: I would save the values of the two recursive calls of getDepth, it would greatly increase performance.
Also, if you do use short variable names such as i, m or j (in a non-loop index kind of way) it would be helpful to document their meaning.
And conserning your first question:
tree.setLeft(new Node());
What would be the value of this Node as of now? And what will happen if the i < j codition in the recurive call will not pass? If you can answer those questions, you should be able to fix the code yourself.
I have 1000 numbers and I make a binary tree and sort the tree. It prints 0 to 100 and the other 899 numbers are duplicates. How can I keep track of the frequency of each number. Like for example the number 28 appears 9 times. Keeping a count somehow. I've been working with one method but Idk if it's close or not. I'll post that method at the end.
public class bigTree {
int data;
int frequency;
bigTree Left, Right;
public bigTree makeTree(int x) {
bigTree p;
p = new bigTree();
p.data = x;
p.Left = null;
p.Right = null;
return p;
}
public void setLeft(bigTree t, int x) {
if (t.Left != null) {
// setLeft(t.Left, x);
System.out.println("Error");
}
else {
t.Left = makeTree(x);
}
}
public void setRight(bigTree t, int x) {
if (t.Right != null) {
//setRight(t.Right, x);
System.out.println("Error");
} else {
t.Right = makeTree(x);
}
}
public void insertLocation(bigTree tree, int v) {
// if (tree.data == v) {
//findDuplicate(v);
//}
if (v < tree.data) {
if (tree.Left != null){
insertLocation(tree.Left, v);
}
else {
setLeft(tree, v);
}
}
if (v > tree.data) {
if (tree.Right != null){
insertLocation(tree.Right, v);
} else {
setRight(tree, v);
}
}
}
public void sort(bigTree t) {
if (t.Left != null) {
sort(t.Left);
}
System.out.println(t.data + " freq = " + frequency);
if (t.Right != null) {
sort(t.Right);
}
}
public void dealArray( String[] x) {
int convert;
bigTree tree = makeTree(Integer.parseInt(x[0]));
for (int i = 1; i < x.length; i++){
//convert = Integer.parseInt(x[i]);
insertLocation(tree, Integer.parseInt(x[i]));
findDuplicate(Integer.parseInt(x[i]));
} sort(tree);
}
----A method that I thought could work but isnt----
public void findDuplicate(int number) {
bigTree tree, h, q;
tree = makeTree(number);
//while (//there are #'s in the list) { //1st while()
h = tree;
q = tree;
while (number != h.data && q != null) { //2nd while()
h = q;
if (number < h.data ) {
q = q.Left;
} else {
q = q.Right;
}
} //end of 2nd while()
if (number == h.data) {
//h.frequency++;
System.out.println("Duplcate: " + number + "freq = " + h.frequency++);
}
else {
if (number < h.data) {
setLeft(h,number);
}
else {
setRight(h, number);
}
}
//} // End of 1st while()
sort(h);
}
PrePost:
If you need to use the binary tree search, it appears that your code above is creating a new tree for each element that it is looking for. Instead you should have a single tree that you search, add / update for each element that you are looking for.
Previous Post:
Although #Woot4Moo's answer will work, there is the overhead of creating the count and incrementing. I would suggest using Guava's ListMultimap class to handle all this for you.
ListMultimap
ListMultimap<Integer, Integer> mymap;
for (Integer value : values){
mymap.put(value, value);
}
Map<Integer, List<Integer>> asmap = mymap.asMap();
for (Entry<Integer, List<Integer>> entry : asmap.entrySet()){
System.out.println(String.format("Value %d occurred %d times", entry.getKey(), entry.getValue().size());
}
So the first thing you need to do is select a data structure that can represent a value and the number of duplicates it has. The first thing that comes to mind is a Map
Map<Integer,Integer> // Keeps track of the integer that is the key and the count which is the value
So what needs to happen as you parse out your array (or whatever structure you are reading this values from) is do a check on the Map like so:
myMap.contains(integerToCheck);
if contains returns true you need to increment the value stored in myMap for that key. Else you need to insert a new key that uses integerToCheck and a new value of 1 .
Then to print those values you would do the following:
for(Map.Entry<Integer,Integer> entry: myMap.entrySet())
{
System.out.println(entry.getKey + " : " + entry.getValue());
}
I Think your heading in the right direction.
1) You need to sort your elements. There is a number of ways to do this.
enter link description here
2) You need to find the duplicate elements. This can be done by comparing ith element with i+1 element. You can store your answers in a Map.