All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.solovyev.common.collections.tree.BinarySearchTreeNode Maven / Gradle / Ivy

The newest version!
package org.solovyev.common.collections.tree;

import org.solovyev.common.text.Strings;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Comparator;

final class BinarySearchTreeNode {

	@Nullable
	private final T value;

	@Nullable
	private BinarySearchTreeNode parent;

	@Nullable
	private BinarySearchTreeNode leftChild;

	@Nullable
	private BinarySearchTreeNode rightChild;

	BinarySearchTreeNode(@Nullable T value, @Nullable BinarySearchTreeNode parent) {
		this.value = value;
		this.parent = parent;
	}

	@Nullable
	public T getValue() {
		return value;
	}

	@Nullable
	public BinarySearchTreeNode getLeftChild() {
		return leftChild;
	}

	@Nullable
	public BinarySearchTreeNode getRightChild() {
		return rightChild;
	}

	@Nullable
	public BinarySearchTreeNode getParent() {
		return parent;
	}

	@Nonnull
	BinarySearchTreeNode addNode(@Nullable T newValue, @Nonnull Comparator comparator) {
		final int compare = comparator.compare(newValue, value);
		if (compare < 0) {
			if (leftChild != null) {
				return leftChild.addNode(newValue, comparator);
			} else {
				leftChild = Trees.newBinarySearchTreeNode(newValue, this);
				return leftChild;
			}
		} else {
			if (rightChild != null) {
				return rightChild.addNode(newValue, comparator);
			} else {
				rightChild = Trees.newBinarySearchTreeNode(newValue, this);
				return rightChild;
			}
		}
	}

	public boolean removeChildSubtree(@Nonnull BinarySearchTreeNode child) {
		if (leftChild != null && leftChild.equals(child)) {
			child.removeParent();
			leftChild = null;
			return true;
		}

		if (rightChild != null && rightChild.equals(child)) {
			child.removeParent();
			rightChild = null;
			return true;
		}

		return false;
	}

	private void removeParent() {
		this.parent = null;
	}

	boolean removeSelfFromTree(@Nonnull BinarySearchTree tree) {
		boolean removed = false;

		if (leftChild == null && rightChild == null) {
			if (tree.getRoot().equals(this)) {
				throw new IllegalArgumentException("Tree must have at least one node (root)");
			} else {
				if (parent != null) {
					removed = parent.removeChildSubtree(this);
				}
			}
		} else if (leftChild == null && rightChild != null) {
			replaceSubtree(tree, rightChild);
		} else if (leftChild != null && rightChild == null) {
			replaceSubtree(tree, leftChild);
		} else {
			final BinarySearchTreeNode leftMostNode = findLeftMostNode(rightChild);
			leftMostNode.removeSelfFromTree(tree);

			leftMostNode.leftChild = leftChild;
			if(leftMostNode.leftChild != null) {
				leftMostNode.leftChild.parent = leftMostNode;
			}

			leftMostNode.rightChild = rightChild;
			if(leftMostNode.rightChild != null) {
				leftMostNode.rightChild.parent = leftMostNode;
			}

			if(tree.getRoot().equals(this)) {
				tree.setRoot(leftMostNode);
			} else {
				replaceSubtree(tree, leftMostNode);
			}

			leftChild = null;
			rightChild = null;
		}

		return removed;
	}

	private void replaceSubtree(@Nonnull BinarySearchTree tree, @Nonnull BinarySearchTreeNode newNode) {
		if (newNode.parent != null) {
			newNode.parent.removeChildSubtree(newNode);
		}

		if (tree.getRoot().equals(this)) {
			tree.setRoot(newNode);
		} else {
			newNode.parent = this.parent;

			boolean wasLeft = false;
			boolean wasRight = false;

			if (this.parent != null) {
				if (this.parent.leftChild != null && this.parent.leftChild.equals(this)) {
					wasLeft = true;
				}
				if (this.parent.rightChild != null && this.parent.rightChild.equals(this)) {
					wasRight = true;
				}
				this.parent.removeChildSubtree(this);
			}

			if (wasLeft) {
				newNode.parent.leftChild = newNode;
			}

			if (wasRight) {
				newNode.parent.rightChild = newNode;
			}
		}
	}

	@Nonnull
	private BinarySearchTreeNode findLeftMostNode(@Nonnull BinarySearchTreeNode node) {
		if (node.leftChild == null) {
			return node;
		} else {
			return findLeftMostNode(node.leftChild);
		}
	}

	@Nullable
	BinarySearchTreeNode findNode(@Nullable T value, @Nonnull Comparator comparator) {
		final int compare = comparator.compare(this.value, value);
		if(compare < 0) {
			return tryFindNode(value, comparator, rightChild);
		} else if (compare > 0) {
			return tryFindNode(value, comparator, leftChild);
		} else {
			return this;
		}
	}

	@Nullable
	private static  BinarySearchTreeNode tryFindNode(@Nullable T value, @Nonnull Comparator comparator, @Nullable BinarySearchTreeNode node) {
		if(node != null) {
			return node.findNode(value, comparator);
		} else {
			return null;
		}
	}

	void toString(int depth, @Nonnull StringBuilder out, @Nullable String prefix) {
		out.append(Strings.repeat(" ", depth));
		if (prefix != null) {
			out.append(prefix).append(":");
		}
		out.append(this.value);
		out.append("\n");

		if (rightChild != null) {
			rightChild.toString(depth + 1, out, "r");
		}

		if (leftChild != null) {
			leftChild.toString(depth + 1, out, "l");
		}
	}

	public boolean isThisLeftChildOf(@Nonnull BinarySearchTreeNode parent) {
		final BinarySearchTreeNode parentLeftChild = parent.getLeftChild();
		if(parentLeftChild != null && parentLeftChild.equals(this)) {
			return true;
		} else {
			return false;
		}
	}

	public boolean isThisRightChildOf(@Nonnull BinarySearchTreeNode parent) {
		final BinarySearchTreeNode parentRightChild = parent.getRightChild();
		if(parentRightChild != null && parentRightChild.equals(this)) {
			return true;
		} else {
			return false;
		}
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy