// AvlTree.java // // Author: // Antonio J. Nebro <antonio@lcc.uma.es> // Juan J. Durillo <durillo@lcc.uma.es> // // Copyright (c) 2011 Antonio J. Nebro, Juan J. Durillo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. // package jmetal.util.avl; import java.util.Comparator; /** * Created with IntelliJ IDEA. * User: Antonio J. Nebro * Date: 08/07/13 * Time: 15:51 * Class implementing Avl trees. */ public class AvlTree<T> { AvlNode<T> top_; Comparator comparator_; /** * Constructor * * @param comparator_ */ public AvlTree(Comparator comparator_) { top_ = null; this.comparator_ = comparator_; } public void insert(T item) { AvlNode<T> node = new AvlNode<T>(item); insertAvlNode(node); } public void insertAvlNode(AvlNode node) { if (AvlIsEmpty()) { insertTop(node); } else { AvlNode<T> closestNode = null; int result = searchClosestNode(node); switch (result) { case -1: insertNodeLeft(node); break; case +1: insertNodeRight(node); ; break; default: ; } } } public AvlNode<T> search(T item) { AvlNode<T> node = new AvlNode<T>(item); return searchNode(node); } public AvlNode<T> searchNode(AvlNode<T> targetNode) { AvlNode<T> currentNode; AvlNode<T> result = null; currentNode = top_; if (top_ == null) { result = null; } else { boolean searchFinished; int comparison; searchFinished = false; while (!searchFinished) { comparison = compareNodes(targetNode, currentNode); if (comparison < 0) { if (currentNode.getLeft() != null) { currentNode = currentNode.getLeft(); } else { searchFinished = true; result = null; } } else if (comparison > 0) { if (currentNode.getRight() != null) { currentNode = currentNode.getRight(); } else { searchFinished = true; result = null; } } else { searchFinished = true; result = currentNode; } } } return result; } public void delete(T item) { deleteNode(new AvlNode<T>(item)); } public void deleteNode(AvlNode<T> node) { AvlNode<T> nodeFound; nodeFound = searchNode(node); if (nodeFound != null) { if (nodeFound.isLeaf()) { deleteLeafNode(nodeFound); } else if (nodeFound.hasOnlyALeftChild()) { deleteNodeWithALeftChild(nodeFound); } else if (nodeFound.hasOnlyARightChild()) { deleteNodeWithARightChild(nodeFound); } else { // has two children AvlNode<T> successor = findSuccessor(nodeFound); T tmp = successor.getItem(); successor.setItem(nodeFound.getItem()); nodeFound.setItem(tmp); if (successor.isLeaf()) { deleteLeafNode(successor); } else if (successor.hasOnlyALeftChild()) { deleteNodeWithALeftChild(successor); } else if (successor.hasOnlyARightChild()) { deleteNodeWithARightChild(successor); } } } } public void deleteLeafNode(AvlNode<T> node) { if (!node.hasParent()) { top_ = null; } else { if (node.getParent().getLeft() == node) { node.getParent().setLeft(null); } else { node.getParent().setRight(null); } node.getParent().updateHeight(); rebalance(node.getParent()); } } public void deleteNodeWithALeftChild(AvlNode<T> node) { node.setItem((T) node.getLeft().getItem()); node.setLeft(null); node.updateHeight(); rebalance(node); } public void deleteNodeWithARightChild(AvlNode<T> node) { node.setItem((T) node.getRight().getItem()); node.setRight(null); node.updateHeight(); rebalance(node); } /** * Searches for the closest node of the node passed as argument * * @param node * @return -1 if node has to be inserted in the left, +1 if it must be * inserted in the right, 0 otherwise */ public int searchClosestNode(AvlNode node) { AvlNode<T> currentNode; int result = 0; currentNode = top_; if (top_ == null) { result = 0; } else { int comparison; boolean notFound = true; while (notFound) { comparison = compareNodes(node, currentNode); if (comparison < 0) { if (currentNode.hasLeft()) { currentNode = currentNode.getLeft(); } else { notFound = false; node.setClosestNode_(currentNode); result = -1; } } else if (comparison > 0) { if (currentNode.hasRight()) { currentNode = currentNode.getRight(); } else { notFound = false; node.setClosestNode_(currentNode); result = 1; } } else { notFound = false; node.setClosestNode_(currentNode); result = 0; } } } return result; } public AvlNode<T> findSuccessor(AvlNode<T> node) { AvlNode<T> result = null; if (node.hasRight()) { AvlNode<T> tmp = node.getRight(); while (tmp.hasLeft()) tmp = tmp.getLeft(); result = tmp; } else { while (node.hasParent() && (node.getParent().getRight() == node)) { node = node.getParent(); } result = node.getParent(); } return result; } /** * Insert node in the left of its nearest node * * @param node REQUIRES: a previous call to searchClosestNode(node) */ public void insertNodeLeft(AvlNode<T> node) { node.getClosestNode().setLeft(node); node.setParent(node.getClosestNode()); rebalance(node); } /** * Insert node in the right of its nearest node * * @param node REQUIRES: a previous call to searchClosestNode(node) */ public void insertNodeRight(AvlNode<T> node) { node.getClosestNode().setRight(node); node.setParent(node.getClosestNode()); rebalance(node); } /** * Comparator * * @param node1 * @param node2 * @return -1 if node1 < node2, +1 if node1 > node2; 0 if node1 == node2 */ public int compareNodes(AvlNode node1, AvlNode node2) { return comparator_.compare(node1.getItem(), node2.getItem()); } public void rebalance(AvlNode<T> node) { AvlNode<T> currentNode; boolean notFinished; currentNode = node; notFinished = true; while (notFinished) { //setBalance(currentNode); if (getBalance(currentNode) == -2) { if (height(currentNode.getLeft().getLeft()) >= height(currentNode.getLeft().getRight())) { leftRotation(currentNode); } else { doubleLeftRotation(currentNode); } } if (getBalance(currentNode) == 2) { if (height(currentNode.getRight().getRight()) >= height(currentNode.getRight().getLeft())) { rightRotation(currentNode); } else doubleRightRotation(currentNode); } //currentNode.updateHeight(); if (currentNode.hasParent()) { currentNode.getParent().updateHeight(); currentNode = currentNode.getParent(); } else { setTop(currentNode); notFinished = false; } } } public void leftRotation(AvlNode<T> node) { AvlNode<T> leftNode = node.getLeft(); if (node.hasParent()) { leftNode.setParent(node.getParent()); if (node.getParent().getLeft() == node) node.getParent().setLeft(leftNode); else { node.getParent().setRight(leftNode); } } else { setTop(leftNode); } node.setLeft(node.getLeft().getRight()); leftNode.setRight(node); node.setParent(leftNode); node.updateHeight(); leftNode.updateHeight(); } public void rightRotation(AvlNode<T> node) { AvlNode<T> rightNode = node.getRight(); if (node.hasParent()) { rightNode.setParent(node.getParent()); if (node.getParent().getRight() == node) node.getParent().setRight(rightNode); else node.getParent().setLeft(rightNode); } else { setTop(rightNode); } node.setRight(node.getRight().getLeft()); rightNode.setLeft(node); node.setParent(rightNode); node.updateHeight(); rightNode.updateHeight(); } public void doubleLeftRotation(AvlNode<T> node) { AvlNode<T> leftNode = node.getLeft(); rightRotation(leftNode); leftRotation(node); } public void doubleRightRotation(AvlNode<T> node) { AvlNode<T> rightNode = node.getRight(); leftRotation(rightNode); rightRotation(node); } public int getBalance(AvlNode<T> node) { int leftHeight; int rightHeight; leftHeight = 0; rightHeight = 0; if (node.hasLeft()) { leftHeight = node.getLeft().getHeight(); } else { leftHeight = -1; } if (node.hasRight()) { rightHeight = node.getRight().getHeight(); } else { rightHeight = -1; } return rightHeight - leftHeight; } public boolean AvlIsEmpty() { return (top_ == null); } public void insertTop(AvlNode node) { top_ = node; } public AvlNode<T> getTop() { return top_; } public void setTop(AvlNode<T> top) { this.top_ = top; this.top_.setParent(null); } public int height(AvlNode<T> node) { int result = 0; if (node == null) result = -1; else { result = node.getHeight(); } return result; } public String toString() { String result = ""; result = inOrder(top_); return result; } private String inOrder(AvlNode<T> node) { String result; if (node == null) { return ""; } else { result = ""; result = " | " + node.getItem(); result += inOrder(node.getLeft()); result += inOrder(node.getRight()); return result; } } }