树形结构----AVL平衡树实现映射

本文介绍了AVL平衡树的基本概念,包括由Adelson-Velsky和Landis创立的历史、定义——保证任意节点的左右子树高度差不超过1。详细阐述了平衡因子、插入删除节点后的平衡调整方法,如左旋、右旋及复合旋转,并讨论了使用AVL树实现映射接口的数据结构,最后展示了代码测试和运行结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. AVL平衡树的介绍:AVL平衡树是由G.M.Adelson-Velsky和E.M.Landis两人创建,因此命名为AVL。AVL平衡树是一种最早的自平衡二分搜索树结构满二叉树一定是平衡二叉树,高度最低;完全二叉树也是平衡二叉树

2. AVL平衡树的定义:对于AVL平衡树而言,其任意一个节点,左子树和右子树的高度差都不能超过1

3. AVL平衡树的平衡因子:节点的左右子树的高度差

4. AVL平衡树的调整:当向树中添加或删除节点时,会对树的结构发生改变,因此就需要进行平衡调整

5. 平衡调整的方式:

        1>. 左旋转:即当插入的元素在不平衡结点的右侧的右侧时,可以用左旋转进行平衡调整。

        2>. 右旋转:即当插入的元素在不平衡结点的左侧的左侧时,可以用右旋转进行平衡调整。

        3>. 左右旋转:即当插入的元素在不平衡结点的左侧的右侧时,可以先用左旋转,再用右旋转进行平衡调整。

        4>. 右左旋转:即当插入的元素在不平衡结点的右侧的左侧时,可以先用右旋转,再用左旋转进行平衡调整。

6. 映射接口的定义:

public interface Map<K, V> {
	
	//向map中添加键值对
	public void put(K key, V value);
	
	//删除map中指定key的键值对
	public V remove(K key);
	
	//判断二分搜索树中是否包含指定key
	public boolean contains(K key);
	
	//通过key从map中获取key对应的值
	public V get(K key);
	
	//修改指定key对应的值
	public void set(K key, V value);
	
	//获取map中有效元素的个数
	public int size();
	
	//判断map是否为空
	public boolean isEmpty();
	
	//获取map中的键的集合 因为map中键key是唯一的 所以用集合set进行存储
	public Set<K> keySet();
	
	//获取map中的值的列表 因为map中键value不是唯一的 所以用列表list进行存储
	public List<V> values();
	
	//获取map中键值对的集合 因为map中键值对(key:value)是唯一的 所以用集合set进行存储
	public Set<Entry<K, V>> entrySet();
	
	//定义一个获取键值对Entry<K, V>的接口
	public interface Entry<K, V> extends Comparable<Entry<K, V>>{
		
		//获取键值对的键
		public K getKey();
		
		//获取键值对的值
		public V getValue();
				
	}
}

7. 底层通过AVL平衡树实现的映射数据结构的实现:

//底层通过AVL平衡树实现的映射
/*
* AVL平衡树是最早的一种二分搜索树之一 因此AVL平衡树实现的映射中的key也应具有比较性
* 只有一个树的每个节点的平衡因子的绝对值 小于等于1 该树才能称为平衡树
* 平衡因子:当前节点的左子树高度减右子树高度的差值小于1 或者 大于-1
* 节点高度:节点的左右子树中高度的最大值 加1
* 
* 每次进行添加和删除节点后 都应更新节点的高度 并且进行平衡调整
* 在AVL平衡树中平衡调整有四种方法 右旋转 左旋转 左右旋转 右左旋转
* 右旋转:适用于不平衡节点位于当前节点左侧的左侧
* 左旋转:适用于不平衡节点位于当前节点右侧的右侧
* 左右旋转:适用于不平衡节点位于当前节点左侧的右侧
* 右左旋转:适用于不平衡节点位于当前节点右侧的左侧
*/
public class AVLTreeMap<K extends Comparable<K>, V> implements Map<K, V>{
	
	//创建AVL平衡树的节点 由五部分构成
	//键key 值value 节点高度height 左孩子指针 右孩子指针
	private class Node{
		private K key; //表示节点的键
		private V value; //表示节点的值
		private int height; //表示结点的高度 默认高度为1
		private Node leftChild; //指针域 指向节点的左孩子
		private Node rightChild; //指针域 指向节点的右孩子
		
		public Node(K key, V value) {
			this.key = key;
			this.value = value;
			height = 1;
			leftChild = null;
			rightChild = null;
		}
	}

	private Node root; //表示AVL平衡树的根节点
	
	private int size; //表示AVL平衡树的有效元素的个数
	
    public AVLTreeMap() {
		root = null;
		size = 0;
	}

	//获取以node节点为根节点的二分搜索树中指定key的节点
	private Node getNode(Node node, K key) {
		if(node == null) {
			return null;
		}
		if(key.compareTo(node.key) < 0) {
			return getNode(node.leftChild, key);
		}else if(key.compareTo(node.key) > 0) {
			return getNode(node.rightChild, key);
		}else {
			return node;
		}
	}
	
	//获取指定节点的高度
	private int getHight(Node node) {
		if(node == null) {
			return 0;
		}
		return node.height;
	}
	
	//获取指定节点的平衡因子 即该节点的左子树高度减去右子树高度
	private int getBalanceFactor(Node node) {
		if(node == null) {
			return 0;
		}
		return getHight(node.leftChild) - getHight(node.rightChild);
	}
	
	//判断该树是否是一个二分搜索树 即判断中序遍历后的结果是否是递增的即可
	public boolean isBinarySearchTree() {
		ArrayList<K> list = new ArrayList<K>();
		inOrderKey(root, list);
		for(int i = 1; i < list.size(); i++) {
			if(list.get(i - 1).compareTo(list.get(i)) > 0) {
				return false;
			}
		}
		return true;
	}
	
	//对AVL树进行中序遍历
	private void inOrderKey(Node node, List<K> list) {
		if(node == null) {
			return;
		}
		inOrderKey(node.leftChild, list);
		list.add(node.key);
		inOrderKey(node.rightChild, list);
	}
		
	//判断该树是否是一个AVL平衡树 向外提供的
	public boolean isAVLTree() {
		return isAVLTree(root);
	}
	
	//判断以node节点为根节点的树是否是AVL平衡树
	//即当前节点的平衡因子的绝对值小于等于1 并且 当前节点的是左子树和右子树都是AVL平衡树
	private boolean isAVLTree(Node node) {
		if(node == null) {
			return true;
		}
		int balanceFactor = getBalanceFactor(node);
		if(Math.abs(balanceFactor) > 1) {
			return false;
		}
		return isAVLTree(node.leftChild) && isAVLTree(node.rightChild);
	}

	//右旋转(左侧的左侧) 将y结点进行右旋转 并返回旋转后新树的根
	private Node rightRotate(Node y) {
		Node x = y.leftChild;
		Node T3 = x.rightChild;
		x.rightChild = y;
		y.leftChild = T3;
		
		//旋转后更新旋转的节点的高度
		y.height = Math.max(getHight(y.leftChild), getHight(y.rightChild)) + 1;
		x.height = Math.max(getHight(x.leftChild), getHight(x.rightChild)) + 1;
		
		return x;
	}
	
	//左旋转(右侧的右侧) 将y结点进行左旋转 并返回旋转后新树的根
	private Node leftRotate(Node y) {
		Node x = y.rightChild;
		Node T3 = x.leftChild;
		x.leftChild = y;
		y.rightChild = T3;
		
		//旋转后更新旋转的节点的高度
		y.height = Math.max(getHight(y.leftChild), getHight(y.rightChild)) + 1;
		x.height = Math.max(getHight(x.leftChild), getHight(x.rightChild)) + 1;
		
		return x;
	}
		
	//向AVL平衡树中添加键值对 向外提供的
	@Override
	public void put(K key, V value) {
		root = put(root, key, value);
	}

	//向以node为根节点的AVL平衡树中添加键值对 并返回添加后的新树的根节点
	private Node put(Node node, K key, V value) {
		if(node == null) {
			size++;
			return new Node(key, value);
		}
		if(key.compareTo(node.key) < 0) {
			node.leftChild = put(node.leftChild, key, value);
		}else if(key.compareTo(node.key) > 0) {
			node.rightChild = put(node.rightChild, key, value);
		}else {
			node.value = value;
		}
		
		//当前结点的高度需要更新
		node.height = Math.max(getHight(node.leftChild), getHight(node.rightChild)) + 1;
		
		//判断当前结点是否是平衡的 如果不平衡就要进行平衡调整
		int balanceFactor = getBalanceFactor(node);
		
		//balanceFactor > 1说明以当前节点为根的左子树不平衡
		//getBalanceFactor(node.leftChild) >= 0说明是向当前节点的左孩子的左孩子添加了元素 
		//导致以当前节点为根的树不平衡 因此为左左不平衡 
		//通过对当前节点右旋转调整		
		if(balanceFactor > 1 && getBalanceFactor(node.leftChild) >= 0) {
			return rightRotate(node);
		}
		
		//balanceFactor > 1说明以当前节点为根的左子树不平衡
		//getBalanceFactor(node.leftChild) < 0说明是向当前节点的左孩子的右孩子添加了元素 
		//导致以当前节点为根的树不平衡 因此为左右不平衡 
		//通过先对当前节点的左孩子进行左旋转 再对当前节点右旋转调整
		if(balanceFactor > 1 && getBalanceFactor(node.leftChild) < 0) {
			node.leftChild = leftRotate(node.leftChild);
			return rightRotate(node);
		}
		
		//balanceFactor < -1说明以当前节点为根的右子树不平衡
		//getBalanceFactor(node.leftChild) >= 0说明是向当前节点的右孩子的左孩子添加了元素 
		//导致以当前节点为根的树不平衡 因此为右左不平衡 
		//通过先对当前节点的右孩子右旋转 再对当前节点左旋转调整
		if(balanceFactor < -1 && getBalanceFactor(node.rightChild) >= 0) {
			node.rightChild = rightRotate(node.rightChild);
			return leftRotate(node);
		}
		
		//balanceFactor < -1说明以当前节点为根的右子树不平衡
		//getBalanceFactor(node.leftChild) < 0说明是向当前节点的右孩子的右孩子添加了元素 
		//导致以当前节点为根的树不平衡 因此为右右不平衡 
		//通过对当前节点左旋转调整
		if(balanceFactor < -1 && getBalanceFactor(node.rightChild) < 0) {
			return leftRotate(node);
		}
		
		return node;
	}

	//删除AVL平衡树中指定key的键值对 向外提供的
	@Override
	public V remove(K key) {
		Node ret = getNode(root, key);
		if(ret != null) {
			root = remove(root, key);
			return ret.value;
		}
		return null;
	}

	//删除以node为根节点的AVL平衡树中指定key的键值对 并返回删除后新树的根
	/*
	 * 删除节点与二分搜索树中的删除节点操作类似 但是不同点为:
	 * 删除节点后要向上一层返回删除后当前新树的根 但是在AVL平衡树中
	 * 不可以直接向上返回 因为删除节点后树结构就发生了改变 就需要进行平衡调整
	 * 所以删除节点后返回的新根 可以放在重新定义的节点中存着 
	 * 对其进行高度的更新 以及平衡的判断 若不平衡 则进行平衡调整
	 * 平衡调整结束 再向上一层返回
	 */
	private Node remove(Node node, K key) {
		if(node == null) {
			return null;
		}
		
		Node retNode = null; //用于存放想上一层返回的节点
		
		if(key.compareTo(node.key) < 0) {
			node.leftChild = remove(node.leftChild, key);
			retNode = node; //将要向上一层返回的节点存储起来
		}else if(key.compareTo(node.key) > 0) {
			node.rightChild = remove(node.rightChild, key);
			retNode = node; //将要向上一层返回的节点存储起来
		}else {
			if(node.leftChild == null) {
				Node rightNode = node.rightChild;
				node.rightChild = null;
				size--;
				retNode = rightNode; //将要向上一层返回的节点存储起来
			}else if(node.rightChild == null) {
				Node leftNode = node.leftChild;
				node.leftChild = null;
				size--;
				retNode = leftNode; //将要向上一层返回的节点存储起来
			}else {
				Node successor = getMinNum(node.rightChild);
				successor.rightChild = remove(node.rightChild, successor.key);
				successor.leftChild = node.leftChild;
				node.leftChild = node.rightChild = null;
				retNode = successor; //将要向上一层返回的节点存储起来
			}
		}
		
		//判断向上一层返回的节点是否为空 若删除的节点为叶子节点 则有可能为空
		if(retNode == null) {
			return null;
		}
		
		//更新要向上一层返回的节点的高度
		retNode.height = Math.max(getHight(retNode.leftChild), getBalanceFactor(retNode.rightChild)) + 1;
		//获取要向上一层返回的节点平衡因子
		int balanceFactor = getBalanceFactor(retNode);
		
		//判断要向上一层返回的节点是否平衡 若不平衡 则进行平衡调整
		/*
		 * 若要向上一层返回的节点的平衡因子大于1 则表示以该节点为根节点的左子树不平衡
		 * 并且该节点的左孩子的平衡因子大于等于0 则表示是左左不平衡
		 * 则将该节点右旋转进行平衡调整
		*/
		if(balanceFactor > 1 && getBalanceFactor(retNode.leftChild) >= 0) {
			return rightRotate(retNode);
		}
		
		/*
		 * 若要向上一层返回的节点的平衡因子大于1 则表示以该节点为根节点的左子树不平衡
		 * 并且该节点的左孩子的平衡因子小于0 则表示是左右不平衡
		 * 则先将该节点的左孩子进行左旋转 再将该节点右旋转进行平衡调整
		*/
		if(balanceFactor > 1 && getBalanceFactor(retNode.leftChild) < 0) {
			retNode.leftChild = leftRotate(retNode.leftChild);	
			return rightRotate(retNode);
		}

		/*
		 * 若要向上一层返回的节点的平衡因子小于-1 则表示以该节点为根节点的右子树不平衡
		 * 并且该节点的右孩子的平衡因子大于等于0 则表示是右左不平衡
		 * 则先将该节点的右孩子进行右旋转 再将该节点左旋转进行平衡调整
		*/
		if(balanceFactor < -1 && getBalanceFactor(retNode.leftChild) >= 0) {
			retNode.rightChild = rightRotate(retNode.rightChild);
			return leftRotate(retNode);
		}

		/*
		 * 若要向上一层返回的节点的平衡因子小于-1 则表示以该节点为根节点的右子树不平衡
		 * 并且该节点的右孩子的平衡因子小于0 则表示是右右不平衡
		 * 则将该节点左旋转进行平衡调整
		*/
		if(balanceFactor < -1 && getBalanceFactor(retNode.leftChild) < 0) {
			return leftRotate(retNode);
		}
		return retNode;
	}

	//获取以node为根节点的AVL平衡树的最小值
	private Node getMinNum(Node node) {
		if(node.leftChild == null) {
			return node;
		}else {
			return getMinNum(node.leftChild);
		}
	}

	//判断AVL平衡树中是否包含指定key
	@Override
	public boolean contains(K key) {
		return getNode(root, key) != null;
	}

	//通过key从AVL平衡树中获取key对应的值
	@Override
	public V get(K key) {
		Node node = getNode(root, key);
		if(node != null) {
			return node.value;
		}
		return null;
	}

	//修改指定key对应的值
	@Override
	public void set(K key, V value) {
		Node node = getNode(root, key);
		if(node == null) {
			throw new IllegalArgumentException("key-value is not exist");
		}
		node.value = value;
	}

	//获取AVL平衡树中有效元素的个数
	@Override
	public int size() {
		return size;
	}

	//判断AVL平衡树是否为空
	@Override
	public boolean isEmpty() {
		return size == 0 && root == null;
	}

	//对AVL平衡树进行前序遍历 向外提供的
	public void preOrder() {
		preOrder(root);
	}
	
	//对以node为根节点的AVL平衡树进行前序遍历
	private void preOrder(Node node) {
		if(node == null) {
			return;
		}
		System.out.println(node.key);
		preOrder(node.leftChild);
		preOrder(node.rightChild);
	}

	//获取map中的键的集合 因为map中键key是唯一的 所以用集合set进行存储
	@Override
	public Set<K> keySet() {
		TreeSet<K> set = new TreeSet<K>();
		inOrderKeySet(root, set);
		return set;
	}

	//中序遍历以node节点为根节点的二分搜索树中的键 并将结果存放在指定集合set中
	private void inOrderKeySet(Node node, TreeSet<K> set) {
		if(node == null) {
			return;
		}
		inOrderKeySet(node.leftChild, set);
		set.add(node.key);
		inOrderKeySet(node.rightChild, set);
	}

	//获取map中的值的列表 因为map中键value不是唯一的 所以用列表list进行存储
	@Override
	public List<V> values() {
		LinkedList<V> list = new LinkedList<V>();
		inOrderValues(root, list);
		return list;
	}

	//中序遍历以node节点为根节点的二分搜索树的值 并将结果存放在指定列表list中
	private void inOrderValues(Node node, LinkedList<V> list) {
		if(node == null) {
			return;
		}
		inOrderValues(node.leftChild, list);
		list.add(node.value);
		inOrderValues(node.rightChild, list);
	}

	//获取map中键值对的集合 因为map中键值对(key:value)是唯一的 所以用集合set进行存储
	@Override
	public Set<Entry<K, V>> entrySet() {
		TreeSet<Entry<K, V>> set = new TreeSet<Entry<K,V>>();
		inOrderEntries(root, set);
		return set;
	}

	//中序遍历以node节点为根节点的二分搜索树的键值对 并将结果存放在指定集合set中
	private void inOrderEntries(Node node, TreeSet<Entry<K, V>> set) {
		if(node == null) {
			return;
		}
		inOrderEntries(node.leftChild, set);
		set.add(new AVLEntry(node.key, node.value));
		inOrderEntries(node.rightChild, set);
	}
	
	//定义一个获取键值对Entry<K, V>的实现类 来封装键值对
	/*
	 * 用treeSet来存储键值对 treeSet底层又是通过二分搜索树实现的
	 * 因此treeSet存储的内容必须具有可比性 所以键值对的实现类中必须重写compareTo方法
	 * 键值对中的键又要具有可比性 因此键也要继承于Comparable
	 */
	private class AVLEntry implements Entry<K, V>{

		public K key;
		public V value;
		
		public AVLEntry(K key, V value) {
			this.key = key;
			this.value = value;
		}
		
		@Override
		public K getKey() { //获取键值对的键
			return key;
		}

		@Override
		public V getValue() { //获取键值对的值
			return value;
		}
		
		@Override
		public String toString() { //规定键值对的输出格式
			return key + ":" + value;
		}
		
		@Override
		public int compareTo(Entry<K, V> o) { //规定键的比较规则
			return this.key.compareTo(o.getKey());
		}
	}
}

8. 代码的测试:

public class TestAVLTreeMap {
	public static void main(String[] args) {
		AVLTreeMap<Integer, String> map = new AVLTreeMap<Integer, String>();
		map.put(6,"666");
        map.put(4,"444");
        map.put(3,"333");
        map.put(7,"777");
        map.put(5,"555");
        map.put(9,"999");
        map.put(8,"888");
        map.put(1,"111");
        map.put(2,"222");
        System.out.println("AVL平衡树键的集合:" + map.keySet());
        System.out.println("AVL平衡树值的集合:" + map.values());
        System.out.println("AVL平衡树键值对的集合:" + map.entrySet());
        System.out.println("====添加节点后的前序遍历===");
        map.preOrder();
        
        map.remove(6);
        System.out.println("AVL平衡树键的集合:" + map.keySet());
        System.out.println("AVL平衡树值的集合:" + map.values());
        System.out.println("AVL平衡树键值对的集合:" + map.entrySet());
        System.out.println("====删除节点后的前序遍历===");
        map.preOrder();
        
        //判断删除节点后 是否还是一棵AVL平衡树
        System.out.println(map.isAVLTree());
        //判断删除节点后 是否还是一棵二分搜索树
        System.out.println(map.isBinarySearchTree());
	}
}

9. 代码的运行结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值