开始一道算法题
今天在力扣刷了一道题:
给你一个二叉树的根节点 root , 检查它是否轴对称
输入是一个数组,输出是布尔值,如果是一个对称二叉树,那就返回true;如果不是,那就返回false。
我第一次的代码是这样写的
/**
* @param {TreeNode} root
* @return {boolean}
*/
var isSymmetric = function (root) {
// 计算二叉树的层数
const tierLevel = Math.log2(root.length + 1);
// 遍历每一层
for (let i = 1; i <= tierLevel; i++) {
let count = 0;
// 计算当前层的左侧索引
let leftIndex = Math.pow(2, i) + count;
// 计算当前层的右侧索引
let rightIndex = Math.pow(2, i + 1) - (count + 1);
// 当左侧索引小于右侧索引时,继续比较
while (leftIndex < rightIndex) {
// 如果对应位置的节点值不相等,返回 false
if (root[leftIndex - 1] !== root[rightIndex - 1]) {
return false;
}
// 更新计数器和索引值,继续比较下一对节点
count++;
leftIndex = Math.pow(2, i) + count;
rightIndex = Math.pow(2, i + 1) - (count + 1);
}
}
// 如果所有对应位置的节点都相等,返回 true
return true;
};
这个代码将会检查每一层的对应位置的节点是否相等。如果在遍历过程中发现有不相等的节点,函数会立即返回 false。如果所有对应位置的节点都相等,那么函数最后返回 true。
此处涉及到一些数学规律。一个满二叉树,从第一层开始排序,
- 第一层节点节点序号是2^0
- 第二层节点序号是2^1+0,2^1+1
- 第三层节点序号是2^2+0,2^2+1,2^2+2,2^2+3
- 第n层节点序号是2^(n-1)+0, 2^(n-1)+1, ...., 2^(n-1)+[2^(n-1)-1]
所以代码中的左侧索引为Math.pow(2, i) + count
,右侧索引为Math.pow(2, i + 1) - (count + 1)
,目的是,先比较一层中的最左边和最右边,然后依次往中间比较,如果有不相等的就不对称,如果遍历完一层,就遍历下一层。
优化我的代码
我问GPT:上面的代码是否可以优化?
GPT:可以,下面是优化后的代码
// 定义 isSymmetric 函数,输入为一个表示二叉树的数组
var isSymmetric = function (root) {
// 计算二叉树的层数
const levels = Math.floor(Math.log2(root.length + 1));
// 遍历每一层
for (let level = 1; level < levels; level++) {
// 计算当前层的起始索引
const start = Math.pow(2, level) - 1;
// 计算当前层的结束索引
const end = Math.pow(2, level + 1) - 2;
// 计算当前层的节点数
const levelNodes = end - start + 1;
// 遍历当前层的一半节点
for (let i = 0; i < levelNodes / 2; i++) {
// 计算当前对比节点的左侧索引
const leftIndex = start + i;
// 计算当前对比节点的右侧索引
const rightIndex = end - i;
// 如果对应位置的节点值不相等,返回 false
if (root[leftIndex] !== root[rightIndex]) {
return false;
}
}
}
// 如果所有对应位置的节点都相等,返回 true
return true;
};
代码优化的点:
算法递归版代码
我问GPT: 在判断是否为对称二叉树有其他办法吗 GPT:有的,还可以将数组转成二叉树的形式,再进行递归判断,而且递归代码要更简洁 我:是嘛?快给我看看 GPT:好的,下面是实例代码
// 将数组转换为二叉树结构的函数,传入数组和起始索引(默认为0)
const arrayToTree = (array, index = 0) => {
// 如果数组为空或索引超出范围,返回 null
if (!array || array.length === 0 || index >= array.length) return null;
// 创建当前节点并设置其值
const currentNode = new TreeNode(array[index]);
// 设置当前节点的左子节点
currentNode.left = arrayToTree(array, 2 * index + 1);
// 设置当前节点的右子节点
currentNode.right = arrayToTree(array, 2 * index + 2);
// 返回当前节点
return currentNode;
};
// 检查二叉树是否对称的函数,传入一个表示二叉树的数组
const isSymmetric = (array) => {
// 如果数组为空,返回 false
if (!array || array.length === 0) return false;
// 将数组转换为二叉树结构
const tree = arrayToTree(array);
// 定义递归函数来检查左右子树是否对称
const isEqual = (left, right) => {
// 如果左右子树都为 null,返回 true
if (left === null && right === null) return true;
// 如果左右子树有一个为 null,返回 false
if (left === null || right === null) return false;
// 检查左右子树的值是否相等,以及递归检查左右子树的子树是否对称
return (
left.val === right.val && isEqual(left.left, right.right) && isEqual(left.right, right.left)
);
};
// 检查根节点的左右子树是否对称
return isEqual(tree.left, tree.right);
};
我: 递归的方式确实更好理解了,也更加直观了,谢谢你 GPT:不客气,请问还有什么可以帮助你 我:嗯,将数组变成二叉树,你提供的方案是递归,请问有没有迭代版本 GPT: 有的,下面迭代版本
数组如何转成二叉树
// 根据给定的数组生成二叉树结构
const generateArray = (array) => {
// 如果数组为空或长度为0,返回null
if (!array || array.length === 0) return;
// 初始化根节点并将其添加到队列中
const node = new TreeNode(array[0]);
const queue = [node];
// 初始化数组索引
let index = 1;
// 当索引未到达数组末尾时,继续处理节点
while (index < array.length) {
// 从队列中取出当前节点
const currentNode = queue.shift();
// 如果数组当前索引的值不为null,则创建左子节点并将其添加到队列中
if (array[index] !== null) {
currentNode.left = new TreeNode(array[index]);
queue.push(currentNode.left);
}
// 增加索引
index++;
// 如果索引未到达数组末尾且数组当前索引的值不为null,则创建右子节点并将其添加到队列中
if (index < array.length && array[index] !== null) {
currentNode.right = new TreeNode(array[index]);
queue.push(currentNode.right);
}
// 增加索引
index++;
}
// 返回根节点
return node;
};
我:哇谢谢你
总结:
文章开头是一个算法题,判断一个二叉树是否对称,首先用的方法是一个直接数组判断法,后面GPT提供了用二叉树的数据结构,并且用迭代的方法判断是否对称。再后来又对数组转成二叉树的方法进行了追问。
这种体验真是不错,像跟着一个老师来学习😁