java-平衡二叉树AVL的实现

       平衡二叉树AVL也是一个二叉搜索树,所以二叉搜索树的一些方法对他也适用。他跟二叉搜索树的区别在于他的每次插入,删除操作,都要校验插入或删除的节点,是否造成了树的不平衡,如果不平衡了就要适当调整他。  

        AVL的定义和旋转的定义就不插了,网上一搜一大片,贴一个百科地址把http://baike.baidu.com/view/671745.htm

       图解简单说一下几种旋转:

       LL旋转(顺时针旋转,由于在T节点的左子树的左子树上插入节点,导致了T节点的平衡因子改变而发生的旋转,),本质上,就是因为T节点的左子树加深了一层而导致了T节点的不平衡,所以用T节点的左子树代替T节点,将T节点左子树的右子树赋值给T节点当左子树,T节点变成原来T节点的右子树,如下图(算法导论):



 

 单看图很难理解怎么编码,把LL旋转(顺时针旋转)转换成4步走,还是看图(自己画的,转载请说明,谢谢):



 I   首先肯定将值value插入相应的位置,与二叉排序树插入完全一样。

II  k1指向t的左孩子,然后断开k1和t

III  把k1的右孩子(大于k1,小于t)送给t当左孩子

IV   t当k1的右孩子

 对应I  II   III    IV,很容易写出对应的旋转代码:

      

/**
	 * rotateWithLeftChild: 带左子树旋转,适用于LL型(顺时针旋转)
	 * 
	 * @param k2
	 * @return AVLNode 返回类型
	 */
	private AVLNode<T> rotateWithLeftChild(AVLNode<T> t) {
		//定义临时节点k1,指向T的左孩子,旋转后替代T的位置
		AVLNode<T> k1 = t.leftChild;
		//将t(失衡节点)的左孩子指向k1(旋转后的t位置)的右孩子
		t.leftChild = k1.rightChild;
		//k1的右孩子位置指向t
		k1.rightChild = t;
		//以上,旋转完成,下边两行更新旋转的两个节点的高度值
		//为啥能这么写,从图里可以看到,其实t的左右孩子的高度,和k1左右孩子的高度,是没有变的
		t.height = Math.max(height(t.leftChild), height(t.rightChild)) + 1;
		k1.height = Math.max(height(k1.leftChild), t.height) + 1;
		return k1;
	}

     理解了LL旋转,RR旋转就很容易理解了。   至于LR和RL,其本质就是先L旋转成RR型,然后再RR旋转,代码都不用写,两次旋转即可。

     查询不比说了。。   就是排序树的查询。

      删除,类似排序树,也有一个简单的方法,就是给每个节点赋一个boolean值,删除时把该值置为false。

      一般性删除即:

       1)  找出该节点的位置T,

       2) 找到T节点的子树中,较大的子树(这样做的目的是,用交大子树的节点替换该节点删除后,该节点为根节点组成的树,本身肯定是一颗平衡二叉树,不用对该节点再做平衡判断了)

       3)找出2)中要替换的节点(如果T的左子树较高,那取T左子树的最大节点),把该节点的值与T替换,删除该节点;

      4) 递归向上判断删除T节点后,树是否产生了不平衡状态,做相应调整(如果平衡因子变成2,说明因为删除右子树导致不平衡(相当于插入左子树),进行LL旋转,反之亦然)。

      java代码:

/**  
	* remove: 删除avl树中的节点
	* @param node    要删除的节点所在的根节点
	* @param value   要删除的value
	* @return 
	* AVLNode  返回类型   
	*/
	private AVLNode<T> remove(AVLNode<T> node, T value) {
		int cmp = value.compareTo(node.value);
		if (cmp < 0) { // 待删除的节点在"node的左子树"中
			node.leftChild = remove(node.leftChild, value);
			// 删除节点后,若AVL树失去平衡,则进行相应的调节。
			if (height(node.rightChild) - height(node.leftChild) == 2) {
				AVLNode<T> delNode = node.rightChild;
				if (height(delNode.leftChild) > height(delNode.rightChild))
					node = rotateWithLeftChild(node);
				else
					node = rotateWithRightChild(node);
			}
		} else if (cmp > 0) { // 待删除的节点在"tree的右子树"中
			node.rightChild = remove(node.rightChild, value);
			// 删除节点后,若AVL树失去平衡,则进行相应的调节。
			if (height(node.leftChild) - height(node.leftChild) == 2) {
				AVLNode<T> delNode = node.leftChild;
				if (height(delNode.rightChild) > height(delNode.leftChild))
					node = rotateWithLeftChild(node);
				else
					node = rotateWithRightChild(node);
			}
		} else { // 删除节点代码在这里,上面都是删除后的调整代码。
			// 要删除的节点node的左右孩子都非空
			if ((node.leftChild != null) && (node.rightChild != null)) {
				if (height(node.leftChild) > height(node.rightChild)) {
					// 如果node的左子树比右子树高;
					// 则首先找出node的左子树中的最大节点
					// 第二步将该最大节点的值赋值给node。
					// 最后删除该最大节点。
					// 这类似于用"node的左子树中最大节点"做"node"的替身;
					// 采用这种方式的好处是:删除"node的左子树中最大节点"之后,AVL树仍然是平衡的,这样可以免去很多不必要的麻烦。
					T max = this.getMax(node.leftChild);
					node.value = max;
					node.leftChild = remove(node.leftChild, max);
				} else {
					//与上面操作类似
					T min = this.getMin(node.rightChild);
					node.value = min;
					node.leftChild = remove(node.rightChild, min);
				}
			} else {
				node = (node.leftChild != null) ? node.leftChild : node.rightChild;
			}
		}
		return node;
	}

  

     测试代码:

/**
	 * main: 主函数
	 * 
	 * @param args
	 *            void 返回类型
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		AVLTree<Integer> tree = new AVLTree<Integer>(2);
		tree.insert(1);
		tree.insert(2);
		tree.insert(3);
		tree.insert(4);
		tree.insert(5);
		tree.insert(6);
		TreeTools.levelTravel(tree.root);
		System.out.println("\n"+tree.searchNode(4));
		System.out.println(tree.isAVL());
		tree.remove(3);
		TreeTools.levelTravel(tree.root);
		System.out.println("\n"+tree.searchNode(3));
		System.out.println(tree.isAVL());
		TreeTools.midOrderTravel(tree.root);
	}

 测试结果:

4	2	5	1	3	6	
true
true
4	2	5	1	6	
false
true
1	2	4	5	6	

 附上全部代码

猜你喜欢

转载自709002341.iteye.com/blog/2258678
今日推荐