平衡二叉排序樹(AVL)構造過程及Java代碼實現

1、定義

平衡二叉排序樹要求任何節點的左右子樹高度差絕對值不超過1(左右子樹高度可相同)。

2、普通二叉排序樹不足

以依次插入1,2,3,4,5,6數爲例,結果如下圖所示:

當查找元素6時,時間複雜度爲O(n),並且這樣構造相當於有一邊的孩子節點失去了意義,這樣就違背了二叉樹的初衷。

3、平衡二叉排序樹構造

       爲了在不斷向二叉排序樹插入或者刪除元素的過程中保持平衡性(即滿足平衡二叉排序樹定義要求),需要進行可能的四種操作:左單旋、右單旋、先右旋再左旋、先左旋再右旋。

(1)左單旋

插入:當前節點(值爲1的節點)的右子樹高度減去左子樹高度大於1時,並且插入節點(值爲3的節點)大於當前節點的右子節點值時需左單旋

刪除:當前節點(值爲1的節點)的右子樹高度減去左子樹高度大於1時,並且當前節點的右子節點的左子樹高度小於右子樹高度時需左單旋

(2)右單旋

插入:當前節點(值爲3的節點)的左子樹高度減去右子樹高度大於1時,並且插入節點(值爲1的節點)小於當前節點的左子節點值時需右單旋

刪除:當前節點(值爲3的節點)的左子樹高度減去右子樹高度大於1時,並且當前節點的左子節點的左子樹高度大於右子樹高度時需右單旋

(3)先右旋再左旋

插入:當前節點(值爲1的節點)的右子樹高度減去左子樹高度大於1時,並且插入節點(值爲1.5的節點)小於當前節點的右子節點值時需先右旋再左旋

刪除:當前節點(值爲1的節點)的右子樹高度減去左子樹高度大於1時,並且當前節點的右子節點的左子樹高度大於等於右子樹高度時需先右旋再左旋

(4)先左旋再右旋

插入:當前節點(值爲3的節點)的左子樹高度減去右子樹高度大於1時,並且插入節點(值爲1的節點)大於當前節點的左子節點值時需先左旋再右旋

刪除:當前節點(值爲3的節點)的左子樹高度減去右子樹高度大於1時,並且當前節點的左子節點的左子樹高度小於等於右子樹高度時需先左旋再右旋

4、以給定的數組[3,2,1,4,5,6,7,10,9,8]進行平衡二叉排序樹插入構造示例

插入元素3:

插入元素2:

插入元素1:

插入元素4:

插入元素5:

插入元素6:

插入元素7:

插入元素10:

插入元素9:

插入元素8:

5、已給定刪除順序7,9,10,5,4,6,3,2,8爲例進行二叉排序樹重新平衡示例

刪除元素7:

刪除元素9:

刪除元素10:

刪除元素5:

刪除元素4:

刪除元素6:

刪除元素3:

6、Java代碼實現

package com.jingchenyong.boundless.algo;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * 平衡二叉排序樹代碼實現
 * 
 * @author henry
 *
 */
public class AvlTree {

	Queue<Tree> queue = new LinkedBlockingDeque<Tree>();// 按層遍歷時所需隊列

	private Tree root; // 二叉平衡排序樹頭節點

	public void insert(int data) {
		root = insert(root, data);
	}

	public void remove(int data) {
		root = remove(root, data);
	}

	/**
	 * 插入操作
	 * 
	 * @param root
	 * @param data
	 * @return
	 */
	public Tree insert(Tree root, int data) {
		if (root == null) {// 如果節點爲null,那麼就在此處插入那個新的節點,用來終止遞歸
			root = new Tree();
			root.data = data;
			return root;
		}
		if (data <= root.data) {
			root.leftChild = insert(root.leftChild, data); // 如果要插的值小於或等於當前的節點值,插入進左子樹
			if (getHeight(root.leftChild) - getHeight(root.rightChild) > 1) {// 當前節點左子樹高度減去右子樹高度大於1時
				// 由於是向當前root樹的左邊插入,所以左子樹高度必定不小於右子樹高度
				if (data <= root.leftChild.data) {
					// 最後插入的葉子節點在該root樹的左孩子的左邊
					root = rightRotate(root); // 右旋,然後將調整後的root返回給其父節點的左子樹域
				} else {
					// 最後插入的葉子節點在該root樹的左孩子的右邊
					root = leftAndRight(root); // 先左旋再右旋,然後將調整後的root返回給其父節點的左子樹域
				}
			}
		} else {
			root.rightChild = insert(root.rightChild, data);// 如果要插的值大於當前的節點值,插入進右子樹
			if (getHeight(root.rightChild) - getHeight(root.leftChild) > 1) {
				if (data <= root.rightChild.data) {
					root = rightAndLeft(root); // 先右旋再左旋,然後將調整後的root返回給其父節點的右子樹域
				} else {
					root = leftRotate(root); // 左旋,然後將調整後的root返回給其父節點的右子樹域
				}
			}
		}
		// 重新調整root節點的高度值(當插入新節點後,從這個節點到根節點的最短路徑上的節點的高度值一定會發生改變,另外當出現失衡點時,這個失衡點的所有祖先節點的高度值也可能會發生改變)
		root.height = Math.max(getHeight(root.leftChild), getHeight(root.rightChild)) + 1;
		return root;
	}

	/**
	 * 刪除操作
	 * 
	 * @param root
	 * @param data
	 * @return
	 */
	public Tree remove(Tree root, int data) {
		if (root == null) {
			// 沒有找到刪除的節點
			return null;
		}
		if (data < root.data) {
			// 在左子樹上刪除
			root.leftChild = remove(root.leftChild, data);
			if (getHeight(root.rightChild) - getHeight(root.leftChild) > 1) {
				if (getHeight(root.rightChild.leftChild) > getHeight(root.rightChild.rightChild)) {
					root = rightAndLeft(root);
				} else {
					root = leftRotate(root);
				}
			}
		} else if (data == root.data) {
			// 找到刪除的節點
			if (root.leftChild != null && root.rightChild != null) {
				// 刪除節點既有左子樹也有右子樹
				root.data = findNextNode(root).data; // 將右子樹中最小值節點的值賦值給當前節點(表明當前節點已刪除)
				root.rightChild = remove(root.rightChild, root.data);// 將問題轉爲刪除當前節點的右子樹中最小的節點
				// 如果是根節點刪除也需要進行二叉平衡判斷
				if (getHeight(root.leftChild) - getHeight(root.rightChild) > 1) {
					if (getHeight(root.leftChild.leftChild) > getHeight(root.leftChild.rightChild)) {
						root = rightRotate(root);
					} else {
						root = leftAndRight(root);
					}
				} else if (getHeight(root.rightChild) - getHeight(root.leftChild) > 1) {
					if (getHeight(root.rightChild.leftChild) > getHeight(root.rightChild.rightChild)) {
						root = rightAndLeft(root);
					} else {
						root = leftRotate(root);
					}
				}
			} else {
				// 刪除節點只有左子樹或者只有右子樹或者爲葉子節點的情況
				root = (root.leftChild == null) ? root.rightChild : root.leftChild;
			}
		} else {
			// 在右子樹上刪除
			root.rightChild = remove(root.rightChild, data);
			if (getHeight(root.leftChild) - getHeight(root.rightChild) > 1) {
				if (getHeight(root.leftChild.leftChild) > getHeight(root.leftChild.rightChild)) {
					root = rightRotate(root);
				} else {
					root = leftAndRight(root);
				}
			}
		}
		if (root != null) {
			root.height = Math.max(getHeight(root.leftChild), getHeight(root.rightChild)) + 1;
		}
		return root;
	}

	/**
	 * 右單旋操作
	 * 
	 * @param root
	 * @return
	 */
	public Tree rightRotate(Tree root) {
		Tree temp = root.leftChild;
		root.leftChild = temp.rightChild;
		temp.rightChild = root;

		// 旋轉後節點高度值再調整
		root.height = Math.max(getHeight(root.leftChild), getHeight(root.rightChild)) + 1;
		temp.height = Math.max(getHeight(root.leftChild), root.height) + 1;
		return temp;
	}

	/**
	 * 左單旋操作
	 * 
	 * @param root
	 * @return
	 */
	public Tree leftRotate(Tree root) {
		Tree temp = root.rightChild;
		root.rightChild = temp.leftChild;
		temp.leftChild = root;
		// 旋轉後節點高度值再調整
		root.height = Math.max(getHeight(root.leftChild), getHeight(root.leftChild)) + 1;
		temp.height = Math.max(getHeight(temp.leftChild), getHeight(temp.leftChild)) + 1;
		return temp;
	}

	/**
	 * 先左旋再右旋操作
	 * 
	 * @param root
	 * @return
	 */
	public Tree leftAndRight(Tree root) {
		root.leftChild = leftRotate(root.leftChild);
		return rightRotate(root);
	}

	/**
	 * 先右旋再左旋操作
	 * 
	 * @param root
	 * @return
	 */
	public Tree rightAndLeft(Tree root) {
		root.rightChild = rightRotate(root.rightChild);
		return leftRotate(root);
	}

	/**
	 * 找到右子樹下最小節點
	 * 
	 * @param root
	 * @return
	 */
	public Tree findNextNode(Tree root) {
		if (root == null) {
			return null;
		}
		Tree r = root.rightChild;
		while (r != null && r.leftChild != null) {
			r = r.leftChild;
		}
		return r;
	}

	/**
	 * 獲取當前節點的高度
	 * 
	 * @param node
	 * @return
	 */
	public int getHeight(Tree node) {
		return node == null ? -1 : node.height;
	}

	/**
	 * 獲取樹節點的高度(方法二)
	 * 
	 * @param node
	 * @return
	 */
	/*
	public int getHeight(Tree node) {
		if (node == null) {
			return -1;
		}
		if (node.leftChild == null && node.rightChild == null) {
			return 0;
		} else if (node.leftChild == null && node.rightChild != null) {
			return getHeight(node.rightChild) + 1;
		} else if (node.leftChild != null && node.rightChild == null) {
			return getHeight(node.leftChild) + 1;
		} else {
			if (getHeight(node.leftChild) < getHeight(node.rightChild)) {
				return getHeight(node.rightChild) + 1;
			} else {
				return getHeight(node.leftChild) + 1;
			}
		}
	}*/

	/**
	 * 中序遍歷
	 * 
	 * @param node
	 */
	public void middle(Tree node) {
		if (node == null) {
			return;
		}
		middle(node.leftChild);
		System.out.println(node.data);
		middle(node.rightChild);
	}

	/**
	 * 按層遍歷
	 * 
	 * @param queue
	 */
	public void floorShow(Queue<Tree> queue) {
		Tree currentNode;
		while ((currentNode = queue.poll()) != null) {
			System.out.println(currentNode.data);
			if (currentNode.leftChild != null) {
				queue.offer(currentNode.leftChild);
			}
			if (currentNode.rightChild != null) {
				queue.offer(currentNode.rightChild);
			}
			floorShow(queue);
		}
	}

	/**
	 * 平衡二叉排序樹節點
	 * 
	 * @author Administrator
	 *
	 */
	private static class Tree {
		public Tree leftChild;
		public Tree rightChild;
		public int data;// 記錄節點的數據
		public int height;// 記錄節點的高度
	}

	public static void main(String[] args) {
		AvlTree avlTree = new AvlTree();

		avlTree.insert(3);
		avlTree.insert(2);
		avlTree.insert(1);
		avlTree.insert(4);
		avlTree.insert(5);
		avlTree.insert(6);
		avlTree.insert(7);
		avlTree.insert(10);
		avlTree.insert(9);
		avlTree.insert(8);
		System.out.println("中序遍歷:");
		avlTree.middle(avlTree.root);
		System.out.println("按層遍歷:");
		avlTree.queue.offer(avlTree.root);
		avlTree.floorShow(avlTree.queue);
		System.out.println();

		avlTree.remove(7);
		avlTree.remove(9);
		avlTree.remove(10);
		avlTree.remove(5);
		avlTree.remove(4);
		avlTree.remove(6);
		System.out.println("中序遍歷:");
		avlTree.middle(avlTree.root);
		System.out.println("按層遍歷:");
		avlTree.queue.offer(avlTree.root);
		avlTree.floorShow(avlTree.queue);

	}
}

代碼運行結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章