I'm tasked to implement a remove() method for my Sorted Binary Tree. I need to pass a set of tests which was assigned by our teacher, and I'm struggling to do so. This is the code I have currently, which was heavily borrowed from our schoolbook.
#Override
#SuppressWarnings("unchecked")
public V remove(Object key) throws NoSuchElementException {
if(!containsKey((K) key)){
throw new NoSuchElementException();
}
ReturnObject oldEntry = new ReturnObject(null);
SortedTreeMap<K, V> newRoot = removeEntry(getRoot(this), (K) key, oldEntry);
if(newRoot != null){
setRoot(newRoot.data);
setLeftChild(newRoot.leftChild);
setRightChild(newRoot.rightChild);
} else{
this.data = null;
}
if(oldEntry.get() != null){
size--;
}
return oldEntry.get();
}
/*
* Removes the entry in a given root node of a subtree.
* rootNode is the root node of the subtree.
* Returns the root node of the revised subtree.
*
* */
private SortedTreeMap<K,V> removeFromRoot(SortedTreeMap<K, V> rootNode){
if(rootNode.hasLeftChild() && rootNode.hasRightChild()){
SortedTreeMap<K,V> leftSubtreeRoot = rootNode.getLeftChild();
SortedTreeMap<K,V> largestNode = findLargest(leftSubtreeRoot);
rootNode.setRoot(largestNode.getRoot());
rootNode.setLeftChild(removeLargest(leftSubtreeRoot));
} else if(rootNode.hasRightChild()) {
rootNode = rootNode.getRightChild();
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Finds the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the node containing the largest entry in the tree.
*
* */
private SortedTreeMap<K,V> findLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
rootNode = findLargest(rootNode.getRightChild());
}
return rootNode;
}
/*
* Removes the node containing the largest entry in a given tree.
* rootNode is the root node of the tree.
* Returns the root node of the revised tree.
*
* */
private SortedTreeMap<K,V> removeLargest(SortedTreeMap<K, V> rootNode){
if(rootNode.hasRightChild()){
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rightEntry = removeLargest(rightEntry);
rootNode.setRightChild(rightEntry);
} else{
rootNode = rootNode.getLeftChild();
}
return rootNode;
}
/*
* Removes an entry from the tree rooted at a given node.
* rootNode is a reference to the root of a tree.
* entry is the object to be removed.
* oldEntry is an object whose data field is null
* Returns the root node of the resulting tree; if entry matches
* an entry in the tree, oldEntry's data field is the entry
* that was removed from the tree; otherwise it is null.
* */
private SortedTreeMap<K,V> removeEntry(SortedTreeMap<K,V> rootNode, K entry, ReturnObject oldEntry){
if(rootNode != null){
K rootData = rootNode.data.key;
int comparison = entry.compareTo(rootData);
if(comparison == 0){
oldEntry.set(rootNode.data.value);
rootNode = removeFromRoot(rootNode);
} else if(comparison < 0){
SortedTreeMap<K,V> leftEntry = rootNode.getLeftChild();
SortedTreeMap<K,V> subtreeRoot = removeEntry(leftEntry, entry, oldEntry);
rootNode.setLeftChild(subtreeRoot);
} else{
SortedTreeMap<K,V> rightEntry = rootNode.getRightChild();
rootNode.setRightChild(removeEntry(rightEntry, entry, oldEntry));
}
}
return rootNode;
}
And this is the test I'm struggling to pass:
/**
* Check that entry is no longer in map after remove
*/
public Property remove_removes_entry() {
return property(isKVList,
kvs -> implies(kvs.length() > 0,
() -> property(choose(0, kvs.length()-1),
i -> {
P2<Integer, String> entry = kvs.index(i);
SortedTreeMap<Integer, String> tm = new SortedTreeMap<>(intOrd.toComparator());
kvs.foreachDoEffect(kv -> tm.add(kv._1(), kv._2()));
tm.remove(entry._1());
List<Integer> keys = fromIterator(tm.keys().iterator());
return prop(!keys.exists(key -> key.equals(entry._1())));
})
)
);
}
The error message I'm getting is this:
java.lang.Error: Falsified after 2 passed tests with arguments:
List(List((0,gzprxt),(4,lntpqj),(-5,caki),(-6,jzf)),2)
I can get in between 0 to 60 passes and I don't get which scenario the remove method fails in. I've tried to make a Junit test, but didn't succeed.
Here is some more context for my class:
public class Entry<K, V> {
public final K key;
public final V value;
public Entry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public boolean equals(Object o) {
if (o instanceof Entry) {
Entry other = (Entry)o;
return this.key.equals(other.key) && this.value.equals(other.value);
}
return false;
}
}
public class SortedTreeMap<K extends Comparable<? super K>, V> implements ISortedTreeMap<K, V> {
private int size;
private Entry<K,V> data;
private SortedTreeMap<K,V> leftChild, rightChild, parent;
private Comparator<K> keyComparator;
private SortedTreeMap(K key, V value, Comparator<K> keyComparator, SortedTreeMap<K, V> parent){
data = new Entry<>(key, value);
this.parent = parent;
this.keyComparator = keyComparator;
}
public SortedTreeMap(Comparator<K> kComparator) {
keyComparator = kComparator;
}
private Entry<K,V> getRoot(){
return this.data;
}
private SortedTreeMap<K,V> getLeftChild(){
return this.leftChild;
}
private SortedTreeMap<K,V> getRightChild(){
return this.rightChild;
}
private void setLeftChild(SortedTreeMap<K, V> newLeftChild){
leftChild = newLeftChild;
}
private void setRightChild(SortedTreeMap<K, V> newRightChild){
rightChild = newRightChild;
}
private void setRoot(Entry<K, V> newRoot){
data = newRoot;
}
private boolean hasLeftChild(){
return leftChild != null;
}
private boolean hasRightChild(){
return rightChild != null;
}
#Override
public V add(K key, V value) {
V result = null;
if(isEmpty()) {
data = new Entry<>(key, value);
} else{
int comparison = keyComparator.compare(key, data.key);
SortedTreeMap<K,V> newChild = new SortedTreeMap<>(key, value, keyComparator, this);
if(comparison < 0){
if (hasLeftChild()) {
result = leftChild.add(key, value);
} else{
setLeftChild(newChild);
}
} else if(comparison > 0){
if (hasRightChild()) {
result = rightChild.add(key, value);
} else{
setRightChild(newChild);
}
} else{
result = data.value;
this.data = new Entry<>(key, value);
}
}
if (result == null){
size++;
}
return result;
}
#Override
public Iterable<K> keys() {
return new KeyIterator(this);
}
public class KeyIterator implements Iterable<K>, Iterator<K>{
private SortedTreeMap<K,V> next;
KeyIterator(SortedTreeMap<K, V> root){
next = root;
while(next.leftChild != null){
next = next.leftChild;
}
}
#Override
public Iterator<K> iterator() {
return this;
}
public boolean hasNext(){
return next != null && !next.isEmpty();
}
public K next(){
if(!hasNext()){
throw new NoSuchElementException();
}
SortedTreeMap<K,V> r = next;
if(next.rightChild != null){
next = next.rightChild;
while(next.leftChild != null){
next = next.leftChild;
}
return r.data.key;
}
while(true){
if(next.parent == null){
next = null;
return r.data.key;
}
if(next.parent.leftChild == next){
next = next.parent;
return r.data.key;
}
next = next.parent;
}
}
}
Related
There is a Tree implementation, which requires the correction and idea of how to implement the logic.
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
/**
* #param <T>
*/
class TreeNode<T> implements Iterable<TreeNode<T>> {
private final List<TreeNode<T>> elementsIndex;
public T data;
public TreeNode<T> parent;
public List<TreeNode<T>> children;
/**
* #param data
*/
public TreeNode(T data) {
this.data = data;
this.children = new LinkedList<TreeNode<T>>();
this.elementsIndex = new LinkedList<TreeNode<T>>();
this.elementsIndex.add(this);
}
/**
* #param depth
* #return Indentation
*/
private static String createIndent(int depth) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < depth; i++) {
sb.append('-');
}
return sb.toString();
}
/**
* #return parent
*/
public boolean isRoot() {
return parent == null;
}
/**
* #return check the size of child
*/
public boolean isLeaf() {
return children.size() == 0;
}
/**
* #param child
* #return child node
*/
public TreeNode<T> addChild(T child) {
TreeNode<T> childNode = new TreeNode<T>(child);
childNode.parent = this;
this.children.add(childNode);
this.registerChildForSearch(childNode);
return childNode;
}
/**
* #return the depth of the level
*/
public int getLevel() {
if (this.isRoot())
return 0;
else
return parent.getLevel() + 1;
}
/**
* #param node
*/
private void registerChildForSearch(TreeNode<T> node) {
elementsIndex.add(node);
if (parent != null)
parent.registerChildForSearch(node);
}
/**
* #param cmp
* #return finds the element
*/
public TreeNode<T> findTreeNode(Comparable<T> cmp) {
for (TreeNode<T> element : this.elementsIndex) {
T elData = element.data;
if (cmp.compareTo(elData) == 0)
return element;
}
return null;
}
/**
* #return the root
*/
#Override
public String toString() {
return data != null ? data.toString() : "[data null]";
}
/**
* #return iterator
*/
#Override
public Iterator<TreeNode<T>> iterator() {
TreeNodeIter<T> iter = new TreeNodeIter<T>(this);
return iter;
}
/**
* #param treeRoot
* #param data
* #return the data else null if not found
*/
public TreeNode<T> search(TreeNode<T> treeRoot, T data) {
Comparable<T> searchCriteria = new Comparable<T>() {
#Override
public int compareTo(Object treeData) {
if (treeData == null)
return 1;
boolean nodeOk = treeData.equals(data);
return nodeOk ? 0 : 1;
}
};
TreeNode<T> found = treeRoot.findTreeNode(searchCriteria);
return found;
}
/**
* #param treeRoot
* #return the whole tree
*/
public StringBuilder display(TreeNode<T> treeRoot) {
StringBuilder print = new StringBuilder();
print.append('\n');
for (TreeNode<T> node : treeRoot) {
String indent = createIndent(node.getLevel());
print.append(indent + node.data);
print.append("\n|");
}
if (print.length() > 0)
print.deleteCharAt(print.length() - 1);
return print;
}
}
/**
* #param <T>
*/
class TreeNodeIter<T> implements Iterator<TreeNode<T>> {
private final TreeNode<T> treeNode;
private final Iterator<TreeNode<T>> childrenCurNodeIter;
private ProcessStages doNext;
private TreeNode<T> next;
private Iterator<TreeNode<T>> childrenSubNodeIter;
/**
* #param treeNode
*/
public TreeNodeIter(TreeNode<T> treeNode) {
this.treeNode = treeNode;
this.doNext = ProcessStages.ProcessParent;
this.childrenCurNodeIter = treeNode.children.iterator();
}
/**
* #return true if there is nay element
*/
#Override
public boolean hasNext() {
if (this.doNext == ProcessStages.ProcessParent) {
this.next = this.treeNode;
this.doNext = ProcessStages.ProcessChildCurNode;
return true;
}
if (this.doNext == ProcessStages.ProcessChildCurNode) {
if (childrenCurNodeIter.hasNext()) {
TreeNode<T> childDirect = childrenCurNodeIter.next();
childrenSubNodeIter = childDirect.iterator();
this.doNext = ProcessStages.ProcessChildSubNode;
return hasNext();
} else {
this.doNext = null;
return false;
}
}
if (this.doNext == ProcessStages.ProcessChildSubNode) {
if (childrenSubNodeIter.hasNext()) {
this.next = childrenSubNodeIter.next();
return true;
} else {
this.next = null;
this.doNext = ProcessStages.ProcessChildCurNode;
return hasNext();
}
}
return false;
}
/**
* #return the next element
*/
#Override
public TreeNode<T> next() {
return this.next;
}
#Override
public void remove() {
throw new UnsupportedOperationException();
}
enum ProcessStages {
ProcessParent,
ProcessChildCurNode,
ProcessChildSubNode
}
}
So, the issue is that I have the data something like this
Gen
|-Test1
|--Mat
|-Test2
|--123
|---Child's Child
|----Child's Child's Child
|----2406
|-Test3
|--24
But to implement that I have to hard code something like this
public static TreeNode<String> getSet() {
TreeNode<String> root = new TreeNode<String>("Gen");
{
TreeNode<String> node0 = root.addChild("Test1");
{
TreeNode<String> node00 = node0.addChild("Mat");
}
TreeNode<String> node1 = root.addChild("Test2");
{
TreeNode<String> node10 = node1.addChild("123");
{
TreeNode<String> node100 = node10.addChild("Child's Child");
{
TreeNode<String> node1000 = node100.addChild("Child's Child's Child");
TreeNode<String> node1001 = node100.addChild("2406");
}
}
}
TreeNode<String> node2 = root.addChild("Test3");
{
TreeNode<String> node20 = node2.addChild("24");
}
}
return root;
}
}
On the top that I have the helper method called getPartentChildPart() method which is used to get the
Map<Parent, List> that is Map<String,List<String>>
Gen
|-Test1
|--Mat
|-Test2
|--123
|---Child's Child
|----Child's Child's Child
|----2406
|-Test3
|--24
For example, if I gave Gen as the parameter to getPartentChildPart() then it will return
Map<Gen,<Test1,Test2,Test3>>
So far what I can achieve is this
public void Build(Map<String, List<String>> ROOT) {
TreeNode<String> root = null;
TreeNode<String> node = null;
for (Map.Entry<String, List<String>> i : ROOT.entrySet()) {
root = new TreeNode<>(i.getKey());
for (String j : i.getValue())
{
node = root.addChild(j);
parent = getPartentChildPart(j);
Build(parent);
}
System.out.println("Root"+root.display(root));
}
}
public void Show() {
Map<String, List<String>> rt = null;
rt = getPartentChildPart("Gen");
Build(rt);
}
The result what I get this is
Gen
|-Test1
|-Test2
|-Test3
I don't understand why recursion is not working and how can I implement this... Although if I hardcode this it can work what if my tree grows in future then hard code will not work.
The first thing to do would be to add a method that returns your list of children. From there recursion becomes quite simple.
void itChildren(TreeNode<T> node)
{
// do stuff
if (!node.getChildren().isEmpty())
{
for (int i = 0; i < node.getChildren().size(); i++)
{
itChildren(node.getChildren().get(i));
}
}
// do stuff
}
This would then iterate over the entirety of the tree. Depending on whether you want to do things before (which I believe is your case) or after the children you can place your code either at the top or bottom of the function.
Let me know if this works for you.
final TreeNode<String> struct = "Gen";
public void build(Map<String, List<String>> parentRoot, TreeNode<String> node) {
Map<String, List<String>> parent;
for (Map.Entry<String, List<String>> i : parentRoot.entrySet()) {
for (Stringj : i.getValue()) {
TreeNode<String> childNode = node.addChild(new TreeNode<>(j));
parent = getPartentChildPart(j);
build(parent, childNode);
}
}
}
public void show() {
Map<String, List<String>> ROOT = null;
ROOT = getPartentChildPart("Gen");
build(ROOT, struct);
System.out.println(struct.display(struct));
}
The mistake was that I need to create a new node every time and need to get the root outside the recurrsion.
Currently, I'm working on a project in which I am implementing a Linked Dictionary interface from scratch. It was smooth sailing until I realized my code claims the entire dictionary is empty as soon as it sees a single null value.
public class LinkedDictionary<K, V> implements Dictionary<K, V> {
/** List node. */
private class Node {
K key;
V value;
Node next;
Node(K key, V value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
/** The first node in this dictionary, or null if this dictionary is empty. */
private Node front;
#Override
public void put(K key, V value) {
if(value == null) { // if a null value is given, the dictionary is empty
isEmpty();
return;
}
for (Node n = front; n != null; n = n.next) {
if (n.key.equals(key)) {
return;
}
}
front = new Node(key, value, front); // front node now contains new key and value
}
#Override
public V get(K key) {
if (key == null) // if a null key is given, null is returned
return null;
for (Node n = front; n != null; n = n.next) {
if (n.key.equals(key)) {
return n.value; // if they key is within the contents of the dictionary, return the corresponding value
}
}
return null; //if the key does not appear in the dictionary, null is returned.
}
#Override
public boolean isEmpty() {
return front == null;
}
}
I see my problem pretty clearly. In my "put" method, if the user tries to input a null value, the entire dictionary is marked as "empty."
I'm imagining this scenario:
Dictionary<Integer, String> d = new LinkedDictionary<>();
d.put(1, "one");
d.put(2, "two");
d.put(1, null);
As soon as my implementation reaches the third line in which the key 1 is set to null, it is going to set the entire dictionary to empty, despite the fact there is still a valid entry at key 2.
I've been wracking my brain over how to alter my code to allow for this sort of scenario, but I cannot figure it out. I've tried altering my isEmpty method, and I've even tried adding an additional method, but I just can't seem to figure out what to do here!
Any pointers or shoves in the right direction would be much appreciated!
public final class LinkedDictionary<K, V> {
private Node<K, V> head;
private Node<K, V> tail;
private int size;
public int getSize() {
return size;
}
public void put(K key, V value) {
Objects.requireNonNull(key, "Node.key");
Node<K, V> node = new Node<>(key, value);
remove(key);
if (isEmpty())
head = node;
else
tail.next = node;
size++;
tail = node;
}
public V remove(K key) {
Objects.requireNonNull(key, "Node.key");
if (isEmpty())
return null;
if (head.key.equals(key)) {
V value = head.value;
head = head.next;
tail = head == null ? null : tail;
size--;
return value;
}
Node<K, V> prv = head;
Node<K, V> it = head;
while (it != null) {
if (it.key.equals(key)) {
V value = it.value;
prv.next = it.next;
it.next = null;
size--;
return value;
}
prv = it;
it = it.next;
}
return null;
}
public V get(K key) {
Objects.requireNonNull(key, "Node.key");
Node<K, V> node = head;
while (node != null) {
if (node.key.equals(key))
return node.value;
node = node.next;
}
return null;
}
public boolean isEmpty() {
return head == null;
}
private static class Node<K, V> {
private final K key;
private final V value;
private Node<K, V> next;
public Node(K key, V value) {
this.key = key;
this.value = value;
}
}
}
I'm copy yours code excluding implementation and #Override, everything works fine
and d.isEmpty() give me a false as result
I'm trying to implement a Dictionary using a Red-Black tree.
I've tested the insert method and it seems to work good, the RBtree seems to keep the correct shape and colors. The method that performs the binary tree node deletion seems to be correct, but I'm having huge problems on the deleteFixUp method called at the end of the deletion.
Would you like to help me figure out what I'm doing wrong? And, of course, if you have any suggestion to improve my code it would be very appreciated.
RBTreeWParentDictionary.java (Here I implemented the RedBlackTree)
package dictionary;
import java.util.Comparator;
public class RBTreeWParentDictionary<K, V> implements IDictionary<K, V> {
/**
* The root node of the RBTreeWParentDictionary
*/
public RBTreeWParentNode<K, V> root;
/**
* Object used to compare two T objects.
*/
private Comparator<K> comparator;
private int length;
/**
* Creates the dictionary based on red/black tree with null root
*
* #param comparator
* The comparator for keys
*/
public RBTreeWParentDictionary(Comparator<K> comparator) {
this.root = null;
this.comparator = comparator;
this.length = 0;
}
/**
* Checks if the tree is empty
*
* #return True if the tree is empty
*/
public boolean isEmpty() {
return this.root == null;
}
/**
* Returns the number of elements in the tree
*
* #return The number of elements in the tree
*/
public int length() {
return this.length;
}
/**
* Performs a left rotation on the tree node
*
* #param node
* The node on which rotate
*/
private void rotateLeft(RBTreeWParentNode<K, V> node) {
RBTreeWParentNode<K, V> y = node.getRight();
node.setRight(y.getLeft());
if (y.hasLeft()) {
y.getLeft().setParent(node);
}
y.setParent(node.getParent());
if (!node.hasParent()) { // = this.isEmpty()
this.root = y;
}
else {
if (node.equals(node.getParent().getLeft())) {
node.getParent().setLeft(y);
}
else {
node.getParent().setRight(y);
}
}
y.setLeft(node);
}
/**
* Performs a right rotation on the tree node
*
* #param node
* The node on which rotate
*/
private void rotateRight(RBTreeWParentNode<K, V> node) {
RBTreeWParentNode<K, V> y = node.getLeft();
node.setLeft(y.getRight());
if (y.hasRight()) {
y.getRight().setParent(node);
}
y.setParent(node.getParent());
if (!node.hasParent()) {
this.root = y;
}
else {
if (node.equals(node.getParent().getRight())) {
node.getParent().setRight(y);
}
else {
node.getParent().setLeft(y);
}
}
y.setRight(node);
}
/*
* Uses for first tests, now removed
*
* public void testRotateLeft() { this.rotateLeft(this.root); }
*
* public void testRotateRight() { this.rotateRight(this.root); }
*/
/**
* Performs all the needed work to the tree under the 3 main rules of R/BTree
*
* #param node
* The current node that needs to be checked
*/
private void treeFixUp(RBTreeWParentNode<K, V> node) {
RBTreeWParentNode<K, V> u;
if (!node.hasParent()) {
return;
}
while (node.getParent().isRed()) {
if (node.getParent().equals(node.getParent().getParent().getLeft())) {
u = node.getParent().getParent().getRight();
if (u != null && u.isRed()) {
node.getParent().setBlack();
u.setBlack();
node.getParent().getParent().setRed();
node = node.getParent().getParent();
}
else {
if (node.equals(node.getParent().getRight())) {
node = node.getParent();
rotateLeft(node);
}
node.getParent().setBlack();
node.getParent().getParent().setRed();
rotateRight(node.getParent().getParent());
}
}
else {
u = node.getParent().getParent().getLeft();
if (u != null && u.isRed()) {
node.getParent().setBlack();
u.setBlack();
node.getParent().getParent().setRed();
node = node.getParent().getParent();
}
else {
if (node.equals(node.getParent().getLeft())) {
node = node.getParent();
rotateRight(node);
}
node.getParent().setBlack();
node.getParent().getParent().setRed();
rotateLeft(node.getParent().getParent());
}
}
if (!node.hasParent()) {
node.setBlack();
break;
}
}
}
/**
* Inserts a node with give key/value
*
* #param key
* The key of the node to be inserted
* #param value
* The value of the node to be inserted
*/
#Override
public void insert(K key, V value) {
int res;
RBTreeWParentNode<K, V> insertedNode = new RBTreeWParentNode<K, V>(key,
value);
if (this.isEmpty()) {
this.root = insertedNode;
this.root.setBlack();
}
else {
RBTreeWParentNode<K, V> node = this.root;
while (node != null) {
res = comparator.compare(key, node.getKey());
if (res < 0) {
if (node.hasLeft()) {
node = node.getLeft();
}
else break;
}
else if (res > 0) {
if (node.hasRight()) {
node = node.getRight();
}
else break;
}
else { // duplicate key, overwriting
node.setValue(value);
return;
}
}
res = comparator.compare(key, node.getKey());
if (res < 0) {
node.setLeft(insertedNode);
}
else {
node.setRight(insertedNode);
}
treeFixUp(insertedNode);
this.length++;
}
}
#Override
public V get(K key) {
// TODO Auto-generated method stub
return null;
}
#Override
public void delete(K key) {
RBTreeWParentNode<K, V> node = root;
boolean oldColor;
int res;
while (node != null
&& (res = comparator.compare(key, node.getKey())) != 0) {
if (res < 0) node = node.getLeft();
else node = node.getRight();
}
if (node == null)
return;
oldColor = node.getColor();
// key found, work with children
if (!node.hasParent()) {//In root
root = null;
return;
}
else if(node.hasLeft() && !node.hasRight()) {//left child
node.getLeft().setParent(node.getParent());
node.getParent().setLeft(node.getLeft());
}
else if (!node.hasLeft() && node.hasRight()) {//right child
node.getRight().setParent(node.getParent());
node.getParent().setRight(node.getRight());
}
else if (node.hasLeft() && node.hasRight()) {//both children
RBTreeWParentNode<K, V> tmp = node;
node = min(tmp.getRight());
//fix parent node of node
node.setParent(tmp.getParent());
if (tmp.getParent().getLeft().equals(tmp)) {
node.getParent().setLeft(node);
}
else node.getParent().setRight(node);
node.setRight(deleteMin(tmp.getRight()));
node.setLeft(tmp.getLeft());
tmp = null;
}
else { // is a leaf
if (node.equals(node.getParent().getLeft()) ) {
node.getParent().setLeft(null);
}
else node.getParent().setRight(null);
}
if (oldColor == false) {
deleteFixUp(node);
}
}
private RBTreeWParentNode<K, V> deleteMin(
RBTreeWParentNode<K, V> node) {
if (node.getLeft() == null) {
return node.getRight();
}
node.setLeft(deleteMin(node.getLeft()));
return node;
}
private RBTreeWParentNode<K, V> min(RBTreeWParentNode<K, V> node) {
if (node.getLeft() == null) {
return node;
}
else return min(node.getLeft());
}
private void deleteFixUp(RBTreeWParentNode<K, V> node) {
while (!node.equals(this.root) && node.isBlack()) {
if (node.equals(node.getParent().getLeft())) {
if (node.getParent().hasRight()) {
RBTreeWParentNode<K, V> w = node.getParent().getRight();
if (w.isRed()) {
w.setBlack();
node.getParent().setRed();
rotateLeft(node.getParent());
w=node.getParent().getRight();
}
if (w.hasLeft() && w.hasRight() && w.getLeft().isBlack() && w.getRight().isBlack()) {
w.setRed();
node = node.getParent();
}
else {
if (w.hasRight() && w.getRight().isBlack()) {
w.getLeft().setBlack();
w.setRed();
rotateRight(w);
w = node.getParent().getRight();
}
w.setColor(node.getParent().getColor());
node.getParent().setBlack();
w.getRight().setBlack();
rotateLeft(node.getParent());
node = this.root;
}
}
}
else {
//Repeat up changing left with right
if (node.getParent().hasLeft()) {
RBTreeWParentNode<K, V> w = node.getParent().getLeft();
if (w.isRed()) {
w.setBlack();
node.getParent().setRed();
rotateRight(node.getParent());
w=node.getParent().getLeft();
}
if (w.hasLeft() && w.hasRight() && w.getLeft().isBlack() && w.getRight().isBlack()) {
w.setRed();
node = node.getParent();
}
else {
if (w.hasLeft() && w.getLeft().isBlack()) {
w.getRight().setBlack();
w.setRed();
rotateLeft(w);
w = node.getParent().getLeft();
}
w.setColor(node.getParent().getColor());
node.getParent().setBlack();
w.getLeft().setBlack();
rotateRight(node.getParent());
node = this.root;
}
}
}
}
node.setBlack();
}
#SuppressWarnings("unused")
#Override
public boolean equals(Object other) {
if (!(other instanceof RBTreeWParentDictionary)) {
return false;
}
if ((this == null && other != null) || (this != null && other == null)) {
return false;
}
if (this == null && other == null) {
return true;
}
else {
#SuppressWarnings("unchecked")
RBTreeWParentDictionary<K, V> oth = (RBTreeWParentDictionary<K, V>) other;
return equalsNodes(this.root, oth.root);
}
}
private boolean equalsNodes(RBTreeWParentNode<K, V> node1,
RBTreeWParentNode<K, V> node2) {
if ((node1 == null && node2 != null) || (node1 != null && node2 == null)) {
return false;
}
else if (node1 == null && node2 == null) {
return true;
}
else return node1.equals(node2)
&& equalsNodes(node1.getLeft(), node2.getLeft())
&& equalsNodes(node1.getRight(), node2.getRight());
}
}
RBTreeWParentNode.java (Here is the node of RedBlackTree)
package dictionary;
public class RBTreeWParentNode<K, V> {
private K key;
private V value;
private boolean color;
private RBTreeWParentNode<K, V> left, right, parent;
private static final boolean RED = true;
private static final boolean BLACK = false;
public RBTreeWParentNode(K key, V value, RBTreeWParentNode<K, V> left,
RBTreeWParentNode<K, V> right, RBTreeWParentNode<K, V> parent) {
this.key = key;
this.value = value;
this.color = RED;
this.left = left;
if (this.hasLeft())
this.getLeft().setParent(this);
this.right = right;
if (this.hasRight())
this.getRight().setParent(this);
this.parent = parent;
}
public RBTreeWParentNode(K key, V value) {
this.key = key;
this.value = value;
this.color = RED;
}
public RBTreeWParentNode() {
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public boolean getColor() {
return color;
}
public RBTreeWParentNode<K, V> getLeft() {
return left;
}
public RBTreeWParentNode<K, V> getRight() {
return right;
}
public RBTreeWParentNode<K, V> getParent() {
return parent;
}
public RBTreeWParentNode<K, V> getBrother() {
if (this.hasParent()) {
if (this.getParent().getLeft().equals(this)) {
return this.getParent().getRight();
}
else return this.getParent().getLeft();
}
else return null;
}
public boolean isRed() {
return this.color == RED;
}
public boolean isBlack() {
return this.color == BLACK;
}
public boolean hasLeft() {
return this.getLeft() != null;
}
public boolean hasRight() {
return this.getRight() != null;
}
public boolean hasParent() {
return this.getParent() != null;
}
public boolean hasBrother() {
if (this.hasParent()) {
if (this.getParent().getLeft().equals(this)) {
return this.getParent().getRight() != null;
}
else return this.getParent().getLeft() != null;
}
else return false;
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value) {
this.value = value;
}
public void setRed() {
this.color = RED;
}
public void setBlack() {
this.color = BLACK;
}
public void setParent(RBTreeWParentNode<K, V> node) {
this.parent = node;
}
public void setLeft(RBTreeWParentNode<K, V> node) {
this.left = node;
if (this.hasLeft())
this.left.setParent(this);
}
public void setRight(RBTreeWParentNode<K, V> node) {
this.right = node;
if (this.hasRight())
this.right.setParent(this);
}
public void setColor(boolean color) {
this.color = color;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof RBTreeWParentNode)) {
return false;
}
if ((this == null && other != null) || (this != null && other == null)) {
return false;
}
#SuppressWarnings("unchecked")
RBTreeWParentNode<K, V> oth = (RBTreeWParentNode<K, V>) other;
return checkFieldsEquals(oth);
}
private boolean checkFieldsEquals(RBTreeWParentNode<K, V> oth) {
//Check keys
if ((this.getKey() == null && oth.getKey() != null)
|| (this.getKey() != null && oth.getKey() == null)) {
return false;
}
else {
if ((this.getKey() == null && oth.getKey() == null)
|| this.getKey().equals(oth.getKey())) {
if ((this.getValue() == null && oth.getValue() != null)
|| (this.getValue() != null && oth.getValue() == null)) {
return false;
}
else {
if ((this.getValue() == null && oth.getValue() == null)
|| (this.getValue().equals(oth.getValue()))) {
if (this.getColor() != oth.getColor()) {
return false;
}
else {
return (this.getKey() == null && oth.getKey() == null)
|| this.getKey().equals(oth.getKey());
}
}
else return false;
}
}
else {
return false;
}
}
}
}
RBTreeWParentDictionaryTest.java -> My test class
Update 09/07/2016
I've updated my code because i found out that I wasn't updating the node cursor to root after the fix-up and I wasn't calling the fix-up only when the deleted node is black.
Considering my test case testDeleteDoubles I've figured out that I'm choosing a wrong candidate to switch with the item to delete when it has a brother.
Seeing this simulator the candidate should be the max node on the left branch of the deleted item, but shouldn't it be the successor, so the min item on the right branch?
In delete(), you need to remember the child node of the deleted node because the red-black properties might be violated after the delete. Let's say we declare RBTreeWParentNode<K, V> childOfDeletedNode;
Then, for the case of left child you update childOfDeletedNode = node.getLeft();
For the case of right child you update childOfDeletedNode = node.getRight();
For both children, you need to add the following after calling min():
oldColor = node.getColor();
childOfDeletedNode = node.getLeft();
node.setColor(tmp.getColor());
For leaf, take any child childOfDeletedNode = node.getRight();
Then, you fix the color of the child node with deleteFixUp(childOfDeletedNode);
Now since childOfDeletedNode can be null, you need to handle that case in deleteFixUp by adding a check for node != null in the loop condition and adding an if-statement before setting the color to black in the last line.
Anyway, the simulator you refer to finds the maximum node of the left sub-tree. Your solution finds the minimum node of the right sub-tree. Both are correct, but will result in different results. You'll need fix your test case.
To illustrate, before the delete:
10(B)
/ \
8(R) 100(B)
/ \
5(B) 9(B)
/ \
2(R) 6(R)
After the delete of 8, you replace it 9, the minimum node of the right sub-tree. The color is changed to red.
10(B)
/ \
9(R) 100(B)
/
5(B)
/ \
2(R) 6(R)
This question already has answers here:
Is Java "pass-by-reference" or "pass-by-value"?
(93 answers)
Closed 7 years ago.
So in simpletons, I am creating my own AVLTree data structure. Now when i add a new node into my tree, it seems to add fine.
EDIT: It doesnt seem to take into account my duplicates (nor add them to the original node's list by key).
But when i print the rootNode to see if it exists it doesn't exist. I can't figure out what the problem is with my add method.
Here is my AVLTree class:
package cw.util;
import java.util.ArrayList;
import java.util.Comparator;
public class AVLTree<K, V>
{
public class Node {
private K key;
private ArrayList<V> valuesList;
private Node left, right;
private int height;
public Node(K key, ArrayList<V> valuesList) {
this.key = key;
this.valuesList = valuesList;
this.height = 0;
}
public Node(V value) {
}
public void addToNode(V value) {
valuesList.add(value);
}
public K getKey() {
return key;
}
public ArrayList<V> getValues() {
return valuesList;
}
public Node getLeftChild() {
return left;
}
public Node getRightChild() {
return right;
}
public int getHeight() {
return height;
}
public Node getChildNodeFromSide(String side) {
switch(side) {
default: return null;
case "left": return left;
case "right": return right;
}
}
}
private Node rootNode;
private Comparator<K> comparator;
//Unused
public AVLTree() {
}
public AVLTree(Comparator<K> comparator) {
this.comparator = comparator;
this.rootNode = null;
}
public V insert(K key, V value) {
Node n = insert(key, value, rootNode);
if(n != null) {
for(V v : n.getValues())
System.out.println(v.toString());
System.out.println();
return value;
} else {
return null;
}
}
public Node insert(K key, V value, Node node) {
ArrayList<V> values = new ArrayList<V>();
values.add(value);
if(node == null)
node = new Node(key, values);
else if(comparator.compare(key, node.key) < 0) {
node.left = insert(key, value, node.left);
if(height(node.left) - height(node.right) == 2) {
if(comparator.compare(key, node.left.key) < 0)
node = rotateWithLeftChild(node);
else
node = doubleRotateWithLeft(node);
}
} else if(comparator.compare(key, node.key) > 0) {
node.right = insert(key, value, node.right);
if(height(node.right) - height(node.left) == 2) {
if(comparator.compare(key, node.right.key) > 0)
node = rotateWithRightChild(node);
else
node = doubleRotateWithRight(node);
}
} else node.getValues().add(value);
node.height = Math.max(height(node.left), height(node.right)) + 1;
return node;
}
public Node search(K key) {
return search(key, rootNode);
}
public Node search(K key, Node node) {
boolean isFound = false;
while((node != null) && !isFound) {
K nodeKey = node.getKey();
if(comparator.compare(key, nodeKey) < 0)
node = node.getLeftChild();
else if(comparator.compare(key, nodeKey) > 0)
node = node.getRightChild();
else {
isFound = true;
}
node = search(key, node);
}
if(isFound) return node;
else return null;
}
//Custom Methods
public boolean isEmpty() {
return rootNode == null;
}
private int height(Node n) {
return n == null ? -1 : n.getHeight();
}
private Node rotateWithLeftChild(Node node2) {
Node node1 = node2.left;
node2.left = node1.right;
node1.right = node2;
node2.height = Math.max(height(node2.left), height(node2.right)) + 1;
node1.height = Math.max(height(node1.left), node2.getHeight()) + 1;
return node1;
}
private Node rotateWithRightChild(Node node1) {
Node node2 = node1.right;
node1.right = node2.left;
node2.left = node1;
node1.height = Math.max(height(node1.left), height(node1.right)) + 1;
node2.height = Math.max(height(node2.left), node1.getHeight()) + 1;
return node2;
}
private Node doubleRotateWithLeft(Node node) {
node.left = rotateWithRightChild(node.left);
return rotateWithLeftChild(node);
}
private Node doubleRotateWithRight(Node node) {
node.right = rotateWithLeftChild(node.right);
return rotateWithRightChild(node);
}
}
Here is how I test the class:
package cw.avl;
import cw.util.AVLTree;
public class AVLTest
{
public static void main(String[] args) {
AVLTree<String, Integer> tree = new AVLTree<String, Integer>(String.CASE_INSENSITIVE_ORDER);
for (int i=1; i <= 10;i++) {
String s = "S" + i;
int x = i;
tree.insert(s, x);
tree.insert(s, x);
}
}
}
Well, you don't seem to ever assign to rootNode, so it starts null and remains so. In fact, your methods create nodes and return them:
if(node == null)
node = new Node(key, values);
...
return node
But you don't use the returned node.
Edit: longer explanation:
When you call from the other function like this: Node n = insert(key, value, rootNode); you are basically saying: Node n = insert(key, value, null);. On the receiving end, here:
public Node insert(K key, V value, Node node) { ,
you are creating a new variable called node with initial value null. Then you replace that value when you do:
node = new Node(key, values);
That value is for the node variable in the insert(K,V,N) method, in no way is rootNode retroactively updated. You could just do so right there:
if(node == null) {
node = new Node(key, values);
rootNode = node;
}
So a hashmap is a hash-based implementation of a map structure in java. I've figured out how to get the hashmap put method to work, but I want to write a method that removes the key value pair, and I'm having trouble implementing it.
The only thing I can really understand right now is how to tell the function to stop in the event that the key is empty or doesn't exist.. I'd love any sort of help. An explanation as to how the method will work, or some basic pseudo-code examples would be much appreciated.
This is what I have in the delete method so far:
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
// Implement this method
}
If it helps, here is my completed Map Entry class:
public class MapEntry<K, V> {
MapEntry<K, V> next;
K key;
V value;
public MapEntry(K key, V value) {
this.setKey(key);
this.setValue(value);
}
public void setKey(K key) {
this.key = key;
}
public void setValue(V value) {
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public void setNext(MapEntry<K, V> next) {
this.next = next;
}
public MapEntry<K, V> getNext() {
return next;
}
}
Also, here's the entirety of my HashMap class if it helps.
public class HashMap<K, V> {
private int DEFAULT_CAPACITY = 10;
private MapEntry<K, V>[] Hash;
private int size;
public HashMap() {
Hash = new MapEntry[DEFAULT_CAPACITY];
}
public int getHashCode(K key) {
int bucketIndex = key.hashCode() % Hash.length;
return bucketIndex;
}
public V get(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
MapEntry<K, V> entry = Hash[getHashCode(key)];
while (entry != null && !key.equals(entry.getKey()))
entry = entry.getNext();
if (entry != null)
return entry.getValue();
else
return null;
}
/**
*
* #param key
* #param value
* The put method works by associating the specified value with
* the given key in the map.
* If the key is already in the map,
* the old value is replaced with the new one.
*/
public void put(K key, V value) {
int keyBucket = hash(key);
MapEntry<K, V> temp = Hash[keyBucket];
while (temp != null) {
if ((temp.key == null && key == null)
|| (temp.key != null && temp.key.equals(key))) {
temp.value = value;
return;
}
temp = temp.next;
}
Hash[keyBucket] = new MapEntry<K, V>(key, value);
size++;
}
public void delete(K key) {
if (key == null) {
throw new IllegalArgumentException("Null Key!");
}
// Implement this method
}
public void print(){
//Bonus Method
}
private int hash(K key) {
if (key == null) {
return 0;
} else {
return Math.abs(key.hashCode() % this.Hash.length);
}
} }
Using the same logic that you do in get(), locate the correct bucket and, within that bucket, the correct MapEntry (let's call it e). Then simply remove e from the bucket—basically, this is removing a node from a single-linked list. If e is the first element in the bucket, set the corresponding element of Hash to e.next; otherwise set the next field of the element just before e to e.next. Note that you need one more variable (updated as you're finding e) to keep track of the previous entry in the bucket.
source code
public class MyInternalMap<K, V> implements Map<K, V> {
/**
* https://stackoverflow.com/questions/16266459/implementing-a-remove-method-in-a-java-hashmap
*/
private final int initialCapacity;
private MyMapEntry<K, V>[] mapEntries;
private int size;
public MyInternalMap() {
this(16);
}
public MyInternalMap(int initialCapacity) {
this.initialCapacity = initialCapacity;
mapEntries = new MyMapEntry[initialCapacity];
}
#Override
public int size() {
return size;
}
#Override
public boolean isEmpty() {
return size == 0;
}
#Override
public boolean containsKey(Object key) {
return get(key) != null;
}
#Override
public boolean containsValue(Object value) {
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
if (containsValue(value, mapEntry)) {
return true;
}
}
return false;
}
private boolean containsValue(Object value, MyMapEntry<K, V> mapEntry) {
if (mapEntry == null) {
return false;
}
if (value == mapEntry.getValue() || mapEntry.getValue().equals(value)) {
return true;
}
return containsValue(value, mapEntry.next);
}
#Override
public V get(Object key) {
if (key == null) {
return null;
}
MyMapEntry<K, V> entry = mapEntries[getHashCode(key)];
while (entry != null) {
if (key.equals(entry.key)) {
return entry.value;
}
entry = entry.next;
}
return null;
}
#Override
public V put(K key, V value) {
int keyBucket = getHashCode(key);
MyMapEntry<K, V> temp = mapEntries[keyBucket];
if (temp == null) {
//create new head node in this bucket
mapEntries[keyBucket] = new MyMapEntry<>(key, value);
size++;
return null;
}
while (temp != null) {
if ((temp.key == null && key == null)
|| (temp.key != null && temp.key.equals(key))) {
V returnValue = temp.value;
temp.value = value;
return returnValue;
}
temp = temp.next;
}
//create new node in this bucket
mapEntries[keyBucket].next = new MyMapEntry<>(key, value);
size++;
return null;
}
#Override
public V remove(Object key) {
/**
* Using the same logic that you do in get(), locate the correct bucket and, within that bucket, the correct MapEntry (let's call it e). Then simply remove e from the bucket—basically,
* this is removing a node from a single-linked list. If e is the first element in the bucket, set the corresponding element of Hash to e.next;
* otherwise set the next field of the element just before e to e.next. Note that you need one more variable (updated as you're finding e) to keep track of the previous entry in the bucket
*/
int keyBucket = getHashCode(key);
MyMapEntry<K, V> temp = mapEntries[keyBucket];
if (temp == null)
return null;
MyMapEntry<K, V> prev = temp;
while (temp != null) {
if (temp.key != null && temp.key.equals(key)) {
V valueReturn = temp.value;
if (prev == temp) { //first element?
mapEntries[keyBucket] = temp.next;
} else {
prev.next = temp.next;
}
size--;
return valueReturn;
}
prev = temp;
temp = temp.next;
}
return null;
}
#Override
public void putAll(Map<? extends K, ? extends V> m) {
//TODO impl
}
#Override
public void clear() {
mapEntries = new MyMapEntry[initialCapacity];
size = 0;
}
#Override
public Set<K> keySet() {
Set<K> resultKeys = new HashSet<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addKeySet(mapEntry, resultKeys);
}
return resultKeys;
}
private void addKeySet(MyMapEntry<K, V> mapEntry, Set<K> resultKeys) {
if (mapEntry != null) {
resultKeys.add(mapEntry.key);
addKeySet(mapEntry.next, resultKeys);
}
}
#Override
public Collection<V> values() {
Collection<V> resultValues = new ArrayList<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addValue(mapEntry, resultValues);
}
return resultValues;
}
private void addValue(MyMapEntry<K, V> mapEntry, Collection<V> resultValues) {
if (mapEntry != null) {
resultValues.add(mapEntry.value);
addValue(mapEntry.next, resultValues);
}
}
#Override
public Set<Entry<K, V>> entrySet() {
Set<Entry<K, V>> entrySetResult = new HashSet<>();
for (int i = 0; i < mapEntries.length; i++) {
MyMapEntry<K, V> mapEntry = mapEntries[i];
addEntrySet(mapEntry, entrySetResult);
}
return entrySetResult;
}
private void addEntrySet(MyMapEntry<K, V> mapEntry, Set<Entry<K, V>> entrySetResult) {
if (mapEntry != null) {
entrySetResult.add(mapEntry);
addEntrySet(mapEntry.next, entrySetResult);
}
}
private int getHashCode(Object key) {
if (key == null)
return 0;
int bucketIndex = Math.abs(key.hashCode()) % initialCapacity;
return bucketIndex;
}
class MyMapEntry<K, V> implements Map.Entry<K, V> {
private K key;
private V value;
private MyMapEntry<K, V> next;
public MyMapEntry(K key, V value) {
this.key = key;
this.value = value;
}
#Override
public K getKey() {
return key;
}
#Override
public V getValue() {
return value;
}
#Override
public V setValue(V value) {
V oldValue = this.value;
this.value = value;
return oldValue;
}
}
}
public class MyInternalMapTest {
#ParameterizedTest()
#MethodSource({"mapArgumentsProvider"})
public void mapTest(Map<Integer, String> map) {
assertNull(map.get(0));
assertNull(map.get(null));
assertNull(map.remove(0));
assertNull(map.remove(null));
assertNull(map.remove(1));
assertEquals(0, map.size());
assertNull(map.put(1, "1"));
assertEquals(1, map.size());
assertEquals("1", map.put(1, "2"));
assertEquals(1, map.size());
assertEquals("2", map.get(1));
assertEquals(1, map.size());
assertNull(map.put(2, "3"));
assertEquals(2, map.size());
assertEquals("2", map.remove(1));
assertEquals(1, map.size());
assertNull(map.remove(1));
assertEquals("3", map.remove(2));
assertEquals(0, map.size());
}
#ParameterizedTest()
#MethodSource({"mapArgumentsProvider"})
public void mapSameHashCodeTest(Map<Integer, String> map) {
assertNull(map.put(1, "1"));
assertEquals("1", map.get(1));
assertNull(map.put(17, "2"));
assertEquals("1", map.get(1));
assertEquals("2", map.get(17));
assertEquals("1", map.get(1));
assertTrue(map.containsValue("1"));
assertTrue(map.containsValue("2"));
assertFalse(map.containsValue("3"));
assertEquals(Arrays.asList("1", "2"), map.values().stream().sorted().collect(Collectors.toList()));
}
private static Stream<Arguments> mapArgumentsProvider() {
return Stream.of(
Arguments.of(new MyInternalMap<>()),
Arguments.of(new HashMap<>())
);
}
}