🥳每日一练-判断二叉树是否为AVL-JS简易版

这篇文章还是来点简单的:判断一颗树是否为平衡二叉树

平衡二叉树的性质是左右子树的高度差不超过 2。

因此可以用简单的逻辑来求解今天的练习。

  1. 先判断左右子树是否为平衡二叉树,如果不是,那整个树就不是平衡二叉树;如果是就继续下面的判断
  2. 求解左右子树的高度差,如果不超过 2,那这棵树就是平衡二叉树;否则就不是了

按照这个逻辑就可以写出递归代码

准备数据

先准备一个描述上图的平衡二叉树的数据

javascript 复制代码
class Node {
	constructor(value) {
		this.value = value;
		this.height = 1;
		this.left = null;
		this.right = null;
	}
}

class AVL {
	constructor() {
		this.root = null;
	}
	getHeight(node) {
		if (!node) return 0;
		return node.height;
	}
	updateHeight(node) {
		node.height = Math.max(this.getHeight(node.left), this.getHeight(node.right)) + 1;
	}

	rotateLeft(node) {
		const newRoot = node.right;
		node.right = newRoot.left;
		newRoot.left = node;

		this.updateHeight(node);
		this.updateHeight(newRoot);

		return newRoot;
	}

	rotateRight(node) {
		const newRoot = node.left;
		node.left = newRoot.right;
		newRoot.right = node;

		this.updateHeight(node);
		this.updateHeight(newRoot);

		return newRoot;
	}

	getBalance(node) {
		return this.getHeight(node.left) - this.getHeight(node.right);
	}

	balance(node) {
		if (this.getBalance(node) > 1) {
			if (this.getBalance(node.left) < 0) {
				node.left = this.rotateLeft(node.left);
			}
			return this.rotateRight(node);
		} else if (this.getBalance(node) < -1) {
			if (this.getBalance(node.right) > 0) {
				node.right = this.rotateRight(node.right);
			}
			return this.rotateLeft(node);
		}
		return node;
	}

	insert(value) {
		this.root = this._insert(this.root, value);
	}
	_insert(node, value) {
		if (!node) return new Node(value);
		if (value < node.value) node.left = this._insert(node.left, value);
		else if (value >= node.value) node.right = this._insert(node.right, value);

		this.updateHeight(node);
		return this.balance(node);
	}
}

代码中声明了两个对象。一个描述了节点的属性,另一个描述了创建 AVL 树(平衡二叉树)的各种操作。

代码有些多,具体讲解可以看这篇文章:🥳每日一练-排序二叉树中求解节点层次-JS简易版 - 掘金

创建 AVL

javascript 复制代码
const data = [3, 2, 1, 4, 5, 6, 7, 9, 8];
const avl = new AVL();
data.forEach((item) => {
	avl.insert(item);
});

console.log(avl);

输出看看 avl 的结构是否和图中描述一致:

json 复制代码
{
  root: {
    value: 4,
    height: 4,
    left: {
      value: 2,
      height: 2,
      left: {
        value: 1,
        height: 1,
        left: null,
        right: null,
      },
      right: {
        value: 3,
        height: 1,
        left: null,
        right: null,
      },
    },
    right: {
      value: 6,
      height: 3,
      left: {
        value: 5,
        height: 1,
        left: null,
        right: null,
      },
      right: {
        value: 8,
        height: 2,
        left: {
          value: 7,
          height: 1,
          left: null,
          right: null,
        },
        right: {
          value: 9,
          height: 1,
          left: null,
          right: null,
        },
      },
    },
  },
}

是不是不太好看,看着很不方便,将 json 结构的输出改成树形结构的输出,像下面这样,就很方便了:

这个怎么做到的?

javascript 复制代码
let res = "";
const printTree = (data, deeps = [1]) => {
	let space = deeps
		.slice(0, -1)
		.map((item) => {
			return item == 1 ? "|\t\t" : "\t\t";
		})
		.join("");

	space += "|__";
	res = res + space + (data?.value || "null") + "\n";

	if (!data) return res;
	printTree(data.left, [...deeps, 1]);
	printTree(data.right, [...deeps, 0]);
	return res;
};
console.log(res);

这是将 json 结构树形输出的代码,可以用这个代码输出其他的树,不仅二叉树,多叉树,都可以。

判断是否为 AVL

javascript 复制代码
const judgeAVL = (tree) => {
  if (!tree) return 0;
  
  const leftHeight = judgeAVL(tree.left);
  const rightHeight = judgeAVL(tree.right);
  
  if (leftHeight == -1 || rightHeight == -1) return -1;
  if (Math.abs(leftHeight - rightHeight) > 1) return -1;
  
  return Math.max(leftHeight, rightHeight) + 1;
};

judgeAVL是一个判断是否为 AVL 树的函数。其中步骤就是我开头提到的:

  1. 先判断左右子树是否为平衡二叉树,如果不是,那整个树就不是平衡二叉树;如果是就继续下面的判断
  2. 求解左右子树的高度差,如果不超过 2,那这棵树就是平衡二叉树;否则就不是了

函数接受 tree 的根节点作为参数,并且,如果返回值不为-1,这棵树就是一颗 AVL

测试代码:

javascript 复制代码
console.log(judgeAVL(avl.root)); //4

刚好树的高度是 4 层,没有问题

javascript 复制代码
avl.root.right = null;
console.log(judgeAVL(avl.root)); //-1

将树的的右子树直接删除,这棵树肯定就不是 AVL。测试结果输出了-1,符合预期

完整代码

javascript 复制代码
class Node {
	constructor(value) {
		this.value = value;
		this.height = 1;
		this.left = null;
		this.right = null;
	}
}
class AVL {
	constructor() {
		this.root = null;
	}
	getHeight(node) {
		if (!node) return 0;
		return node.height;
	}
	updateHeight(node) {
		node.height = Math.max(this.getHeight(node.left), this.getHeight(node.right)) + 1;
	}

	rotateLeft(node) {
		const newRoot = node.right;
		node.right = newRoot.left;
		newRoot.left = node;

		this.updateHeight(node);
		this.updateHeight(newRoot);

		return newRoot;
	}

	rotateRight(node) {
		const newRoot = node.left;
		node.left = newRoot.right;
		newRoot.right = node;

		this.updateHeight(node);
		this.updateHeight(newRoot);

		return newRoot;
	}

	getBalance(node) {
		return this.getHeight(node.left) - this.getHeight(node.right);
	}

	balance(node) {
		if (this.getBalance(node) > 1) {
			if (this.getBalance(node.left) < 0) {
				node.left = this.rotateLeft(node.left);
			}
			return this.rotateRight(node);
		} else if (this.getBalance(node) < -1) {
			if (this.getBalance(node.right) > 0) {
				node.right = this.rotateRight(node.right);
			}
			return this.rotateLeft(node);
		}
		return node;
	}

	insert(value) {
		this.root = this._insert(this.root, value);
	}
	_insert(node, value) {
		if (!node) return new Node(value);
		if (value < node.value) node.left = this._insert(node.left, value);
		else if (value >= node.value) node.right = this._insert(node.right, value);

		this.updateHeight(node);
		return this.balance(node);
	}
}

const data = [3, 2, 1, 4, 5, 6, 7, 9, 8];
const avl = new AVL();
data.forEach((item) => {
	avl.insert(item);
});

// 判断是否为平衡二叉树
const judgeAVL = (tree) => {
	if (!tree) return 0;
	const leftHeight = judgeAVL(tree.left);
	const rightHeight = judgeAVL(tree.right);
	if (leftHeight == -1 || rightHeight == -1) return -1;
	if (Math.abs(leftHeight - rightHeight) > 1) return -1;
	return Math.max(leftHeight, rightHeight) + 1;
};
let res = "";
const printTree = (data, deeps = [1]) => {
	let space = deeps
		.slice(0, -1)
		.map((item) => {
			return item == 1 ? "|\t\t" : "\t\t";
		})
		.join("");

	space += "|__";
	res = res + space + (data?.value || "null") + "\n";

	if (!data) return res;
	printTree(data.left, [...deeps, 1]);
	printTree(data.right, [...deeps, 0]);
	return res;
};


console.log(judgeAVL(avl.root));

avl.root.right = null;
printTree(avl.root);
console.log(res);

console.log(judgeAVL(avl.root));

总结:

这篇文章分享了如何判断一颗树是否为平衡二叉树,同时也给出了 JS 代码的实现。读者可以直接 copy 文末的代码

你觉得这篇文章怎么样?喜欢就点赞+关注吧

相关推荐
Code_Solitude18 分钟前
C语言动态内存 + 柔性数组 + 数组名退化
c语言·算法·柔性数组
上海潭龙东海科技有限公司24 分钟前
客运站管控之动态孪生技术方案(新增一屏管控模块
数据仓库·人工智能·算法·重构·动态规划
总要学点什么3 小时前
第105篇:三维特效-流动图片墙体效果——让箭头沿墙长滚,它俩竟是同一份代码
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
YLCHUP3 小时前
【思维&图论建模】题解:债权(debt)_图论_建模_思路转化_思维题目_C++算法竞赛
c++·笔记·算法·数学建模·贪心算法·深度优先·图论
思麟呀3 小时前
背包DP(01背包+完全背包)
c++·算法
Agent世界4 小时前
用 CDP 做发布自动化:四道机器闸门与产物验收
前端·javascript
总要学点什么4 小时前
第104篇:三维特效-流动墙体效果——让贴图沿墙流动,它却只肯走一条对角线
开发语言·javascript·ecmascript·gis·webgl·材质·cesium
笑容掩饰我的失落4 小时前
区块链从小白到精通(七):Solidity 语法精讲——数据类型、函数、修饰器与事件
算法·区块链·php·智能合约·哈希算法
程序员大阳4 小时前
副队长ES6+教程(3)--解构赋值
前端·javascript·es6·赋值·解构
神膘护体小月半5 小时前
css - 元素背景图由 hover 触发,初次加载时闪动的解决办法
前端·javascript·css