二叉树,二叉查找树,平衡二叉树,AVL树的一种实现
网上关于二叉树的介绍有很多,这里不做多介绍,今天给大家分享一个自己实现的二叉查找树和AVL树。其实AVL树只是
比二叉查找树多一步平衡,后面会单独介绍平衡方法。
1、首先构建一个AvlNode。
public class AvlNode {
//节点
public int element;
//左子树
public AvlNode left;
//右子树
public AvlNode right;
//树高
public int height;
public AvlNode(int theElement) {
this(theElement, null, null);
}
public AvlNode(int theElement, AvlNode lt, AvlNode rt) {
this.element = theElement;
this.left = lt;
this.right = rt;
this.height = 0;
}
}
2、insert方法
public AvlNode insert(int element, AvlNode avlNode, boolean isBalance) {
if (avlNode == null) {
return new AvlNode(element, null, null);
}
int compareResult = element - avlNode.element;
if (compareResult < 0) {
avlNode.left = insert(element, avlNode.left, isBalance);
} else if (compareResult > 0) {
avlNode.right = insert(element, avlNode.right, isBalance);
} else { //相等,do nothing
;
}
return isBalance ? balance(avlNode) : avlNode;
}
1、element是要插入的元素,avlNode是要往这个节点下插入元素。
2、isBalance=true,则构建AVL树,isBalance=false则构建二叉查找树。
3、当AvlNode == null,则构建新节点返回。否则,若元素小于当前节点,则往左节点插入;若大于当前节点,则往右节点插入;若相等,则不进行任何处理。
4、到这一步,二叉查找树构建完毕,若是需要构建AVL树,则需要进行平衡。
3、平衡方法(balance)
private AvlNode balance(AvlNode avlNode) {
if (avlNode == null) {
return avlNode;
}
if (getHeight(avlNode.left) - getHeight(avlNode.right) > BALANCE_FACTOR) {
if (getHeight(avlNode.left.left) >= getHeight(avlNode.left.right)) {
avlNode = leftLeftRotate(avlNode);
} else {
avlNode = leftRightRotate(avlNode);
}
} else if (getHeight(avlNode.right) - getHeight(avlNode.left) > 1) {
if (getHeight(avlNode.right.right) >= getHeight(avlNode.right.left)) {
avlNode = rightRightRotate(avlNode);
} else {
avlNode = rightLeftRotate(avlNode);
}
}
avlNode.height = Math.max(getHeight(avlNode.left), getHeight(avlNode.right)) + 1;
return avlNode;
}
1、BALANCE_FACTOR:平衡因子为1。
2、假设当前节点为root,左子树为L,右子树为R。
若左子树(L)的高减去右子树(R)的高大于平衡因子,则比较左子树的左右子树。
(1)若L.left的高大于L.right的高,则满足LL情况,需要进行右单旋。
(2)若L.left的高小于L.right的高,则满足LR情况,需要两次旋转(先左单旋,再右单旋)。
若右子树(R)的高减去左子树(L)的高大于平衡因子,则比较右子树的左右子树。
(1)若R.right的高大于R.left的高,则满足RR情况,需要进行左单旋。
(2)若R.right的高小于R.left的高,则满足RL情况,需要两次旋转(先右单旋,再左单旋)。
重新计算当前节点的高。
4、 四种旋转(LL、RR、LR、RL)
/**
* 左左情况,右单旋
* @param avlNode
* @return
*/
private AvlNode leftLeftRotate(AvlNode avlNode) {
AvlNode left = avlNode.left;
avlNode.left = left.right;
left.right = avlNode;
avlNode.height = Math.max(getHeight(avlNode.left), getHeight(avlNode.right)) + 1;
left.height = Math.max(getHeight(left.left), getHeight(left.right)) + 1;
return left;
}
/**
* 右右旋转,左单旋
* @param avlNode
* @return
*/
private AvlNode rightRightRotate(AvlNode avlNode) {
AvlNode right = avlNode.right;
avlNode.right = right.left;
right.left = avlNode;
avlNode.height = Math.max(getHeight(avlNode.left), getHeight(avlNode.right)) + 1;
right.height = Math.max(getHeight(right.left), getHeight(right.right)) + 1;
return right;
}
/**
* 左右情况,先用左子树进行左单旋,自己再进行右单旋
* @param avlNode
* @return
*/
private AvlNode leftRightRotate(AvlNode avlNode) {
avlNode.left = rightRightRotate(avlNode.left);
return leftLeftRotate(avlNode);
}
/**
* 右左情况,先用右子树进行右单旋,自己再进行左单旋
* @param avlNode
* @return
*/
private AvlNode rightLeftRotate(AvlNode avlNode) {
avlNode.right = leftLeftRotate(avlNode.right);
return rightRightRotate(avlNode);
}
5、二叉树的删除
/**
*
* @param element
* @param avlNode
* @return
*/
public AvlNode remove(int element, AvlNode avlNode) {
if (avlNode == null) {
return avlNode;
}
int compareResult = element - avlNode.element;
if (compareResult < 0) {
avlNode.left = remove(element, avlNode.left);
} else if (compareResult > 0) {
avlNode.right = remove(element, avlNode.right);
} else if (avlNode.left != null && avlNode.right != null) {
avlNode.element = findMin(avlNode.right).element;
avlNode.right = remove(avlNode.element, avlNode.right);
} else {
avlNode = avlNode.left != null ? avlNode.left : avlNode.right;
}
return balance(avlNode);
}
/**
* 找到最小节点
* @param avlNode
* @return
*/
private AvlNode findMin(AvlNode avlNode) {
if (avlNode == null) {
return null;
} else if (avlNode.left == null) {
return avlNode;
}
return findMin(avlNode.left);
}
1、将要删除的元素和当前节点元素进行对比,若小于当前节点,则去左子树递归删除,若大于当前节点则去右子树递归删除。
2、若是等于当前节点。
(1)当前节点的左右子树都不为空,则需要找到右子树最小的节点,将最小节点的元素放到当前节点。然后递归删除最小节点。
(2)若当前节点左右子树有一个为空,左子树不为空,则返回左子树,否则返回右子树。
6、二叉树的打印
/**
* 直接调用打印方法,用长度为100的数组来存放二叉树
* @param root
*/
public void printAvlTree(AvlNode root) {
printAvlTreeWithLength(root, 100);
}
/**
* 指定数组长度来存放二叉树
* @param root
* @param length
*/
public void printAvlTreeWithLength(AvlNode root, int length) {
List<AvlNode> list1 = new LinkedList<>();
List<AvlNode> list2 = new LinkedList<>();
list1.add(root);
int[] avls = generateAvls(new int[length], root, 0, length);
Map<String, String> flagMap = new HashMap<>();
flagMap.put("flag", "N");
printAvlByRecurion(avls, list1, list2, flagMap);
}
/**
* 递归打印方法
* @param avls
* @param list1
* @param list2
* @param flagMap
*/
private void printAvlByRecurion(int[] avls, List<AvlNode> list1, List<AvlNode> list2, Map<String, String> flagMap) {
if (list1.isEmpty() && list2.isEmpty()) {
return;
}
if (!list1.isEmpty()) {
String[] printAvls = new String[avls.length];
while (!list1.isEmpty()) {
AvlNode avlNode = list1.remove(0);
//将父节点放入到打印数组中
int parentIndex = getIndex(avlNode, avls);
printAvls[parentIndex] = String.valueOf(avlNode.element);
if (avlNode.left != null) {
list2.add(avlNode.left);
int leftIndex = getIndex(avlNode.left, avls);
generatePrivateAvls(leftIndex, parentIndex - 1, printAvls);
}
if (avlNode.right != null) {
list2.add(avlNode.right);
int rightIndex = getIndex(avlNode.right, avls);
generatePrivateAvls(parentIndex + 1, rightIndex, printAvls);
}
}
printAvlsByString(printAvls, flagMap);
printAvlByRecurion(avls, list1, list2, flagMap);
}
if (!list2.isEmpty()) {
String[] printAvls = new String[avls.length];
while (!list2.isEmpty()) {
AvlNode avlNode = list2.remove(0);
//将父节点放入到打印数组中
int parentIndex = getIndex(avlNode, avls);
printAvls[parentIndex] = String.valueOf(avlNode.element);
if (avlNode.left != null) {
list1.add(avlNode.left);
int leftIndex = getIndex(avlNode.left, avls);
generatePrivateAvls(leftIndex, parentIndex - 1, printAvls);
}
if (avlNode.right != null) {
list1.add(avlNode.right);
int rightIndex = getIndex(avlNode.right, avls);
generatePrivateAvls(parentIndex + 1, rightIndex, printAvls);
}
}
printAvlsByString(printAvls, flagMap);
printAvlByRecurion(avls, list1, list2, flagMap);
}
}
/**
* 将构建好的数组打印
* @param avls
* @param flagMap
*/
private void printAvlsByString(String[] avls, Map<String, String> flagMap) {
for (int i = 0; i < avls.length; i++) {
if (avls[i] != null && !avls[i].equals("-")) {
if ("Y".equals(flagMap.get("flag"))) {
System.out.print("|");
}
} else {
System.out.print(" ");
}
}
flagMap.put("flag", "Y");
System.out.println();
for (int i = 0; i < avls.length; i++) {
if (avls[i] != null) {
System.out.print(avls[i]);
} else {
System.out.print(" ");
}
}
System.out.println();
}
/**
* 用于构建左右子树的连接
* @param startIndex
* @param endIndex
* @param avls
*/
private void generatePrivateAvls(int startIndex, int endIndex, String[] avls) {
for (int i = startIndex; i <= endIndex; i++) {
avls[i] = "-";
}
}
/**
* 获取当前元素在数组里的索引
* @param avlNode
* @param avls
* @return
*/
private int getIndex(AvlNode avlNode, int[] avls) {
for (int i = 0; i < avls.length; i++) {
if(avlNode.element == avls[i]) {
return i;
}
}
return -1;
}
主体思想:按层打印,先将第一层的节点放入到list1,然后取出打印,同时每取出一个节点然后将其左右节点放入到另一个list2。然后递归。
7、测试类以及效果
public class AvlTest {
public static void main(String[] args) {
int[] elements = {15, 8, 20, 4, 10, 25, 3, 5, 9, 11, 12};
AvlService service = new AvlService();
AvlNode root = null;
System.out.println("二叉查找树");
for (int element:elements) {
root = service.insert(element, root, false);
}
service.printAvlTree(root);
root = null;
System.out.println("平衡二叉树");
for (int element:elements) {
root = service.insert(element, root, true);
}
service.printAvlTree(root);
int e = 9;
System.out.println("删除节点:" + e);
root = service.remove(e, root);
service.printAvlTree(root);
}
}
项目源码:
github:https://github.com/licy-IT/Tree.git
码云:https://gitee.com/rising-dragon/Tree.git