最近重新学习数据结构和算法,刚刚看完java版的这几个数据结构,比较浅显易懂,有兴趣的可以自己去调试学习,关于这几个的介绍网上很多。
二叉搜索树,比较简单的树结构了
package com.jwetherell.algorithms.data_structures;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Queue;
/**
* A binary search tree (BST), which may sometimes also be called an ordered or
* sorted binary tree, is a node-based binary tree data structure which has the
* following properties: 1) The left subtree of a node contains only nodes with
* keys less than the node's key. 2) The right subtree of a node contains only
* nodes with keys greater than the node's key. 3) Both the left and right
* subtrees must also be binary search trees.
*
* http://en.wikipedia.org/wiki/Binary_search_tree
*
* @author Justin Wetherell
*/
public class BinarySearchTree> {
private int modifications = 0;
protected static final Random RANDOM = new Random();
protected enum Position { LEFT, RIGHT };
protected Node root = null;
protected int size = 0;
protected INodeCreator creator = null;
/**
* Default constructor.
*/
public BinarySearchTree() { }
/**
* Constructor with external Node creator.
*/
public BinarySearchTree(INodeCreator creator) {
this.creator = creator;
}
/**
* Add value to the tree. Tree can contain multiple equal values.
*
* @param value T to add to the tree.
* @return True if successfully added to tree.
*/
public boolean add(T value) {
Node nodeAdded = this.addValue(value);
return (nodeAdded!=null);
}
/**
* Add value to the tree and return the Node that was added. Tree can
* contain multiple equal values.
*
* @param value T to add to the tree.
* @return Node which was added to the tree.
*/
protected Node addValue(T value) {
Node newNode = null;
if (this.creator == null) newNode = new Node(null, value);
else newNode = this.creator.createNewNode(null, value);
//If root is null, assign
if (root == null) {
root = newNode;
size++;
return newNode;
}
Node node = root;
while (node != null) {
if (newNode.id.compareTo(node.id) <= 0) {
//Less than or equal to goes left
if (node.lesser == null) {
// New left node
node.lesser = newNode;
newNode.parent = node;
size++;
return newNode;
} else {
node = node.lesser;
}
} else {
//Greater than goes right
if (node.greater == null) {
// New right node
node.greater = newNode;
newNode.parent = node;
size++;
return newNode;
} else {
node = node.greater;
}
}
}
return newNode;
}
/**
* Does the tree contain the value.
*
* @param value T to locate in the tree.
* @return True if tree contains value.
*/
public boolean contains(T value) {
Node node = getNode(value);
return (node != null);
}
/**
* Locate T in the tree.
*
* @param value T to locate in the tree.
* @return Node representing first reference of value in tree or NULL if not found.
*/
protected Node getNode(T value) {
Node node = root;
while (node != null && node.id!=null) {
if (value.compareTo(node.id) == 0) {
return node;
} else if (value.compareTo(node.id) < 0) {
node = node.lesser;
} else {
node = node.greater;
}
}
return null;
}
/**
* Rotate tree left at sub-tree rooted at node.
*
* @param node Root of tree to rotate left.
*/
protected void rotateLeft(Node node) {
Position parentPosition = null;
Node parent = node.parent;
if (parent!=null) {
if (node.equals(parent.lesser)) {
//Lesser
parentPosition = Position.LEFT;
} else {
//Greater
parentPosition = Position.RIGHT;
}
}
Node greater = node.greater;
node.greater = null;
Node lesser = greater.lesser;
greater.lesser = node;
node.parent = greater;
node.greater = lesser;
if (lesser!=null) lesser.parent = node;
if (parentPosition!=null) {
if (parentPosition==Position.LEFT) {
parent.lesser = greater;
} else {
parent.greater = greater;
}
greater.parent = parent;
} else {
root = greater;
greater.parent = null;
}
}
/**
* Rotate tree right at sub-tree rooted at node.
*
* @param node Root of tree to rotate left.
*/
protected void rotateRight(Node node) {
Position parentPosition = null;
Node parent = node.parent;
if (parent!=null) {
if (node.equals(parent.lesser)) {
//Lesser
parentPosition = Position.LEFT;
} else {
//Greater
parentPosition = Position.RIGHT;
}
}
Node lesser = node.lesser;
node.lesser = null;
Node greater = lesser.greater;
lesser.greater = node;
node.parent = lesser;
node.lesser = greater;
if (greater!=null) greater.parent = node;
if (parentPosition!=null) {
if (parentPosition==Position.LEFT) {
parent.lesser = lesser;
} else {
parent.greater = lesser;
}
lesser.parent = parent;
} else {
root = lesser;
lesser.parent = null;
}
}
/**
* Get greatest node in sub-tree rooted at startingNode. The
* search does not include startingNode in it's results.
*
* @param startingNode Root of tree to search.
* @return Node which represents the greatest node in the
* startingNode sub-tree or NULL if startingNode has no greater
* children.
*/
protected Node getGreatest(Node startingNode) {
if (startingNode==null) return null;
Node greater = startingNode.greater;
while (greater!=null && greater.id!=null) {
Node node = greater.greater;
if (node!=null && node.id!=null) greater = node;
else break;
}
return greater;
}
/**
* Get least node in sub-tree rooted at startingNode. The
* search does not include startingNode in it's results.
*
* @param startingNode Root of tree to search.
* @return Node which represents the least node in the
* startingNode sub-tree or NULL if startingNode has no lesser
* children.
*/
protected Node getLeast(Node startingNode) {
if (startingNode==null) return null;
Node lesser = startingNode.lesser;
while (lesser!=null && lesser.id!=null) {
Node node = lesser.lesser;
if (node!=null && node.id!=null) lesser = node;
else break;
}
return lesser;
}
/**
* Remove first occurrence of value in the tree.
*
* @param value T to remove from the tree.
* @return True if value was removed from the tree.
*/
public boolean remove(T value) {
Node nodeToRemove = this.removeValue(value);
return (nodeToRemove!=null);
}
/**
* Remove first occurrence of value in the tree.
*
* @param value T to remove from the tree.
* @return Node which was removed from the tree.
*/
protected Node removeValue(T value) {
Node nodeToRemoved = this.getNode(value);
if (nodeToRemoved != null) {
Node replacementNode = this.getReplacementNode(nodeToRemoved);
replaceNodeWithNode(nodeToRemoved,replacementNode);
}
return nodeToRemoved;
}
/**
* Get the proper replacement node according to the binary
* search tree algorithm from the tree.
*
* @param nodeToRemoved Node to find a replacement for.
* @return Node which can be used to replace nodeToRemoved.
* nodeToRemoved should NOT be NULL.
*/
protected Node getReplacementNode(Node nodeToRemoved) {
Node replacement = null;
Node parent = nodeToRemoved.parent;
if (parent == null) {
// Replacing the root node
if (nodeToRemoved.lesser != null && nodeToRemoved.greater == null) {
// Replace with lesser subtree
replacement = nodeToRemoved.lesser;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser==null) {
// Replace with greater subtree
replacement = nodeToRemoved.greater;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser!=null) {
//Two children
replacement = this.getLeast(nodeToRemoved.greater);
if (replacement==null) replacement = nodeToRemoved.greater;
}
} else if (parent.lesser != null && (parent.lesser.id.compareTo(nodeToRemoved.id) == 0)) {
// If the node to remove is the parent's lesser node, replace
// the parent's lesser node with one of the node to remove's
// lesser/greater subtrees
if (nodeToRemoved.lesser != null && nodeToRemoved.greater == null) {
// Using the less subtree
replacement = nodeToRemoved.lesser;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser == null) {
// Using the greater subtree (there is no lesser subtree, no refactoring)
replacement = nodeToRemoved.greater;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser!=null) {
//Two children
replacement = this.getLeast(nodeToRemoved.greater);
if (replacement==null) replacement = nodeToRemoved.greater;
}
} else if (parent.greater != null && (parent.greater.id.compareTo(nodeToRemoved.id) == 0)) {
// If the node to remove is the parent's greater node, replace
// the parent's greater node with the node's greater node
if (nodeToRemoved.lesser != null && nodeToRemoved.greater == null) {
// Using the less subtree
replacement = nodeToRemoved.lesser;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser == null) {
// Using the greater subtree (there is no lesser subtree, no refactoring)
replacement = nodeToRemoved.greater;
} else if (nodeToRemoved.greater != null && nodeToRemoved.lesser!=null) {
//Two children - use either the greatest child in the lesser branch or the least child in the greater branch
//Add some randomness to deletions, so we don't always use the greatest/least on deletion
if (modifications%2!=0) {
replacement = this.getGreatest(nodeToRemoved.lesser);
if (replacement==null) replacement = nodeToRemoved.lesser;
} else {
replacement = this.getLeast(nodeToRemoved.greater);
if (replacement==null) replacement = nodeToRemoved.greater;
}
modifications++;
}
}
return replacement;
}
/**
* Replace nodeToRemoved with replacementNode in the tree.
*
* @param nodeToRemoved Node to remove replace in the tree. nodeToRemoved
* should NOT be NULL.
* @param replacementNode Node to replace nodeToRemoved in the tree. replacementNode
* can be NULL.
*/
protected void replaceNodeWithNode(Node nodeToRemoved, Node replacementNode) {
if (replacementNode!=null) {
//Save for later
Node replacementNodeLesser = replacementNode.lesser;
Node replacementNodeGreater = replacementNode.greater;
//Replace replacementNode's branches with nodeToRemove's branches
Node nodeToRemoveLesser = nodeToRemoved.lesser;
if (nodeToRemoveLesser!=null && !nodeToRemoveLesser.equals(replacementNode)) {
replacementNode.lesser = nodeToRemoveLesser;
nodeToRemoveLesser.parent = replacementNode;
}
Node nodeToRemoveGreater = nodeToRemoved.greater;
if (nodeToRemoveGreater!=null && !nodeToRemoveGreater.equals(replacementNode)) {
replacementNode.greater = nodeToRemoveGreater;
nodeToRemoveGreater.parent = replacementNode;
}
//Remove link from replacementNode's parent to replacement
Node replacementParent = replacementNode.parent;
if (replacementParent!=null && !replacementParent.equals(nodeToRemoved)) {
Node replacementParentLesser = replacementParent.lesser;
Node replacementParentGreater = replacementParent.greater;
if (replacementParentLesser!=null && replacementParentLesser.equals(replacementNode)) {
replacementParent.lesser = replacementNodeGreater;
if (replacementNodeGreater!=null) replacementNodeGreater.parent = replacementParent;
} else if (replacementParentGreater!=null && replacementParentGreater.equals(replacementNode)) {
replacementParent.greater = replacementNodeLesser;
if (replacementNodeLesser!=null) replacementNodeLesser.parent = replacementParent;
}
}
}
//Update the link in the tree from the nodeToRemoved to the replacementNode
Node parent = nodeToRemoved.parent;
if (parent == null) {
// Replacing the root node
root = replacementNode;
if (root!=null) root.parent = null;
} else if (parent.lesser != null && (parent.lesser.id.compareTo(nodeToRemoved.id) == 0)) {
parent.lesser = replacementNode;
if (replacementNode!=null) replacementNode.parent = parent;
} else if (parent.greater != null && (parent.greater.id.compareTo(nodeToRemoved.id) == 0)) {
parent.greater = replacementNode;
if (replacementNode!=null) replacementNode.parent = parent;
}
size--;
}
/**
* Get number of nodes in the tree.
*
* @return Number of nodes in the tree.
*/
public int size() {
return size;
}
/**
* Validate the tree for all Binary Search Tree invariants.
*
* @return True if tree is valid.
*/
public boolean validate() {
if (root==null) return true;
return validateNode(root);
}
/**
* Validate the node for all Binary Search Tree invariants.
*
* @param node Node to validate in the tree. node should
* NOT be NULL.
* @return True if the node is valid.
*/
protected boolean validateNode(Node node) {
Node lesser = node.lesser;
Node greater = node.greater;
boolean lesserCheck = true;
if (lesser!=null && lesser.id!=null) {
lesserCheck = (lesser.id.compareTo(node.id) <= 0);
if (lesserCheck) lesserCheck = validateNode(lesser);
}
if (!lesserCheck) return false;
boolean greaterCheck = true;
if (greater!=null && greater.id!=null) {
greaterCheck = (greater.id.compareTo(node.id) > 0);
if (greaterCheck) greaterCheck = validateNode(greater);
}
return greaterCheck;
}
/**
* Get an array representation of the tree in breath first search order.
*
* @return breath first search sorted array representing the tree.
*/
@SuppressWarnings("unchecked")
public T[] getBFS() {
Queue> queue = new ArrayDeque>();
T[] values = (T[]) new Comparable[size];
int count = 0;
Node node = root;
while (node!=null) {
values[count++] = node.id;
if (node.lesser!=null) queue.add(node.lesser);
if (node.greater!=null) queue.add(node.greater);
if (!queue.isEmpty()) node = queue.remove();
else node = null;
}
return values;
}
/**
* Get an array representation of the tree in depth first search order.
*
* @return depth first search sorted array representing the tree.
*/
@SuppressWarnings("unchecked")
public T[] getDFS() {
List> added = new ArrayList>(2);
T[] nodes = (T[]) new Comparable[size];
int index = 0;
Node node = root;
while (index < size && node != null) {
Node parent = node.parent;
Node lesser = (node.lesser != null && !added.contains(node.lesser)) ? node.lesser : null;
Node greater = (node.greater != null && !added.contains(node.greater)) ? node.greater : null;
if (parent == null && lesser == null && greater == null) {
if (!added.contains(node)) nodes[index++] = node.id;
break;
}
if (lesser != null) {
node = lesser;
} else {
if (!added.contains(node)) {
nodes[index++] = node.id;
added.add(node);
}
if (greater != null) {
node = greater;
} else if (greater == null && added.contains(node)) {
node = parent;
} else {
// We should not get here. Stop the loop!
node = null;
}
}
}
return nodes;
}
/**
* Get an array representation of the tree in sorted order.
*
* @return sorted array representing the tree.
*/
public T[] getSorted() {
// Depth first search to traverse the tree in order.
return getDFS();
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return TreePrinter.getString(this);
}
protected static class Node> {
protected T id = null;
protected Node parent = null;
protected Node lesser = null;
protected Node greater = null;
/**
* Node constructor.
*
* @param parent Parent link in tree. parent can be NULL.
* @param id T representing the node in the tree.
*/
protected Node(Node parent, T id) {
this.parent = parent;
this.id = id;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "id =" + id +
" parent=" + ((parent != null) ? parent.id : "NULL") +
" lesser=" + ((lesser != null) ? lesser.id : "NULL") +
" greater=" + ((greater != null) ? greater.id : "NULL");
}
}
protected static interface INodeCreator> {
/**
* Create a new Node with the following parameters.
*
* @param parent of this node.
* @param id of this node.
* @return new Node
*/
public Node createNewNode(Node parent, T id);
}
protected static class TreePrinter {
public static > String getString(BinarySearchTree tree) {
if (tree.root == null) return "Tree has no nodes.";
return getString(tree.root, "", true);
}
private static > String getString(Node node, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
if (node.parent!=null) {
String side = "left";
if (node.equals(node.parent.greater)) side = "right";
builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + side + ") " + node.id + "\n");
} else {
builder.append(prefix + (isTail ? "└── " : "├── ") + node.id + "\n");
}
List> children = null;
if (node.lesser != null || node.greater != null) {
children = new ArrayList>(2);
if (node.lesser != null) children.add(node.lesser);
if (node.greater != null) children.add(node.greater);
}
if (children != null) {
for (int i = 0; i < children.size() - 1; i++) {
builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false));
}
if (children.size() >= 1) {
builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
}
}
return builder.toString();
}
}
}
平衡二叉树,AVL树,比较复杂了,需要对旋转
package com.jwetherell.algorithms.data_structures;
import java.util.ArrayList;
import java.util.List;
/**
* An AVL tree is a self-balancing binary search tree, and it was the first such data
* structure to be invented. In an AVL tree, the heights of the two child subtrees
* of any node differ by at most one. AVL trees are often compared with red-black trees
* because they support the same set of operations and because red-black trees also take
* O(log n) time for the basic operations. Because AVL trees are more rigidly balanced,
* they are faster than red-black trees for lookup intensive applications. However,
* red-black trees are faster for insertion and removal.
*
* http://en.wikipedia.org/wiki/AVL_tree
*
* @author Justin Wetherell
*/
public class AVLTree> extends BinarySearchTree implements BinarySearchTree.INodeCreator {
private enum Balance { LEFT_LEFT, LEFT_RIGHT, RIGHT_LEFT, RIGHT_RIGHT };
/**
* Default constructor.
*/
public AVLTree() {
this.creator = this;
}
/**
* Constructor with external Node creator.
*/
public AVLTree(INodeCreator creator) {
super(creator);
}
/**
* {@inheritDoc}
*/
@Override
protected Node addValue(T id) {
Node nodeToReturn = super.addValue(id);
AVLNode nodeAdded = (AVLNode) nodeToReturn;
while (nodeAdded!=null) {
nodeAdded.updateHeight();
balanceAfterInsert(nodeAdded);
nodeAdded = (AVLNode) nodeAdded.parent;
}
return nodeToReturn;
}
/**
* Balance the tree according to the AVL post-insert algorithm.
*
* @param node Root of tree to balance.
*/
private void balanceAfterInsert(AVLNode node) {
AVLNode grandParent = (AVLNode) node;
int balanceFactor = grandParent.getBalanceFactor();
if (balanceFactor>1 || balanceFactor<-1) {
AVLNode parent = null;
AVLNode child = null;
Balance balance = null;
if (balanceFactor<0) {
parent = (AVLNode) grandParent.lesser;
balanceFactor = parent.getBalanceFactor();
if (balanceFactor<0) {
child = (AVLNode) parent.lesser;
balance = Balance.LEFT_LEFT;
} else {
child = (AVLNode) parent.greater;
balance = Balance.LEFT_RIGHT;
}
} else {
parent = (AVLNode) grandParent.greater;
balanceFactor = parent.getBalanceFactor();
if (balanceFactor<0) {
child = (AVLNode) parent.lesser;
balance = Balance.RIGHT_LEFT;
} else {
child = (AVLNode) parent.greater;
balance = Balance.RIGHT_RIGHT;
}
}
if (balance == Balance.LEFT_RIGHT) {
//Left-Right (Left rotation, right rotation)
rotateLeft(parent);
rotateRight(grandParent);
} else if (balance == Balance.RIGHT_LEFT) {
//Right-Left (Right rotation, left rotation)
rotateRight(parent);
rotateLeft(grandParent);
} else if (balance == Balance.LEFT_LEFT) {
//Left-Left (Right rotation)
rotateRight(grandParent);
} else {
//Right-Right (Left rotation)
rotateLeft(grandParent);
}
grandParent.updateHeight(); //New child node
child.updateHeight(); //New child node
parent.updateHeight(); //New Parent node
}
}
/**
* {@inheritDoc}
*/
@Override
protected Node removeValue(T value) {
//Find node to remove
Node nodeToRemoved = this.getNode(value);
if (nodeToRemoved != null) {
//Find the replacement node
Node replacementNode = this.getReplacementNode(nodeToRemoved);
//Find the parent of the replacement node to re-factor the height/balance of the tree
AVLNode nodeToRefactor = null;
if (replacementNode!=null) nodeToRefactor = (AVLNode) replacementNode.parent;
if (nodeToRefactor==null) nodeToRefactor = (AVLNode) nodeToRemoved.parent;
if (nodeToRefactor!=null && nodeToRefactor.equals(nodeToRemoved)) nodeToRefactor = (AVLNode) replacementNode;
//Replace the node
replaceNodeWithNode(nodeToRemoved,replacementNode);
//Re-balance the tree all the way up the tree
if (nodeToRefactor!=null) {
while (nodeToRefactor!=null) {
nodeToRefactor.updateHeight();
balanceAfterDelete(nodeToRefactor);
nodeToRefactor = (AVLNode) nodeToRefactor.parent;
}
}
}
return nodeToRemoved;
}
/**
* Balance the tree according to the AVL post-delete algorithm.
*
* @param node Root of tree to balance.
*/
private void balanceAfterDelete(AVLNode node) {
int balanceFactor = node.getBalanceFactor();
if (balanceFactor==-2 || balanceFactor==2) {
if (balanceFactor==-2) {
AVLNode ll = (AVLNode) node.lesser.lesser;
int lesser = (ll!=null)?ll.height:0;
AVLNode lr = (AVLNode) node.lesser.greater;
int greater = (lr!=null)?lr.height:0;
if (lesser>=greater) {
rotateRight(node);
node.updateHeight();
if (node.parent!=null) ((AVLNode)node.parent).updateHeight();
} else {
rotateLeft(node.lesser);
rotateRight(node);
AVLNode p = (AVLNode) node.parent;
if (p.lesser!=null) ((AVLNode)p.lesser).updateHeight();
if (p.greater!=null) ((AVLNode)p.greater).updateHeight();
p.updateHeight();
}
} else if (balanceFactor==2) {
AVLNode rr = (AVLNode) node.greater.greater;
int greater = (rr!=null)?rr.height:0;
AVLNode rl = (AVLNode) node.greater.lesser;
int lesser = (rl!=null)?rl.height:0;
if (greater>=lesser) {
rotateLeft(node);
node.updateHeight();
if (node.parent!=null) ((AVLNode)node.parent).updateHeight();
} else {
rotateRight(node.greater);
rotateLeft(node);
AVLNode p = (AVLNode) node.parent;
if (p.lesser!=null) ((AVLNode)p.lesser).updateHeight();
if (p.greater!=null) ((AVLNode)p.greater).updateHeight();
p.updateHeight();
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
protected boolean validateNode(Node node) {
boolean bst = super.validateNode(node);
if (!bst) return false;
AVLNode avlNode = (AVLNode) node;
int balanceFactor = avlNode.getBalanceFactor();
if (balanceFactor>1 || balanceFactor<-1) {
return false;
}
if (avlNode.isLeaf()) {
if (avlNode.height!=1) return false;
} else {
AVLNode avlNodeLesser = (AVLNode) avlNode.lesser;
int lesserHeight = 1;
if (avlNodeLesser!=null) lesserHeight = avlNodeLesser.height;
AVLNode avlNodeGreater = (AVLNode) avlNode.greater;
int greaterHeight = 1;
if (avlNodeGreater!=null) greaterHeight = avlNodeGreater.height;
if (avlNode.height==(lesserHeight+1) || avlNode.height==(greaterHeight+1)) {
return true;
} else {
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return AVLTreePrinter.getString(this);
}
/**
* {@inheritDoc}
*/
@Override
public Node createNewNode(Node parent, T id) {
return (new AVLNode(parent,id));
}
protected static class AVLNode> extends Node {
protected int height = 1;
/**
* Constructor for an AVL node
*
* @param parent Parent of the node in the tree, can be NULL.
* @param value Value of the node in the tree.
*/
protected AVLNode(Node parent, T value) {
super(parent,value);
}
/**
* Determines is this node is a leaf (has no children).
*
* @return True if this node is a leaf.
*/
protected boolean isLeaf() {
return ((lesser == null) && (greater == null));
}
/**
* Updates the height of this node based on it's children.
*/
protected void updateHeight() {
int lesserHeight = 0;
int greaterHeight = 0;
if (lesser != null) {
AVLNode lesserAVLNode = (AVLNode) lesser;
lesserHeight = lesserAVLNode.height;
}
if (greater != null) {
AVLNode greaterAVLNode = (AVLNode) greater;
greaterHeight = greaterAVLNode.height;
}
if (lesserHeight>greaterHeight) {
height = lesserHeight+1;
} else {
height = greaterHeight+1;
}
}
/**
* Get the balance factor for this node.
*
* @return An integer representing the balance factor for this node. It will be
* negative if the lesser branch is longer than the greater branch.
*/
protected int getBalanceFactor() {
int lesserHeight = 0;
int greaterHeight = 0;
if (lesser != null) {
AVLNode lesserAVLNode = (AVLNode) lesser;
lesserHeight = lesserAVLNode.height;
}
if (greater != null) {
AVLNode greaterAVLNode = (AVLNode) greater;
greaterHeight = greaterAVLNode.height;
}
return greaterHeight-lesserHeight;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "value=" + id +
" height=" + height +
" parent=" + ((parent != null) ? parent.id : "NULL") +
" lesser=" + ((lesser != null) ? lesser.id : "NULL") +
" greater=" + ((greater != null) ? greater.id : "NULL");
}
}
protected static class AVLTreePrinter {
public static > String getString(AVLTree tree) {
if (tree.root == null) return "Tree has no nodes.";
return getString((AVLNode)tree.root, "", true);
}
public static > String getString(AVLNode node) {
if (node == null) return "Sub-tree has no nodes.";
return getString(node, "", true);
}
private static > String getString(AVLNode node, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
builder.append(prefix + (isTail ? "└── " : "├── ") + "(" + node.height + ") " + node.id + "\n");
List> children = null;
if (node.lesser != null || node.greater != null) {
children = new ArrayList>(2);
if (node.lesser != null) children.add(node.lesser);
if (node.greater != null) children.add(node.greater);
}
if (children != null) {
for (int i = 0; i < children.size() - 1; i++) {
builder.append(getString((AVLNode)children.get(i), prefix + (isTail ? " " : "│ "), false));
}
if (children.size() >= 1) {
builder.append(getString((AVLNode)children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
}
}
return builder.toString();
}
}
}
BTree 2-3树,分裂和合并部分还是比较简单的
import java.util.Arrays;
import java.util.Comparator;
/**
* B-tree is a tree data structure that keeps data sorted and allows searches,
* sequential access, insertions, and deletions in logarithmic time. The B-tree
* is a generalization of a binary search tree in that a node can have more than
* two children. Unlike self-balancing binary search trees, the B-tree is
* optimized for systems that read and write large blocks of data. It is
* commonly used in databases and file-systems.
*
* http://en.wikipedia.org/wiki/B-tree
*
* @author Justin Wetherell
*/
public class BTree> {
// Default to 2-3 Tree
private int minKeySize = 1;
private int minChildrenSize = minKeySize + 1; //2
private int maxKeySize = 2 * minKeySize; //2
private int maxChildrenSize = maxKeySize + 1; //3
private Node root = null;
private int size = 0;
/**
* Constructor for B-Tree which defaults to a 2-3 B-Tree.
*/
public BTree() { }
/**
* Constructor for B-Tree of ordered parameter.
*
* @param order of the B-Tree.
*/
public BTree(int order) {
this.minKeySize = order;
this.minChildrenSize = minKeySize + 1;
this.maxKeySize = 2 * minKeySize;
this.maxChildrenSize = maxKeySize + 1;
}
/**
* Add value to the tree. Tree can NOT contain multiple equal values.
*
* @param value T to add to the tree.
* @return True if successfully added to tree.
*/
public void add(T value) {
if (root == null) {
root = new Node(null, maxKeySize, maxChildrenSize);
root.addKey(value);
} else {
Node node = root;
while (node != null) {
if (node.numberOfChildren() == 0) {
node.addKey(value);
if (node.numberOfKeys() <= maxKeySize) {
// A-OK
break;
} else {
// Need to split up
split(node);
break;
}
} else {
// navigate
T lesser = node.getKey(0);
if (value.compareTo(lesser) < 0) {
node = node.getChild(0);
continue;
}
int size = node.numberOfKeys();
int last = size - 1;
T greater = node.getKey(last);
if (value.compareTo(greater) > 0) {
node = node.getChild(size);
continue;
}
for (int i = 1; i < node.numberOfKeys(); i++) {
T prev = node.getKey(i - 1);
T next = node.getKey(i);
if (value.compareTo(prev) > 0 && value.compareTo(next) < 0) {
node = node.getChild(i);
break;
}
}
}
}
}
size++;
}
/**
* The node's key size is greater than maxKeySize, split down the middle.
*
* @param node to split.
*/
private void split(Node node) {
int size = node.numberOfKeys();
int medianIndex = size / 2;
T medianValue = node.getKey(medianIndex);
Node left = new Node(null, maxKeySize, maxChildrenSize);
for (int i=0; i0) {
for (int j=0; j<=medianIndex; j++) {
Node c = node.getChild(j);
left.addChild(c);
}
}
Node right = new Node(null, maxKeySize, maxChildrenSize);
for (int i = medianIndex+1; i < size; i++) {
right.addKey(node.getKey(i));
}
if (node.numberOfChildren()>0) {
for (int j=medianIndex+1; j c = node.getChild(j);
right.addChild(c);
}
}
if (node.parent == null) {
// new root, height of tree is increased
Node newRoot = new Node(null, maxKeySize, maxChildrenSize);
newRoot.addKey(medianValue);
node.parent = newRoot;
root = newRoot;
node = root;
node.addChild(left);
node.addChild(right);
} else {
// Move the median value up to the parent
Node parent = node.parent;
parent.addKey(medianValue);
parent.removeChild(node);
parent.addChild(left);
parent.addChild(right);
if (parent.numberOfKeys() > maxKeySize) split(parent);
}
}
/**
* Does the tree contain the value.
*
* @param value T to locate in the tree.
* @return True if tree contains value.
*/
public boolean contains(T value) {
Node node = getNode(value);
return (node != null);
}
/**
* Get the node with value.
*
* @param value to find in the tree.
* @return Node with value.
*/
private Node getNode(T value) {
Node node = root;
while (node != null) {
T lesser = node.getKey(0);
if (value.compareTo(lesser) < 0) {
if (node.numberOfChildren() > 0) node = node.getChild(0);
else node = null;
continue;
}
int size = node.numberOfKeys();
int last = size - 1;
T greater = node.getKey(last);
if (value.compareTo(greater) > 0) {
if (node.numberOfChildren() > size) node = node.getChild(size);
else node = null;
continue;
}
for (int i = 0; i < size; i++) {
T currentValue = node.getKey(i);
if (currentValue.compareTo(value) == 0) {
return node;
}
int next = i + 1;
if (next <= last) {
T nextValue = node.getKey(next);
if (currentValue.compareTo(value) < 0 && nextValue.compareTo(value) > 0) {
if (next < node.numberOfChildren()) {
node = node.getChild(next);
break;
} else {
return null;
}
}
}
}
}
return null;
}
/**
* Remove the value from the tree.
*
* @param value T to remove from the tree.
* @return True if value was removed from the tree.
*/
public boolean remove(T value) {
Node node = this.getNode(value);
if (node == null) return false;
int index = node.indexOf(value);
node.removeKey(value);
if (node.numberOfChildren()==0) {
//leaf node
if (node.parent!=null && node.numberOfKeys() < minKeySize) {
this.combined(node);
} else if (node.parent==null && node.numberOfKeys()==0) {
//Removing root node with no keys or children
root = null;
}
} else {
//internal node
Node lesser = node.getChild(index);
Node greatest = this.getGreatestNode(lesser);
T replaceValue = this.removeGreatestValue(greatest);
node.addKey(replaceValue);
if (greatest.parent!=null && greatest.numberOfKeys() < minKeySize) {
this.combined(greatest);
}
if (greatest.numberOfChildren() > maxChildrenSize) {
this.split(greatest);
}
}
size--;
return true;
}
/**
* Remove greatest valued key from node.
*
* @param node to remove greatest value from.
* @return value removed;
*/
private T removeGreatestValue(Node node) {
T value = null;
if (node.numberOfKeys()>0) {
value = node.removeKey(node.numberOfKeys()-1);
}
return value;
}
/**
* Get the greatest valued child from node.
*
* @param node child with the greatest value.
* @return Node child with greatest value.
*/
private Node getGreatestNode(Node node) {
while (node.numberOfChildren()>0) {
node = node.getChild(node.numberOfChildren()-1);
}
return node;
}
/**
* Combined children keys with parent when size is less than minKeySize.
*
* @param node with children to combined.
* @return True if combined successfully.
*/
private boolean combined(Node node) {
Node parent = node.parent;
int index = parent.indexOf(node);
int indexOfLeftNeighbor = index - 1;
int indexOfRightNeighbor = index + 1;
Node rightNeighbor = null;
int rightNeighborSize = -minChildrenSize;
if (indexOfRightNeighbor < parent.numberOfChildren()) {
rightNeighbor = parent.getChild(indexOfRightNeighbor);
rightNeighborSize = rightNeighbor.numberOfKeys();
}
// Try to borrow neighbor
if (rightNeighbor != null && rightNeighborSize > minKeySize) {
// Try to borrow from right neighbor
T removeValue = rightNeighbor.getKey(0);
int prev = getIndexOfPreviousValue(parent, removeValue);
T parentValue = parent.removeKey(prev);
T neighborValue = rightNeighbor.removeKey(0);
node.addKey(parentValue);
parent.addKey(neighborValue);
if (rightNeighbor.numberOfChildren()>0) {
node.addChild(rightNeighbor.removeChild(0));
}
} else {
Node leftNeighbor = null;
int leftNeighborSize = -minChildrenSize;
if (indexOfLeftNeighbor >= 0) {
leftNeighbor = parent.getChild(indexOfLeftNeighbor);
leftNeighborSize = leftNeighbor.numberOfKeys();
}
if (leftNeighbor != null && leftNeighborSize > minKeySize) {
// Try to borrow from left neighbor
T removeValue = leftNeighbor.getKey(leftNeighbor.numberOfKeys() - 1);
int prev = getIndexOfNextValue(parent, removeValue);
T parentValue = parent.removeKey(prev);
T neighborValue = leftNeighbor.removeKey(leftNeighbor.numberOfKeys() - 1);
node.addKey(parentValue);
parent.addKey(neighborValue);
if (leftNeighbor.numberOfChildren()>0) {
node.addChild(leftNeighbor.removeChild(leftNeighbor.numberOfChildren()-1));
}
} else if (rightNeighbor != null && parent.numberOfKeys() > 0) {
// Can't borrow from neighbors, try to combined with right neighbor
T removeValue = rightNeighbor.getKey(0);
int prev = getIndexOfPreviousValue(parent, removeValue);
T parentValue = parent.removeKey(prev);
parent.removeChild(rightNeighbor);
node.addKey(parentValue);
for (int i=0; i c = rightNeighbor.getChild(i);
node.addChild(c);
}
if (parent.parent != null && parent.numberOfKeys() < minKeySize) {
// removing key made parent too small, combined up tree
this.combined(parent);
} else if (parent.numberOfKeys() == 0) {
// parent no longer has keys, make this node the new root
// which decreases the height of the tree
node.parent = null;
root = node;
}
} else if (leftNeighbor != null && parent.numberOfKeys() > 0) {
// Can't borrow from neighbors, try to combined with left neighbor
T removeValue = leftNeighbor.getKey(leftNeighbor.numberOfKeys() - 1);
int prev = getIndexOfNextValue(parent, removeValue);
T parentValue = parent.removeKey(prev);
parent.removeChild(leftNeighbor);
node.addKey(parentValue);
for (int i=0; i c = leftNeighbor.getChild(i);
node.addChild(c);
}
if (parent.parent != null && parent.numberOfKeys() < minKeySize) {
// removing key made parent too small, combined up tree
this.combined(parent);
} else if (parent.numberOfKeys() == 0) {
// parent no longer has keys, make this node the new root
// which decreases the height of the tree
node.parent = null;
root = node;
}
}
}
return true;
}
/**
* Get the index of previous key in node.
*
* @param node to find the previous key in.
* @param value to find a previous value for.
* @return index of previous key or -1 if not found.
*/
private int getIndexOfPreviousValue(Node node, T value) {
for (int i = 1; i < node.numberOfKeys(); i++) {
T t = node.getKey(i);
if (t.compareTo(value) >= 0) return i - 1;
}
return node.numberOfKeys() - 1;
}
/**
* Get the index of next key in node.
*
* @param node to find the next key in.
* @param value to find a next value for.
* @return index of next key or -1 if not found.
*/
private int getIndexOfNextValue(Node node, T value) {
for (int i = 0; i < node.numberOfKeys(); i++) {
T t = node.getKey(i);
if (t.compareTo(value) >= 0) return i;
}
return node.numberOfKeys() - 1;
}
/**
* Get number of nodes in the tree.
*
* @return Number of nodes in the tree.
*/
public int size() {
return size;
}
/**
* Validate the tree for all B-Tree invariants.
*
* @return True if tree is valid.
*/
public boolean validate() {
if (root == null) return true;
return validateNode(root);
}
/**
* Validate the node according to the B-Tree invariants.
*
* @param node to validate.
* @return True if valid.
*/
private boolean validateNode(Node node) {
int keySize = node.numberOfKeys();
if (keySize > 1) {
// Make sure the keys are sorted
for (int i = 1; i < keySize; i++) {
T p = node.getKey(i - 1);
T n = node.getKey(i);
if (p.compareTo(n) > 0) return false;
}
}
int childrenSize = node.numberOfChildren();
if (node.parent == null) {
//root
if (keySize > maxKeySize) {
// check max key size. root does not have a min key size
return false;
} else if (childrenSize==0) {
// if root, no children, and keys are valid
return true;
} else if (childrenSize < 2) {
// root should have zero or at least two children
return false;
} else if (childrenSize > maxChildrenSize) {
return false;
}
} else {
//non-root
if (keySize < minKeySize) {
return false;
} else if (keySize > maxKeySize) {
return false;
} else if (childrenSize==0) {
return true;
} else if (keySize != (childrenSize - 1)) {
// If there are chilren, there should be one more child then keys
return false;
} else if (childrenSize < minChildrenSize) {
return false;
} else if (childrenSize > maxChildrenSize) {
return false;
}
}
Node first = node.getChild(0);
// The first child's last key should be less than the node's first key
if (first.getKey(first.numberOfKeys() - 1).compareTo(node.getKey(0)) > 0) return false;
Node last = node.getChild(node.numberOfChildren() - 1);
// The last child's first key should be greater than the node's last key
if (last.getKey(0).compareTo(node.getKey(node.numberOfKeys() - 1)) < 0) return false;
// Check that each node's first and last key holds it's invariance
for (int i = 1; i < node.numberOfKeys(); i++) {
T p = node.getKey(i - 1);
T n = node.getKey(i);
Node c = node.getChild(i);
if (p.compareTo(c.getKey(0)) > 0) return false;
if (n.compareTo(c.getKey(c.numberOfKeys() - 1)) < 0) return false;
}
for (int i=0; i c = node.getChild(i);
boolean valid = this.validateNode(c);
if (!valid) return false;
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return TreePrinter.getString(this);
}
private static class Node> {
private T[] keys = null;
private int keysSize = 0;
private Node[] children = null;
private int childrenSize = 0;
private Comparator> comparator = new Comparator>() {
@Override
public int compare(Node arg0, Node arg1) {
return arg0.getKey(0).compareTo(arg1.getKey(0));
}
};
protected Node parent = null;
@SuppressWarnings("unchecked")
private Node(Node parent, int maxKeySize, int maxChildrenSize) {
this.parent = parent;
this.keys = (T[]) new Comparable[maxKeySize+1];
this.keysSize = 0;
this.children = new Node[maxChildrenSize+1];
this.childrenSize = 0;
}
private T getKey(int index) {
return keys[index];
}
private int indexOf(T value) {
for (int i=0; i=keysSize) return null;
T value = keys[index];
keys[index] = null;
for (int i=index+1; i getChild(int index) {
if (index>=childrenSize) return null;
return children[index];
}
private int indexOf(Node child) {
for (int i=0; i child) {
child.parent = this;
children[childrenSize++] = child;
Arrays.sort(children,0,childrenSize,comparator);
return true;
}
private boolean removeChild(Node child) {
boolean found = false;
if (childrenSize==0) return found;
for (int i=0; i removeChild(int index) {
if (index>=childrenSize) return null;
Node value = children[index];
children[index] = null;
for (int i=index+1; i> String getString(BTree tree) {
if (tree.root == null) return "Tree has no nodes.";
return getString(tree.root, "", true);
}
private static > String getString(Node node, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
builder.append(prefix).append((isTail ? "└── " : "├── "));
for (int i = 0; i < node.numberOfKeys(); i++) {
T value = node.getKey(i);
builder.append(value);
if (i < node.numberOfKeys() - 1) builder.append(", ");
}
builder.append("\n");
if (node.children != null) {
for (int i = 0; i < node.numberOfChildren() - 1; i++) {
Node obj = node.getChild(i);
builder.append(getString(obj, prefix + (isTail ? " " : "│ "), false));
}
if (node.numberOfChildren() >= 1) {
Node obj = node.getChild(node.numberOfChildren() - 1);
builder.append(getString(obj, prefix + (isTail ? " " : "│ "), true));
}
}
return builder.toString();
}
}
}
二叉堆,最简单的树结构实现了
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A binary heap is a heap data structure created using a binary tree. It can be
* seen as a binary tree with two additional constraints: 1) The shape property:
* the tree is a complete binary tree; that is, all levels of the tree, except
* possibly the last one (deepest) are fully filled, and, if the last level of
* the tree is not complete, the nodes of that level are filled from left to
* right. 2) The heap property: each node is right than or equal to each of its
* children according to a comparison predicate defined for the data structure.
*
* http://en.wikipedia.org/wiki/Binary_heap
*
* @author Justin Wetherell
*/
public abstract class BinaryHeap> {
public enum HeapType { Tree, Array };
public enum Type { MIN, MAX };
/**
* Number of nodes in the heap.
*
* @return Number of nodes in the heap.
*/
public abstract int size();
/**
* Add value to the heap.
*
* @param value to add to the heap.
*/
public abstract void add(T value);
/**
* Does the value exist in the heap. Warning
* this is a O(n) operation.
*
* @param value to locate in the heap.
* @return True if the value is in heap.
*/
public abstract boolean contains(T value);
/**
* Get the heap in array form.
*
* @return array representing the heap.
*/
public abstract T[] getHeap();
/**
* Get the value of the head node from the heap.
*
* @return value of the head node.
*/
public abstract T getHeadValue();
/**
* Remove the head node from the heap.
*
* @return value of the head node.
*/
public abstract T removeHead();
/**
* Validate the heap according to the invariants.
*
* @return True if the heap is valid.
*/
public abstract boolean validate();
public static > BinaryHeap createHeap(HeapType type) {
switch (type) {
case Array:
return new BinaryHeapArray();
default:
return new BinaryHeapTree();
}
}
/**
* A binary heap using an array to hold the nodes.
*
* @author Justin Wetherell
*/
public static class BinaryHeapArray> extends BinaryHeap {
private static final int MINIMUM_SIZE = 10;
private Type type = Type.MIN;
private int size = 0;
@SuppressWarnings("unchecked")
private T[] array = (T[]) new Comparable[MINIMUM_SIZE];
/**
* Get the parent index of this index, will return Integer.MIN_VALUE if no parent
* is possible.
*
* @param index of the node to find a parent for.
* @return index of parent node or Integer.MIN_VALUE if no parent.
*/
private static final int getParentIndex(int index) {
if (index>0) return (int) Math.floor((index-1)/2);
else return Integer.MIN_VALUE;
}
/**
* Get the left child index of this index.
*
* @param index of the node to find a left child for.
* @return index of left child node.
*/
private static final int getLeftIndex(int index) {
return 2*index+1;
}
/**
* Get the right child index of this index.
*
* @param index of the node to find a right child for.
* @return index of right child node.
*/
private static final int getRightIndex(int index) {
return 2*index+2;
}
/**
* Constructor for heap, defaults to a min-heap.
*/
public BinaryHeapArray() {
size = 0;
}
/**
* Constructor for heap.
*
* @param type Heap type.
*/
public BinaryHeapArray(Type type) {
this();
this.type = type;
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return size;
}
/**
* {@inheritDoc}
*/
@Override
public void add(T value) {
if (size>=array.length) {
array = Arrays.copyOf(array, ((size*3)/2)+1);
}
array[size] = value;
heapUp(size++);
}
protected void heapUp(int nodeIndex) {
T value = this.array[nodeIndex];
while (nodeIndex>=0) {
int parentIndex = getParentIndex(nodeIndex);
if (parentIndex<0) break;
T parent = this.array[parentIndex];
if ( (type == Type.MIN && parent != null && value.compareTo(parent) < 0) ||
(type == Type.MAX && parent != null && value.compareTo(parent) > 0)
){
// Node is less than parent, switch node with parent
this.array[parentIndex] = value;
this.array[nodeIndex] = parent;
} else {
nodeIndex = parentIndex;
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(T value) {
if (array.length==0) return false;
for (int i=0; i 0)) {
return validateNode(leftIndex);
} else {
return false;
}
}
if (rightIndex!=Integer.MIN_VALUE && rightIndex 0)) {
return validateNode(rightIndex);
} else {
return false;
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public T[] getHeap() {
T[] nodes = (T[]) new Comparable[size];
if (array.length==0) return nodes;
for (int i=0; i=MINIMUM_SIZE && size 0 && value.compareTo(right) > 0) ||
(type == Type.MAX && left != null && right != null && value.compareTo(left) < 0 && value.compareTo(right) < 0)
) {
// Both children are greater/lesser than node
if ((type == Type.MIN && right.compareTo(left) < 0) ||
(type == Type.MAX && right.compareTo(left) > 0)
) {
// Right is greater/lesser than left
nodeToMove = right;
nodeToMoveIndex = rightIndex;
} else if ( (type == Type.MIN && left.compareTo(right) < 0) ||
(type == Type.MAX && left.compareTo(right) > 0)
){
// Left is greater/lesser than right
nodeToMove = left;
nodeToMoveIndex = leftIndex;
} else {
// Both children are equal, use right
nodeToMove = right;
nodeToMoveIndex = rightIndex;
}
} else if ( (type == Type.MIN && right != null && value.compareTo(right) > 0) ||
(type == Type.MAX && right != null && value.compareTo(right) < 0)
) {
// Right is greater/lesser than node
nodeToMove = right;
nodeToMoveIndex = rightIndex;
} else if ( (type == Type.MIN && left != null && value.compareTo(left) > 0) ||
(type == Type.MAX && left != null && value.compareTo(left) < 0)
) {
// Left is greater/lesser than node
nodeToMove = left;
nodeToMoveIndex = leftIndex;
}
// No node to move, stop recursion
if (nodeToMove == null) return;
// Re-factor heap sub-tree
this.array[nodeToMoveIndex] = value;
this.array[index] = nodeToMove;
heapDown(nodeToMoveIndex);
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return HeapPrinter.getString(this);
}
protected static class HeapPrinter {
public static > String getString(BinaryHeapArray tree) {
if (tree.array.length==0) return "Tree has no nodes.";
T root = tree.array[0];
if (root == null) return "Tree has no nodes.";
return getString(tree, 0, "", true);
}
private static > String getString(BinaryHeapArray tree, int index, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
T value = tree.array[index];
builder.append(prefix + (isTail ? "└── " : "├── ") + value + "\n");
List children = null;
int leftIndex = getLeftIndex(index);
int rightIndex = getRightIndex(index);
if (leftIndex != Integer.MIN_VALUE || rightIndex != Integer.MIN_VALUE) {
children = new ArrayList(2);
if (leftIndex != Integer.MIN_VALUE && leftIndex= 1) {
builder.append(getString(tree, children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
}
}
return builder.toString();
}
}
}
public static class BinaryHeapTree> extends BinaryHeap {
private Type type = Type.MIN;
private int size = 0;
private Node root = null;
/**
* Constructor for heap, defaults to a min-heap.
*/
public BinaryHeapTree() {
root = null;
size = 0;
}
/**
* Constructor for heap.
*
* @param type Heap type.
*/
public BinaryHeapTree(Type type) {
this();
this.type = type;
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
return size;
}
/**
* Get the navigation directions through the tree to the index.
*
* @param index of the Node to get directions for.
* @return Integer array representing the directions to the index.
*/
private int[] getDirections(int index) {
int directionsSize = (int) (Math.log10(index + 1) / Math.log10(2)) - 1;
int[] directions = null;
if (directionsSize > 0) {
directions = new int[directionsSize];
int i = directionsSize - 1;
while (i >= 0) {
index = (index - 1) / 2;
directions[i--] = (index > 0 && index % 2 == 0) ? 1 : 0; // 0=left,
// 1=right
}
}
return directions;
}
/**
* {@inheritDoc}
*/
@Override
public void add(T value) {
add(new Node(null, value));
}
private void add(Node newNode) {
if (root == null) {
root = newNode;
size++;
return;
}
Node node = root;
int[] directions = getDirections(size); // size == index of new node
if (directions != null && directions.length > 0) {
for (int d : directions) {
if (d == 0) {
// Go left
node = node.left;
} else {
// Go right
node = node.right;
}
}
}
if (node.left == null) {
node.left = newNode;
} else {
node.right = newNode;
}
newNode.parent = node;
size++;
heapUp(newNode);
}
/**
* Remove the root node.
*/
private void removeRoot() {
// Find the last node
int[] directions = getDirections(size - 1); // Directions to the last node
Node lastNode = root;
if (directions != null && directions.length > 0) {
for (int d : directions) {
if (d == 0) {
// Go left
lastNode = lastNode.left;
} else {
// Go right
lastNode = lastNode.right;
}
}
}
Node lastNodeParent = null;
if (lastNode.right != null) {
lastNodeParent = lastNode;
lastNode = lastNode.right;
lastNodeParent.right = null;
} else if (lastNode.left != null) {
lastNodeParent = lastNode;
lastNode = lastNode.left;
lastNodeParent.left = null;
}
lastNode.left = root.left;
if (lastNode.left != null) lastNode.left.parent = lastNode;
lastNode.right = root.right;
if (lastNode.right != null) lastNode.right.parent = lastNode;
lastNode.parent = null;
if (!lastNode.equals(root)) root = lastNode;
else root = null;
size--;
heapDown(lastNode);
}
/**
* Get the node in the startingNode sub-tree which has the value.
*
* @param startingNode node rooted sub-tree to search in.
* @param value to search for.
* @return Node which equals value in sub-tree or NULL if not found.
*/
private Node getNode(Node startingNode, T value) {
Node result = null;
if (startingNode != null && startingNode.value.equals(value)) {
result = startingNode;
} else if (startingNode != null && !startingNode.value.equals(value)) {
Node left = startingNode.left;
if (left != null) {
result = getNode(left, value);
if (result != null) return result;
}
Node right = startingNode.right;
if (right != null) {
result = getNode(right, value);
if (result != null) return result;
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(T value) {
if (root==null) return false;
Node node = getNode(root, value);
return (node!=null);
}
/**
* Heap up the heap from this node.
*
* @param node to heap up.
*/
protected void heapUp(Node node) {
while (node != null) {
Node heapNode = (Node) node;
Node parent = heapNode.parent;
if ( (type == Type.MIN && parent != null && node.value.compareTo(parent.value) < 0) ||
(type == Type.MAX && parent != null && node.value.compareTo(parent.value) > 0)
){
// Node is less than parent, switch node with parent
Node grandParent = parent.parent;
Node parentLeft = parent.left;
Node parentRight = parent.right;
parent.left = heapNode.left;
if (parent.left != null) parent.left.parent = parent;
parent.right = heapNode.right;
if (parent.right != null) parent.right.parent = parent;
if (parentLeft != null && parentLeft.equals(node)) {
heapNode.left = parent;
heapNode.right = parentRight;
if (parentRight != null) parentRight.parent = heapNode;
} else {
heapNode.right = parent;
heapNode.left = parentLeft;
if (parentLeft != null) parentLeft.parent = heapNode;
}
parent.parent = heapNode;
if (grandParent == null) {
// New root.
heapNode.parent = null;
root = heapNode;
} else {
Node grandLeft = grandParent.left;
if (grandLeft != null && grandLeft.equals(parent)) {
grandParent.left = heapNode;
} else {
grandParent.right = heapNode;
}
heapNode.parent = grandParent;
}
} else {
node = heapNode.parent;
}
}
}
/**
* Heap down the heap from this node.
*
* @param node to heap down.
*/
protected void heapDown(Node node) {
Node heapNode = (Node) node;
Node left = heapNode.left;
Node right = heapNode.right;
if (left == null && right == null) {
// Nothing to do here
return;
}
Node nodeToMove = null;
if ( (type == Type.MIN && left != null && right != null && node.value.compareTo(left.value) > 0 && node.value.compareTo(right.value) > 0) ||
(type == Type.MAX && left != null && right != null && node.value.compareTo(left.value) < 0 && node.value.compareTo(right.value) < 0)
) {
// Both children are greater/lesser than node
if ((type == Type.MIN && right.value.compareTo(left.value) < 0) ||
(type == Type.MAX && right.value.compareTo(left.value) > 0)
) {
// Right is greater/lesser than left
nodeToMove = right;
} else if ( (type == Type.MIN && left.value.compareTo(right.value) < 0) ||
(type == Type.MAX && left.value.compareTo(right.value) > 0)
){
// Left is greater/lesser than right
nodeToMove = left;
} else {
// Both children are equal, use right
nodeToMove = right;
}
} else if ( (type == Type.MIN && right != null && node.value.compareTo(right.value) > 0) ||
(type == Type.MAX && right != null && node.value.compareTo(right.value) < 0)
) {
// Right is greater than node
nodeToMove = right;
} else if ( (type == Type.MIN && left != null && node.value.compareTo(left.value) > 0) ||
(type == Type.MAX && left != null && node.value.compareTo(left.value) < 0)
) {
// Left is greater than node
nodeToMove = left;
}
// No node to move, stop recursion
if (nodeToMove == null) return;
// Re-factor heap sub-tree
Node nodeParent = heapNode.parent;
if (nodeParent == null) {
// heap down the root
root = nodeToMove;
root.parent = null;
Node nodeToMoveLeft = nodeToMove.left;
Node nodeToMoveRight = nodeToMove.right;
if (heapNode.left.equals(nodeToMove)) {
nodeToMove.left = heapNode;
nodeToMove.right = heapNode.right;
} else {
nodeToMove.left = heapNode.left;
nodeToMove.right = heapNode;
}
heapNode.parent = nodeToMove;
heapNode.left = nodeToMoveLeft;
heapNode.right = nodeToMoveRight;
} else {
// heap down a left
if (nodeParent.left.equals(node)) {
nodeParent.left = nodeToMove;
nodeToMove.parent = nodeParent;
} else {
nodeParent.right = nodeToMove;
nodeToMove.parent = nodeParent;
}
Node nodeLeft = heapNode.left;
Node nodeRight = heapNode.right;
Node nodeToMoveLeft = nodeToMove.left;
Node nodeToMoveRight = nodeToMove.right;
if (heapNode.left.equals(nodeToMove)) {
nodeToMove.right = nodeRight;
if (nodeRight != null) nodeRight.parent = nodeToMove;
nodeToMove.left = heapNode;
heapNode.parent = nodeToMove;
} else {
nodeToMove.left = nodeLeft;
if (nodeLeft != null) nodeLeft.parent = nodeToMove;
nodeToMove.right = heapNode;
heapNode.parent = nodeToMove;
}
heapNode.left = nodeToMoveLeft;
if (nodeToMoveLeft != null) nodeToMoveLeft.parent = heapNode;
heapNode.right = nodeToMoveRight;
if (nodeToMoveRight != null) nodeToMoveRight.parent = heapNode;
}
heapDown(node);
}
/**
* {@inheritDoc}
*/
@Override
public boolean validate() {
if (root==null) return true;
return validateNode(root);
}
/**
* Validate node for heap invariants.
*
* @param node to validate for.
* @return True if node is valid.
*/
private boolean validateNode(Node node) {
Node left = ((Node)node).left;
Node right = ((Node)node).right;
//We shouldn't ever have a right node without a left in a heap
if (right!=null && left==null) return false;
if (left!=null) {
if ((type == Type.MIN && node.value.compareTo(left.value) < 0) || (type == Type.MAX && node.value.compareTo(left.value) > 0)) {
return validateNode(left);
} else {
return false;
}
}
if (right!=null) {
if ((type == Type.MIN && node.value.compareTo(right.value) < 0) || (type == Type.MAX && node.value.compareTo(right.value) > 0)) {
return validateNode(right);
} else {
return false;
}
}
return true;
}
/**
* Populate the node in the array at the index.
*
* @param node to populate.
* @param index of node in array.
* @param array where the node lives.
*/
private void getNodeValue(Node node, int index, T[] array) {
array[index] = node.value;
index = (index * 2) + 1;
Node left = ((Node)node).left;
if (left != null) getNodeValue(left, index, array);
Node right = ((Node)node).right;
if (right != null) getNodeValue(right, index + 1, array);
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public T[] getHeap() {
T[] nodes = (T[]) new Comparable[size];
if (root != null) getNodeValue(root, 0, nodes);
return nodes;
}
/**
* {@inheritDoc}
*/
@Override
public T getHeadValue() {
T result = null;
if (root != null) result = root.value;
return result;
}
/**
* {@inheritDoc}
*/
@Override
public T removeHead() {
T result = null;
if (root != null) {
result = root.value;
removeRoot();
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return HeapPrinter.getString(this);
}
protected static class HeapPrinter {
public static > void print(BinaryHeapTree tree) {
System.out.println(getString(tree.root, "", true));
}
public static > String getString(BinaryHeapTree tree) {
if (tree.root == null) return "Tree has no nodes.";
return getString(tree.root, "", true);
}
private static > String getString(Node node, String prefix, boolean isTail) {
StringBuilder builder = new StringBuilder();
builder.append(prefix + (isTail ? "└── " : "├── ") + node.value + "\n");
List> children = null;
if (node.left != null || node.right != null) {
children = new ArrayList>(2);
if (node.left != null) children.add(node.left);
if (node.right != null) children.add(node.right);
}
if (children != null) {
for (int i = 0; i < children.size() - 1; i++) {
builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false));
}
if (children.size() >= 1) {
builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true));
}
}
return builder.toString();
}
}
private static class Node> {
private T value = null;
private Node parent = null;
private Node left = null;
private Node right = null;
private Node(Node parent, T value) {
this.value = value;
this.parent = parent;
}
/**
* {@inheritDoc}
*/
@Override
public String toString() {
return "value=" + value +
" parent=" + ((parent != null) ? parent.value : "NULL") +
" left=" + ((left != null) ? left.value : "NULL") +
" right=" + ((right != null) ? right.value : "NULL");
}
}
}
}
后续把学习完的数据结构的其他树结构再贴出来。