写到后来,写吐了..... 跟教材的相比,还是教材的思路更清晰一些,我这个能跑起来,但是写的过程太痛苦
package indi.tom.dataStructure.avltree;
import org.junit.Test;
import indi.tom.dataStructure.avltree.AVLTreeTest.AVLTree;
import indi.tom.dataStructure.avltree.AVLTreeTest.ValueNotFoundException;
/**
* @Author: Tom
* @Date: 2021年1月3日 下午8:43:56
* @Version: 1.0
* @Description: A demo of AVL tree, built on top of BST tree. The main
* difference between BST and AVL tree is that the AVL tree
* addNode() method, The addNode() method guarantee that after
* adding the new node, the tree is still a self-balancing BST
* tree which is a tree that the height difference of the left and
* right subtree doesn't exceeds 1. It utilize the leftRotate()
* and rightRotate() method to make it.
*/
public class AVLTreeTest {
public static void main(String[] args) throws ValueNotFoundException {
AVLTree avlTree = new AVLTree();
for (int i = 0; i < 10; i++) {
avlTree.add(i);
}
avlTree.inOrder();
avlTree.add(10);
avlTree.delete(4);
avlTree.inOrder();
}
@Test
public void test01() throws ValueNotFoundException {
AVLTree avlTree = new AVLTree();
for (int i = 0; i < 10; i++) {
avlTree.add(i);
}
System.out.println(avlTree);
}
static class AVLTree {
Node root;
public AVLTree() {}
public AVLTree(Node root) {
super();
this.root = root;
}
// In order traverse
public void inOrder() {
root.inOrder();
}
// BST search
public Node search(int value) {
if (root == null)
return null;
return root.search(value);
}
// BST add
public void add(int value) {
if (root == null) {
root = new Node(value);
}else {
root.add(value);
}
}
/**
*
* @Description: Delete the node with given value from the tree
* ****Difficult*****
* @param value The Node value to be deleted
* @return no return value
* @throws ValueNotFoundException when the given value is not found in the tree.
*/
public void delete(int value) throws ValueNotFoundException {
// 1. root is null or not found in the tree
if (root == null)
throw new ValueNotFoundException("This is a blank tree");
Node searchResultNode = search(value);
if (searchResultNode == null)
throw new ValueNotFoundException("The given value is not found");
Node parent = searchParent(root, value);
// 2. root node is the one that has the value
if (parent == null) {
if (root.getLeft() == null && root.getRight() == null) {
root = null;
} else if (root.getLeft() != null && root.getRight() != null) {
int minValue = getAndDeleteRightTreeMinValue(root.getRight());
root.setValue(minValue);
root.balance();
} else if (root.getLeft() != null && root.getRight() == null) {
this.root = root.getLeft();
} else if (root.getLeft() == null && root.getRight() != null) {
this.root = root.getRight();
}
// 3. the value exists being held by an non-root node
} else {
// 3.1 searchResultNode is a leaf node, remove it directly
if (searchResultNode.getLeft() == null && searchResultNode.getRight() == null) {
if (parent.getLeft() == searchResultNode) {
parent.setLeft(null);
} else if(parent.getRight() == searchResultNode){
parent.setRight(null);
}
this.root.balance();
// 3.2 both the left and right child of searchResultNode is not null
} else if (searchResultNode.getLeft() != null && searchResultNode.getRight() != null) {
int minValue = getAndDeleteRightTreeMinValue(searchResultNode.getRight());
searchResultNode.setValue(minValue);
this.root.balance();
// 3.3 left child is not null
} else if (searchResultNode.getLeft() != null && searchResultNode.getRight() == null) {
parent.setLeft(searchResultNode.getLeft());
this.root.balance();
// 3.4 right child is not null
} else {
parent.setRight(searchResultNode.getRight());
this.root.balance();
}
}
}
// get the min value in the right subtree and remove the corresponding node
private int getAndDeleteRightTreeMinValue(Node node) throws ValueNotFoundException {
while (node.getLeft() != null) {
node = node.getLeft();
}
int minimal = node.getValue();
// delete the minimal node
delete(minimal);
return minimal;
}
private Node searchParent(Node node, int value) {
if (node.getValue() == value)
return null;
Node target = null;
if (node.getLeft() != null) {
if (node.getLeft().getValue() == value) {
return node;
} else {
target = searchParent(node.getLeft(), value);
if (target != null)
return target;
}
}
if (node.getRight() != null) {
if (node.getRight().getValue() == value) {
return node;
} else {
target = searchParent(node.getRight(), value);
if (target != null)
return target;
}
}
return null;
}
@Override
public String toString() {
return this.root.toString();
}
}
static class ValueNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
public ValueNotFoundException() {
super();
}
public ValueNotFoundException(String message) {
super(message);
}
public ValueNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
}
package indi.tom.dataStructure.avltree;
/**
* @Author: Tom
* @Date: 2021年1月4日 下午9:20:45
* @Version: 1.0
* @Description:
*/
class Node {
private int value;
private Node left, right;
public Node(int value) {
super();
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
// InOrder traverse
public void inOrder() {
if (this.left != null) {
this.left.inOrder();
}
System.out.println(value);
if (this.right != null) {
this.right.inOrder();
}
}
// search
public Node search(int value) {
Node target = null;
if (this.value == value)
return this;
if (this.left != null) {
target = this.left.search(value);
if (target != null)
return target;
}
if (this.right != null) {
target = this.right.search(value);
if (target != null)
return target;
}
return null;
}
// add
public Node add(int value) {
Node target = new Node(value);
// 1. add new node to the right
if (this.value <= value) {
// 1.1 if right == null, add to right directly
if (this.right == null) {
this.right = target;
// 1.2 if right != null, call right.add() recursively
} else {
this.right.add(value);
// 1.2.1 compare the left and right subtree, if the difference is greater than
// 1, adjust it
// a. left rotate the entire tree (this is possible as right subtree add one)
if (right.height() - (left == null ? 0 : left.height()) > 1) {
// before left rotate the entire tree, check if the right.left > right.right, if
// so, right rotate the right subtree
if ((right.left == null ? 0 : right.left.height()) > (right.right == null ? 0
: right.right.height())) {
right.rightRotate();
// then check if the entire tree, right.height - left.heitht > 1, if so, left
// rotate the entire tree
if ((right.height() - (left == null ? 0 : left.height())) > 1) {
this.leftRotate();
}
} else if ((right.right == null ? 0 : right.right.height()) > (right.left == null ? 0
: right.left.height())) {
this.leftRotate();
// The last case is right.right.height()=right.left.height(), but it's not
// possible because before add to the right,
// it must be that right.height > left.height, then it's possible right.height()
// - left.height() > 1.
// So before add, it must be right.height - left.right = 1, then add 1 node to
// right, if right.left.height = right.right.height,
// then the right subtree height must has no change
} /*
* else if(right.right.height() == right.left.height()) {this is not possible
* under the condition that (right.height() - left.height()) > 1}
*/
} /* else if(left.height() - right.height() > 1) {this is no possible} */
}
// 2. add new node to the left
} else {
// 2.1 if left == null, add to left directly
if (this.left == null) {
this.left = target;
// 2.2 if left != null, call left.add() recursively
} else {
this.left.add(value);
if ((left.height() - (right == null ? 0 : right.height())) > 1) {
// if(left.right.height()>left.left.height()), left rotate the left subtree,
// then check if left.height - right.height still > 1, if so,
// right rotate the entire tree
if ((left.right == null ? 0 : left.right.height()) > (left.left == null ? 0 : left.left.height())) {
left.leftRotate();
if ((left.height() - right.height()) > 1) {
this.rightRotate();
}
} else if ((left.right == null ? 0 : left.right.height()) < (left.left == null ? 0
: left.left.height())) {
this.rightRotate();
} /*
* else if(eft.right.height()<left.left.height()) {this is not possible under
* the condition that (left.height() - right.height()) > 1}
*/
} /* else if((right.height()-left.height()) > 1) {this is not gonna happen} */
}
}
return target;
}
// adjust the entire tree to make sure it's a balancing search tree
public void balance() {
if (this.left == null && this.right == null) {
return;
} else if (this.left == null && this.right != null) {
if (right.height() > 1) {
// left rotate
this.leftRotate();
}
} else if (this.left != null && this.right == null) {
if (left.height() > 1) {
this.rightRotate();
}
} else if (this.left != null && this.right != null) {
if ((left.height() - right.height()) > 1) {
// if(left.right.height()>left.left.height()), left rotate the left subtree,
// then check if left.height - right.height still > 1, if so,
// right rotate the entire tree
if (left.right.height() > left.left.height()) {
left.leftRotate();
if ((left.height() - right.height()) > 1) {
this.rightRotate();
}
} else if (left.right.height() < left.left.height()) {
this.rightRotate();
}
} else if ((right.height() - left.height()) > 1) {
// before left rotate the entire tree, check if the right.left > right.right, if
// so, right rotate the right subtree
if (right.left.height() > right.right.height()) {
right.rightRotate();
// then check if the entire tree, right.height - left.heitht > 1, if so, left
// rotate the entire tree
if ((right.height() - left.height()) > 1) {
this.leftRotate();
}
} else if (right.right.height() >= right.left.height()) {
this.leftRotate();
}
}
}
}
// right rotate
public void rightRotate() {
// original = root = this
// create a new Node for the original root node
Node newNode = new Node(value);
// new node.right = root.right
newNode.right = this.right;
// new node.eft = root.left.right
newNode.left = this.left.right;
// root.value = left.value;
this.value = left.value;
// root.right = new node
this.right = newNode;
// root.left = left.left
this.left = left.left;
}
// left rotate
public void leftRotate() {
// root = this
// clone the current root node
Node newNode = new Node(this.value);
// newNode.left = root.left
newNode.left = this.left;
// newNode.right = root.right.left
newNode.right = this.right.left;
// root.value = right.value
this.value = right.value;
// root.right = right.right
this.right = right.right;
// root.left = newNode
this.left = newNode;
}
// calculate height
public int height() {
if (left != null && right == null) {
return left.height() + 1;
} else if (left == null && right != null) {
return right.height() + 1;
} else if (left != null && right != null) {
return Math.max(left.height(), right.height()) + 1;
} else {
return 1;
}
}
@Override
public String toString() {
return "Node [value=" + value + ", left=" + left + ", right=" + right + "]";
}
}