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

这篇文章分享一个 2022 年考研的数据结构题目:

有两棵非空二叉树 T1,T2。二叉树的存储结构式都是线性存储:

javascript 复制代码
const T1 = [40, 25, 60, -1, 30, -1, 80, -1, -1, 27];
const T2 = [40, 50, 60, -1, 30, -1, -1, -1, -1, -1, 35];

请设计一个算法,判断 T1 和 T2 是否为搜索二叉树

线性存储,一般按照层次遍历的顺序存储,先将二叉树的第一层放到数组的第 0 个位置,然后将第二层放到数组的第 1 第 2 个位置;然后将第三层放到数组的第 3,4,5,6 个位置,依次类推,如果遇到节点为空的情况,就将节点的值置为-1

判断一颗树是否为搜索二叉树,有一个判断标准,就是每个节点都大于左子树的所有节点,且都小于右子树的所有节点。

说着容易,可是要怎么做呢?对于每一个节点都求一遍左右子树的最大最小节点吗?可以换个角度求解。从叶子节点开始计算左子树的最大节点,和右子树的最小节点。然后把计算结果给叶子节点的父节点,这样父节点就获得了以自己作为根节点的树的最大值和最小值。

如果这个父节点的父节点想要最大值还是最小值,都没有问题。依次类推,由下而上,层层递进,就可以判断以任意节点作为根节点的子树是否为搜索二叉树。

代码

javascript 复制代码
const judgeBiSearchTree2 = (data) => {
	const min = [...data];
	const max = [...data];
	for (let i = data.length - 1; i > 0; i--) {
		if (data[i] == -1) continue;
		const parentIndex = Math.floor((i - 1) / 2);
		if (i % 2 == 1 && data[parentIndex] > max[i]) min[parentIndex] = min[i];
		else if (i % 2 == 0 && data[parentIndex] < min[i]) max[parentIndex] = max[i];
		else return false;
	}
	return true;
};

用顺序结构作为二叉树的存储结构,根和左右子树满足这样的一个关系:

  • 假设节点在数组中的下标是 i,那么左节点的下标就是 2i+1,右节点的下标就是 2i+2;所以左节点的下标一定是奇数,右节点是偶数。

所以代码中使用i % 2 == 1i % 2 == 0 来判断节点是左节点还是右节点。

  • 假设节点在数组中的下标是 i,那么节点的父节点的下标就是Math.floor((i - 1) / 2)。这个在纸上画个二叉树就能理解了

代码逻辑:

  1. 先初始化两个数组,min 和 max,min 表示一个节点所代表的子树的最小值;max 表示一个节点所代表的子树的最大值。
  2. 从 data 数组的后面往前遍历,即从叶子节点向上遍历
  3. 开始遍历。先获取父节点的下标
  4. 如果当前节点是父节点的左节点,就用父节点和当前节点的所代表的最大值比较,如果大于,就满足二叉搜索树的定义。并且更新父节点的最小值。因为一个节点所代表子树的最小值肯定是来自这个节点的左子树
  5. 如果当前节点是父节点的右节点,就用父节点和当前节点的所代表的最小值比较,如果小于,就满足二叉搜索树的定义。并且更新父节点的最大值。因为一个节点所代表子树的最大值肯定是来自这个节点的右子树
  6. 如果走到了第三个 else,那就说明这个节点不符合二叉搜索树的定义了,直接返回 false
  7. 如果遍历顺利走完了,说明每个节点都满足二叉搜索树,就返回 true 吧

测试代码:

javascript 复制代码
const printBiTree = (data, index = 0) => {
	if (data[index] == -1 || index >= data.length) return;
	printBiTree(data, index * 2 + 1);
	console.log(data[index]);
	printBiTree(data, index * 2 + 2);
};

const T1 = [40, 25, 60, -1, 30, -1, 80, -1, -1, 27];
const T2 = [40, 50, 60, -1, 30, -1, -1, -1, -1, -1, 35];

printBiTree(T1);
// 25
// 27
// 30
// 40
// 60
// 80

二叉搜索树有个性质,左节点 < 当前节点 < 右节点,这就意味着二叉搜索树的中序遍历是一个升序序列。我们可以通过输出的序列,人眼判断这棵树是否为二叉搜索树

上面的输出是升序序列没问题

javascript 复制代码
console.log(judgeBiSearchTree(T1)); 
// true

和人眼判断一致,没有问题。

修改 T1 数组:

javascript 复制代码
const T1 = [40, 25, 60, -1, 30, -1, 80, -1, -1, 99]; // 27 -> 99
printBiTree(T1);
// 25
// 99
// 30
// 40
// 60
// 80
console.log(judgeBiSearchTree(T1)); 
false

将 T1 数组中最后一个值换成了 99,中序遍历不再是升序序列了,而且judgeBiSearchTree的返回值也就变成了 false

解法二

上面提到了利用中序遍历,用人眼辅助判断代码编写是否正确。其实这道题的解法,也可以使用中序遍历来解决。

我们可以在中序遍历的过程中,就直接判断遍历到的序列是否是一个升序序列。来看看怎么实现吧

javascript 复制代码
let num = -1;

const judgeBiSearchTree2 = (data, index = 0) => {
	if (data[index] == -1 || index >= data.length) return true;
	if (judgeBiSearchTree(data, index * 2 + 1) == false) return false;
	if (num < data[index]) num = data[index];
	else return false;
	if (judgeBiSearchTree(data, index * 2 + 2) == false) return false;
	return true;
};

在函数的外面声明了一个 num,来存储上一个遍历到的数据。在遍历过程,会将遍历到的数据和 num 进行比较,如果大于 num,那就更新 num,继续往后遍历;如果小于 num,说明这次遍历到的数据小于上一次遍历到的数据,即遍历序列不是升序序列,也就意味着这棵树不是二叉搜索树。

思想就是这样,代码逻辑也是这样,很简单,直接来看测试代码

javascript 复制代码
const T1 = [40, 25, 60, -1, 30, -1, 80, -1, -1, 27];
const T1_2 = [40, 25, 60, -1, 30, -1, 80, -1, -1, 99];

printBiTree(T1);
console.log(judgeBiSearchTree2(T1));
// 25
// 27
// 30
// 40
// 60
// 80
// true

printBiTree(T1_2);
console.log(judgeBiSearchTree2(T1_2));
// 25
// 99
// 30
// 40
// 60
// 80
// false

测试结果符合预期,代码没有问题

总结

这篇文章分享了两种方式来判断一颗树是否为平衡二叉树,代码简单清晰,测试过程详实,逻辑严谨,是个不可多得的好文章。下篇文章继续分享更多好的算法小练习

你觉得这篇文章怎么样?我每天都会分享一篇算法小练习,喜欢就点赞+关注吧

相关推荐
轻口味几秒前
命名空间与模块化概述
开发语言·前端·javascript
前端小小王36 分钟前
React Hooks
前端·javascript·react.js
迷途小码农零零发1 小时前
react中使用ResizeObserver来观察元素的size变化
前端·javascript·react.js
xiaoshiguang33 小时前
LeetCode:222.完全二叉树节点的数量
算法·leetcode
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
TT哇3 小时前
*【每日一题 提高题】[蓝桥杯 2022 国 A] 选素数
java·算法·蓝桥杯
真滴book理喻4 小时前
Vue(四)
前端·javascript·vue.js
ZSYP-S4 小时前
Day 15:Spring 框架基础
java·开发语言·数据结构·后端·spring
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法