ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AVL树

2021-12-28 23:03:54  阅读:170  来源: 互联网

标签:node return 旋转 AVL key 平衡 节点


本章代码是上一篇《二叉树初步总结》的序章,主要记录AVL树的学习过程。

概念:AVL树是一种自平衡树,添加或移除节点时AVL树会尝试保持自平衡,任意一个节点的左子树和右子树高度最多相差1,添加或移除节点时,AVL树会尽可能尝试转换为完全树。

首先,定义一个AVLTree类

该类只需要结成BinarySearchTree类,再对其中的插入、移除方法进行覆盖即可。

class AVLTree extends BinarySearchTree {
	constructor(compareFn = defaultCompare) {
		super(compareFn)
		this.compareFn = compareFn
		this.root = null
	}
}

其次,定义一个计算节点高度的方法

getNodeHeight(node) {
  if(node == null){
	return -1
  }
  return Math.max(this.getNodeHeight(node.left),this.getNodeHeight(node.right))+1
}

 该方法利用了递归原理,只要节点不为空,就一直向下查,每查一次就+1,直到查到最终结果为止。

再定义一个计算平衡因子的方法

getBlanceFactor(node) {
	const heightDifference = this.getNodeHeight(node.left) - this.getNodeHeight(node.right)
	switch (heightDifference) {
		case -2:
			return BalanceFactor.UNBALANCED_RIGHT
		case -1:
			return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT
		case 1:
			return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT
		case 2:
			return BalanceFactor.UNBALANCED_RIGHT
		default:
			return BalanceFactor.BALANCED
	}
}

  为了避免在代码中直接处理平衡因子的数值,创建一个用来作为计数器的常量

const BalanceFactor = {
	UNBALANCED_RIGHT:1,
	SLIGHTLY_UNBALANCED_RIGHT:2,
	BALANCED:3,
	SLTGHTLY_UNBALANCED_LEFT:4,
	UNBALANCED_LEFT:5
}

如图所示,可以清晰的看出AVL树与非AVL树的区别。

旋转


为了将不平衡的树进行平衡,就需要通过旋转的方式来进行操作,旋转应该如何理解?

右旋转

  如图示,这样一棵树左右子节点的高度差为1,他是处于平衡状态的,但是,当我们再插入一个节点5之后

  当前树的左右子节点高度差达到了2,这个时候平衡性已经被破坏了,就需要进行一些操作去平衡它。

  平衡之后树为

  

 

 

 

这时候执行的操作就是旋转,这么说可能不太明白

看图示,这个时候就是以节点7为原点,将15节点向右侧旋转,旋转之后再用7这个节点去替代原来的15节点,即可以生成一个满足平衡需求的树。注意,在调整时,找离最新插入的节点最近的不平衡的树进行调整。

 

 

现在看一种复杂一点的情况,是节点15存在右侧子节点的时候,这个时候我们需要执行四步操作

  1. 将节点15置于节点20所在的位置
  2. 将节点15的左侧子节点保持不变
  3. 将节点20的左侧子节点置为节点15的右侧子节点
  4. 将节点15的右侧子节点置为节点20

最终结果是这样的

整个流程的代码如下

	// 向右的单旋转
	rotationR(node){
		const tmp = node.left
		node.left = tmp.right
		tmp.right = node
		return tmp
	}

  

左旋转

左旋转即某个节点的右侧子节点导致不平衡的时候,需要对右侧子节点进行左旋转来保证平衡,整体思路与右转选一样,代码如下

// 向左的单旋转
	rotationL(node){
		const tmp = node.right
		node.right = tmp.left
		tmp.left = node
		return tmp
	}

先左旋转再右旋转

 

如图所示,这种情况是根节点的左侧子节点的右侧子节点导致的不平衡,首先,我们对左侧子节点进行平衡,我们知道当右侧子节点不平衡时使用左转

对根节点20的左侧子节点进行左旋之后的结果如图所示,这个时候就变成了所有导致不平衡的情况都是左侧子节点导致的,所以这个时候再对整棵树进行向右的旋转

代码如下:

// 向右的双旋转,先左再又
	rotationLR(node){
		node.left = this.rotationL(node.left)
		return this.rotationR(node)
	}

 

 

先右旋转再左旋转

先右再左是当右侧子节点的左侧子节点导致不平衡时的情况使用的,和先左再右相反,代码如下:

// 向左的双旋转
	rotationRL(node){
		node.right = this.rotationR(node.right)
		return this.rotationL(node)
	}

向AVL树中插入节点

与普通二叉树不同的是,每次插入值之后,都需要去判断一下当前这个节点是否失衡了,如果失衡了需要去进行相应的旋转平衡节点

代码如下:

	insert(key){
		this.root = this.insertNode(this.root,key)
	}
	insertNode(node,key){
		if(node == null){
			return new Node(key)
		}else if(this.compareFn(key,node.key) === Compare.LESS_THAN){
			node.left = this.insertNode(node.left,key)
		}else if(this.compareFn(key,node.key) == Compare.BIGGER_THAN){
			node.right = this.insertNode(node.right,key)
		}else{
			return node
		}
		// 如果需要,就去对树进行平衡操作
		const balanceFactor = this.getBlanceFactor(node)
		if(balanceFactor === balanceFactor.UNBALANCED_LEFT) {
			if(this.compareFn(key,node.left.key) === Compare.LESS_THAN){
				node = this.rotationR(node)
			}else{
				node = this.rotationLR(node)
			}
		}
		if(balanceFactor === balanceFactor.UNBALANCED_RIGHT){
			if(this.compareFn(key,node.right.key) === Compare.BIGGER_THAN){
				node = this.rotationR(node)
			}else{
				return this.rotationRL(node)
			}
		}
		return node
	}

  整体思路为,先插入相应节点,然后去判断这个节点在插入子节点之后是否失衡了,如果是左侧子节点失衡再去判断插入的值是插入了左侧子节点还是右侧子节点,如果是左侧子节点那么直接右旋,如果是右侧子节点就需要先左旋再右旋。当是右侧失衡时思路相反。

  移除节点

  移除节点时直接调用之前的移除节点的方法,然后再对树的平衡进行检测,再针对左侧子节点的左侧子节点不平衡、左侧子节点的右侧子节点不平衡,右侧子节点的左侧子节点不平衡,右侧子节点的右侧子节点不平衡这几种情况进行相应的旋转处理。

 

标签:node,return,旋转,AVL,key,平衡,节点
来源: https://www.cnblogs.com/elonkwl/p/15743173.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有