自己动手实现java数据结构(七) AVL树(2)
AVL树的实现继承自前面介绍的普通二叉搜索树—TreeMap类。由于AVL树通过树高作为判断平衡的依据,因此在二叉搜索树TreeMap的节点中增加了一个height(高度)属性。 /** * 二叉搜索树 内部节点 * */ static class EntryNode<K,V> implements Map.EntryNode<K,V>{ * key值 * */ K key; * value值 * V value; * 高度值 * */ int height; * 左孩子节点 * EntryNode<K,1)"> left; * 右孩子节点 * right; * 双亲节点 * parent; EntryNode(K key,V value) { this.key = key; this.value = value; this.height = 1; } EntryNode(K key,V value,EntryNode<K,1)"> parent) { this.parent = parent; ; } @Override public K getKey() { return key; } @Override V getValue() { value; } @Override public void setValue(V value) { String toString() { return key + "=" + value + " height=" + height; } } 3.2 辅助方法在AVL树的实现过程中,存在一些通用的,细节的逻辑,将其抽取成辅助函数,简化主要代码逻辑,增强代码可读性和可维护性。 * 当前节点 是否满足AVL树约定的平衡条件 * private boolean isAVLBalanced(EntryNode<K,1)"> entryNode){ //获得 左子树高度 int leftChildHeight = getHeight(entryNode.left); 获得右子树高度 int rightChildHeight = getHeight(entryNode.right); 获得左右子树高度差 int difference = leftChildHeight - rightChildHeight; 高度差绝对值在1之内,认为是满足AVL平衡条件 return -1 <= difference && difference <= 1; } * 更新当前节点高度 * void updateHeight(EntryNode<K,1)">int leftHeight =int rightHeight =左右子树高度较高者 + 1 entryNode.height = 1 + Math.max(leftHeight,rightHeight); } * 获得当前节点的高度 * int getHeight(EntryNode<K,1)">if(entryNode == null){ return 0; }else{ entryNode.height; } } * 获得较高子树分支孩子节点 private EntryNode<K,V> getTallerChild(EntryNode<K,1)">if(leftChildHeight > rightChildHeight){ 左子树高度 > 右子树高度 entryNode.left; }左子树高度 <= 右子树高度 entryNode.right; } } * 是否是左孩子 * boolean isLeftChild(EntryNode<K,V> parent,EntryNode<K,1)"> target){ return getRelativeByParent(parent,target) == RelativePosition.LEFT; } 3.3 3+4重构实现refactor34方法:方法的参数为3+4重构目标拓扑结构所需的三个节点(左,中,右),左右孩子的分别挂载的四颗子树。在refactor34方法中,依照3+4重构的原理直接调整节点和子树的关系引用,拼接成最终的所需的结果。 rotateAt方法:方法的参数为重平衡所涉及到的祖孙三代节点(Node、Son、GrandSon),通过判断N、S、GS的拓扑结构,决定调用refactor34方法时传递的参数。方法的返回值为3+4重构后的子树树根节点,便于重平衡操作之后,将重构后新的子树重新接入整颗AVL树中。 * 3+4 重构 * refactor34( EntryNode<K,V> leftNode,V> middleNode,1)"> rightNode,V> llChild,1)"> lrChild,V> rlChild,1)"> rrChild){ 调整 左节点和对应子树的拓扑结构 leftNode.left = llChild; if(llChild != ){ llChild.parent = leftNode; } leftNode.right = lrChild; if(lrChild != ){ lrChild.parent = leftNode; } 更新高度 updateHeight(leftNode); 调整 右节点和对应子树的拓扑结构 rightNode.left = rlChild; if(rlChild != ){ rlChild.parent = rightNode; } rightNode.right = rrChild; if(rrChild != ){ rrChild.parent = rightNode; } updateHeight(rightNode); 调整 中间节点 和左、右节点的拓扑结构 middleNode.left = leftNode; middleNode.right = rightNode; leftNode.parent = middleNode; rightNode.parent = middleNode; updateHeight(middleNode); } * 进行旋转,使用3+4重构完成重平衡 * @return 重构之后子树的树根节点 * grandSonNode){ if(isLeftChild(currentNode,sonNode)){ 左 zig (isLeftChild(sonNode,grandSonNode)){ 左-左 zig-zig旋转 refactor34(grandSonNode,sonNode,currentNode,grandSonNode.left,grandSonNode.right,sonNode.right,currentNode.right); sonNode; }{ 左-右 zig-zag旋转 refactor34(sonNode,grandSonNode,sonNode.left,1)"> grandSonNode; } }右 zag 右-左 zag-zig旋转 refactor34(currentNode,currentNode.left,sonNode.right); grandSonNode; }右-右 zag-zag旋转 sonNode; } } } 3.4 插入方法重写AVL树的实现中,重写了普通二叉搜索树的插入方法(put),整体逻辑和之前TreeMap的实现大致一样,唯一的区别在于,当插入了新的节点之后,会调用afterNewNodeInsert方法,进行AVL树重平衡的一系列操作。 afterNewNodeInsert方法: 参数为新插入的节点。从下至上,遍历检查新插入节点的历代祖先,判断其是否失衡。一旦发现当前迭代的祖先节点失衡,则调用rotateAt方法,使其恢复平衡,全树重新接入子树; (编辑:ASP站长网) |