I'm working on an implementation of TreeMap(called MyTreeMap) and I'm having a lot of trouble with the put method. I was hoping someone could look at my code and point me in the right direction as to where things start to go wrong.
public class MyTreeMap<K extends Comparable<? super K>,V> extends AbstractMap<K,V> {
K key;
V value;
int height;
MyTreeMap<K,V> left,right;
int size;
public V put(K key, V value) {
int compareValue = this.key.compareTo(key);
if(!this.containsKey(key)) {
if(this.key == null) {
this.key = key;
this.value = value;
}
if(this.isLeaf() || this.isEmpty()) {
if(this.key.compareTo(key) > 0)
this.left = new MyTreeMap<K,V>(key,value,null,null);
else
this.right = new MyTreeMap<K,V>(key,value,null,null);
if(left.height > right.height + 1 || right.height > left.height + 1)
restructure(this);
this.size++;
setHeight();
return null;
}
else {
if(compareValue > 0)
return this.left.put(key, value);
else
return this.right.put(key, value);
}
}
else {
if(compareValue == 0) {
V temp = this.value;
this.value = value;
return temp;
}
else if(compareValue < 0)
return this.right.put(key, value);
else
return this.left.put(key, value);
}
}
I think your logic is a bit inside-out, and as a result is a lot more complicated than it needs to be: the top level if should probably be looking at the compareValue, not doing the containsKey check.
Logic should be:
if compareValue==0 then this means you have found the right key, so just update the value and return
otherwise check the left or right branch as appropriate (depending on sign of compareValue):
if the branch is null, then turn it into a new TreeMap branch containing your key and value (you are now done)
otherwise (branch is not null), call put recursively on this branch. If you like you can do rebalancing logic after this call.
P.S. I suggest not allowing null keys in a TreeMap, it will make your life simpler, and will avoid the need to do null checks on keys
Related
I'm learning the BST recursive construction and found that the insert method does not use return keyword when implementing recursion, but the contains method do use the return key word. Can anybody explain this to me? Many thanks!
static class BST {
public int value;
public BST left;
public BST right;
public BST(int value) {
this.value = value;
}
public BST insert(int value) {
// Write your code here.
// Do not edit the return statement of this method.
if (value < this.value) {
if (left == null) {
BST newBST = new BST(value);
left = newBST;
} else {
left.insert(value);
}
} else {
if (right == null) {
BST newBST = new BST(value);
right = newBST;
} else {
right.insert(value);
}
}
return this;
}
public boolean contains(int value) {
// Write your code here.
if (value < this.value) {
if (left == null) {
return false;
} else {
return left.contains(value);
}
} else if (value > this.value) {
if (right == null) {
return false;
} else {
return right.contains(value);
}
} else{
return true;
}
}
Essentially because insert is not implemented as a function but contains is, meaning that insert just has side effects, it changes the state of BST. Contains is inherently a function - it returns an answer for a given input.
The fact insert returns this at the end is not necessary, it could just as easily have a void return value.
A functional version would return a new BST that is like the original but with the element inserted, and that would require use of the returned value, there would be a bit more complexity there.
(I'm not advocating a functional version here!)
The "insert" function only has a return statement right at the end, because all it has to return is "this", rather than being dependent on outside factors and the execution of the function.
So, short version: You use "return" when you need to, and you do not use "return" when you do not need to.
Hello I am attempting to build my own TreeMap data structure. I have an add method that passes a Key and Value, I then have another method called insert that will recursively add the node to the tree. I keep getting a stackoverflow error.
I am unsure how to resolve this error I've re-written my insert method a few times and tried passing parent Nodes into the method. Can someone explain the logic behind what should be passed into the insert method or how my code may possibly still work?
Is the best option here to utilize recursion to add nodes to my tree? or can I accomplish the same thing with a while loop?
Any help is appreciated thank you!
public boolean add(K key, V value)
{
//root is declared as a class level variable in my Map class
if (root == null) {
curSize++;
root = new Node(key, value);
root.parent = null;
return true;
}
//Creating a new Node with the passed arguments
Node n = new Node(key, value);
Node curNode = root;
//Calling insert method
if (curNode.insert(n, curNode)) {
curSize++;
return true;
} else {
return false;
}
}
protected boolean insert(Node curNode, K key, V value) {
int result = key.compareTo(curNode.key);
boolean x = false;
if (curNode.L == null && result < 0) {
curNode.L = new Node(key, value);
x = true;
}
if (curNode.R == null && result > 0) {
curNode.R = new Node(key, value);
x = true;
}
if (result == 0)
x = false;
if (curNode.L != null && result < 0)
insert(curNode.L, key, value);
if (curNode.R != null && result > 0)
insert(curNode.R, key, value);
return x;
}
//My Node class with its constructor, Insert method is inside this class.
private class Node {
public Node L;
public Node R;
public K key;
public V value;
public Node parent;
public Node(K k, V v) {
this.L = null;
this.R = null;
this.key = k;
this.value = v;
}
In your implementation of insert() you have
insert(Node curNode, K key, V value)
but, when you call it, you write
curNode.insert(n, curNode)
Am I missing something? It seems to me like this code shouldn't run. I believe you should be calling it as
insert(curNode, key, value);
Separately, you have
boolean x = true;
at the start of your insert() method, which can be replaced by return statements to avoid entering the recursive blocks after successfully adding a node. I would change your insert method to this:
int result = key.compareTo(curNode.key);
if (curNode.L == null && result < 0) {
curNode.L = new Node(key, value);
return true;
}
if (curNode.R == null && result > 0) {
curNode.R = new Node(key, value);
return true;
}
if (curNode.L != null && result < 0)
return insert(curNode.L, key, value);
if (curNode.R != null && result > 0)
return insert(curNode.R, key, value);
return false;
to avoid accidentally calling insert() when you do not need to.
You forget to use the return values in these calls of insert:
if (curNode.L != null && result < 0)
insert(curNode.L, key, value);
if (curNode.R != null && result > 0)
insert(curNode.R, key, value);
Should be:
if (curNode.L != null && result < 0)
x = insert(curNode.L, key, value);
if (curNode.R != null && result > 0)
x = insert(curNode.R, key, value);
But think of eliminating varibale x by using return statements.
This is my code. I am stuck on the delete part. The insert and get methods are all fine. I want to save the old tree so that I can link the node together soon.
private BinaryTree<K, V> left, right;
public BinaryTree(K key, V value) {
// Leaf node constructor
this.key = key;
this.value = value;
}
public V put(K key, V value) {
if (this.key.equals(key)) {
// Replacing the value at the current node
V ret = this.value;
this.value = value;
return ret;
}
int comp = this.key.compareTo(key);
if (comp < 0) {
// this.key < key
// Put the key we are inserting to the right
// of the current key
if (right == null) {
// We have to create a node to the right
// (Leaf node)
right = new BinaryTree<K, V>(key, value);
return null; // Per interface
}
return right.put(key, value);
} else {
// We don't support keys where equals and compareTo
// disagree with each other.
assert(comp != 0);
// At this point, we know that comp > 0
// Therefore, this.key > key
// Put the key we are inserting to the left of current
if (left == null) {
// We have to create a node to the left
// (Leaf node)
left = new BinaryTree<K, V>(key, value);
return null; // Per interface
}
return left.put(key, value);
// Exercise: You could write a tiny little private method
// to implement this redundant code in one place.
}
}
public V get(K key) {
int comp = this.key.compareTo(key);
if (comp < 0) {
// this.key < key
// Recurse to the right
if (right == null) {
// Not in the tree
return null;
}
return right.get(key);
} else if (comp > 0) {
// this.key > key
// Recurse to the left
if (left == null) {
// Not in the tree
return null;
}
return left.get(key);
} else {
assert(this.key.equals(key));
return value;
}
}
public boolean containsKey(K key) {
// Note: Doesn't work with null values!
return get(key) != null;
}
public BinaryTree<K, V> delete(K key) {
BinaryTree<K,V> tmp,childL,childR,OldTree;
int comp=this.key.compareTo(key);
if(comp<0){
//this.key<Key goes right
if(this.right==null){
throw new UnsupportedOperationException("Nothing you can delete here");
}
else{
return this.right.delete(key);
}
}
if(comp>0){
if(this.left==null){
throw new UnsupportedOperationException("Nothing you can delte here");
}
else{
return this.left.delete(key);
}
}
if(this.key.equals(key)){
}
{
}
//}
// IMPLEMENT THIS FOR YOUR ASSIGNMENT!
// Remove key from the tree, if it exists.
// Throw UnsupportedOperationException if key isn't in the tree.
// Return the new root node if we did delete the key.
// (The new root node may be the same as the old one.)
// If you deleted the last node in the tree, it will return null.
return null;
}}
Some details are posted on the common, is it logical error on my code?
Ok, so I'm I'm working on a school project where we implement a Binary TreeMap and given a basic template to fill out. I'll try to not dump all of the code but here's where I'm hitting a wall. I need to be able to compare keys so insert new elements, properly search and whatnot. But I keep getting a Bad Operand error.
private class Element {
K key;
V value;
public Element(K key, V value) {
this.key = key;
this.value = value;
}
public int compareTo(Element that) {
if (key < that.key) //Error Here
return -1;
else if(key > that.key) //And here
return 1;
else
return 0;
}
}
Now this class is a subclass of the TreeMap class. Again I won't dump the whole code, but the header is like so:
public class TreeMap<K extends Comparable<K>,V> implements MyMap<K,V>
Now everywhere I look seems to point that having K extends Comparable<K> should allow these to be comparable, but they're not. This header was provided by the teacher, so I don't think it needs to be altered. Am I just overlooking or forgetting something?
You can't compare Comparable objects using < and >. Those are only for numeric values. Instead, you can use something like this:
public int compareTo(Element that) {
final int comp = key.compareTo(that.key);
if (comp < 0)
return -1;
else if(comp > 0)
return 1;
else
return 0;
}
Or, better, just return the result of calling compareTo():
public int compareTo(Element that) {
return key.compareTo(that.key);
}
I am preparing my own custom HashMap implementation in Java. Below is my imlementation.
public class Entry<K,V> {
private final K key;
private V value;
private Entry<K,V> next;
public Entry(K key, V value, Entry<K,V> next) {
this.key = key;
this.value = value;
this.next = next;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public Entry<K, V> getNext() {
return next;
}
public void setNext(Entry<K, V> next) {
this.next = next;
}
public K getKey() {
return key;
}
}
public class MyCustomHashMap<K,V> {
private int DEFAULT_BUCKET_COUNT = 10;
private Entry<K,V>[] buckets;
public MyCustomHashMap() {
buckets = new Entry[DEFAULT_BUCKET_COUNT];
for (int i = 0;i<DEFAULT_BUCKET_COUNT;i++)
buckets[i] = null;
}
public void put(K key,V value){
/**
* This is the new node.
*/
Entry<K,V> newEntry = new Entry<K,V>(key, value, null);
/**
* If key is null, then null keys always map to hash 0, thus index 0
*/
if(key == null){
buckets[0] = newEntry;
}
/**
* get the hashCode of the key.
*/
int hash = hash(key);
/**
* if the index does of the bucket does not contain any element then assign the node to the index.
*/
if(buckets[hash] == null) {
buckets[hash] = newEntry;
} else {
/**
* we need to traverse the list and compare the key with each of the keys till the keys match OR if the keys does not match then we need
* to add the node at the end of the linked list.
*/
Entry<K,V> previous = null;
Entry<K,V> current = buckets[hash];
while(current != null) {
boolean done = false;
while(!done) {
if(current.getKey().equals(key)) {
current.setValue(value);
done = true; // if the keys are same then replace the old value with the new value;
} else if (current.getNext() == null) {
current.setNext(newEntry);
done = true;
}
current = current.getNext();
previous = current;
}
}
previous.setNext(newEntry);
}
}
public V getKey(K key) {
int hash = hash(key);
if(buckets[hash] == null) {
return null;
} else {
Entry<K,V> temp = buckets[hash];
while(temp != null) {
if(temp.getKey().equals(key))
return temp.getValue(); // returns value corresponding to key.
temp = temp.getNext();
}
return null; //return null if key is not found.
}
}
public void display() {
for(int i = 0; i < DEFAULT_BUCKET_COUNT; i++) {
if(buckets[i] != null) {
Entry<K,V> entry = buckets[i];
while(entry != null){
System.out.print("{"+entry.getKey()+"="+entry.getValue()+"}" +" ");
entry=entry.getNext();
}
}
}
}
public int bucketIndexForKey(K key) {
int bucketIndex = key.hashCode() % buckets.length;
return bucketIndex;
}
/**
*
* #param key
* #return
*/
private int hash(K key){
return Math.abs(key.hashCode()) % buckets.length;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyCustomHashMap<String, Integer> myCustomHashMap = new MyCustomHashMap<String, Integer>();
myCustomHashMap.put("S", 22);
myCustomHashMap.put("S", 1979);
myCustomHashMap.put("V", 5);
myCustomHashMap.put("R", 31);
System.out.println("Value corresponding to key R: "+myCustomHashMap.getKey("R"));
System.out.println("Value corresponding to key V: "+myCustomHashMap.getKey("V"));
System.out.println("Displaying the contents of the HashMap:: ");
myCustomHashMap.display();
}
}
1) I feel that put (K key,V value) is somewhat flawed. Please do kindly validate and let me know what's wrong here. On entering the same key its giving me wrong result. I have not yet tested it for collision cases having different keys.
2) It is said that we rehash the hashCode so that it eliminates wrong implementation of hashCode. how do I do it because if I give hashCode of key i.e. hash(key.hashCode()) then it dosn't take as it can't compute hashCode of int. How to do this?
Any help would be highly appreciated.
Thanks
Sid
You handle null key incorrectly :
if(key == null){
buckets[0] = newEntry;
}
It's possible that buckets[0] already contains entries, in which case you will lose those entries.
The following loop has some issues :
Entry<K,V> previous = null;
Entry<K,V> current = buckets[hash];
while(current != null) {
boolean done = false;
while(!done) {
if(current.getKey().equals(key)) {
current.setValue(value);
done = true;
} else if (current.getNext() == null) {
current.setNext(newEntry);
done = true;
}
current = current.getNext();
previous = current; // you are not really setting previous to
// to the previous Entry in the list - you
// are setting it to the current Entry
}
}
previous.setNext(newEntry); // you don't need this statement. You
// already have a statement inside the
// loop that adds the new Entry to the list
It looks like removing any statements related to previous will fix this loop.
EDIT:
As kolakao commented, in order for your implementation to be efficient (i.e. require expected constant time for get and put), you must resize the HashMap when the number of entries exceeds some threshold (in order for the average number of entries in each bucket to be bound by a constant).
It is said that we rehash the hashCode so that it eliminates wrong implementation of hashCode. how do I do it because if I give hashCode of key i.e. hash(key.hashCode()) then it dosn't take as it can't compute hashCode of int. How to do this?
The idea of re-hashing doesn't involve calling hashCode for the hashCode of the key. It involves running some hardcoded function on the value obtained by key.hashCode().
For example, in Java 7 implementation of HashMap, the following function is used :
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
Then you use it with :
int hash = hash(key.hashCode());
int bucket = hash % buckets.length;