二叉树,二叉查找树,AVL树的一种实现

二叉树,二叉查找树,平衡二叉树,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值