描述二叉树简单结构
/**
* 二叉树节点的可选类型(节点实例 或 null)
* @template T 节点值的类型,与 TreeNode<T> 保持一致
* @description 简化 TreeNode<T> | null 的重复书写,提升代码可读性
*/
export type NodeType<T> = TreeNode<T> | null;
/**
* 二叉树节点类(泛型支持任意数据类型)
* @template T 节点存储的值的类型
*/
export class TreeNode<T> {
/**
* 二叉树节点构造函数
* @param value 节点存储的值
* @param left 左子节点(默认为 null)
* @param right 右子节点(默认为 null)
*/
constructor(
public value: T,
public left: NodeType<T> = null,
public right: NodeType<T> = null
) {}
}
遍历二叉树
前序遍历
const preOrder = (root: NodeType<string>) => {
if (root === null) return;
// 先访问根节点
console.log('pre:' + root.value + '\t');
preOrder(root.left);
preOrder(root.right);
};
中序遍历
const inOrder = (root: NodeType<string>) => {
if (root === null) return;
inOrder(root.left);
// 左子树遍历后访问根节点
console.log('in:' + root.value + '\t');
inOrder(root.right);
};
后序遍历
const postOrder = (root: NodeType<string>) => {
if (root === null) return;
postOrder(root.left);
postOrder(root.right);
// 最后访问根节点
console.log('pre:' + root.value + '\t');
};
还原二叉树
前序中序还原二叉树
/**
* Generate binary tree from preOrderList and inOrderList
* @param preOrderList
* @param inOrderList
* @returns root
*/
function buildTree<T>(preOrderList: Array<T>, inOrderList: Array<T>): NodeType<T> {
// the precondition of rebuilding Binary Tree
if (!preOrderList.length || !inOrderList.length) return null;
if (preOrderList.length !== inOrderList.length)
throw new Error('The lengths of preOrderList and inOrderList must be equal.');
// first element of preOrderList is root
const rootVal = preOrderList[0];
const root = new TreeNode<T>(rootVal);
// the length of preOrderList equal to 1 (the end of recursion)
if (preOrderList.length === 1) return root;
// find the rootVal' s index in inOrderList
const inOrderListRootIdx = inOrderList.indexOf(rootVal);
// Error handler
if (inOrderListRootIdx === -1) {
throw new Error(`Root value ${rootVal} not found in inOrderList`);
}
// divide inOrderList: left & right
const leftInOrderList = inOrderList.slice(0, inOrderListRootIdx);
const rightInOrderList = inOrderList.slice(inOrderListRootIdx + 1);
// divide preOrderList: left & right
// the size of left tree equals the length of leftInOrderList
const leftSubTreeLen = leftInOrderList.length;
const leftPreOrderList = preOrderList.slice(1, leftSubTreeLen + 1);
const rightPreOrderList = preOrderList.slice(leftSubTreeLen + 1);
// Recursively call the buildTree method
root.left = buildTree(leftPreOrderList, leftInOrderList);
root.right = buildTree(rightPreOrderList, rightInOrderList);
return root;
}
中序后序还原二叉树
function buildTree<T>(postOrderList: Array<T>, inOrderList: Array<T>): NodeType<T> {
if (!postOrderList.length || !inOrderList.length) return null;
if (postOrderList.length !== inOrderList.length)
throw new Error('The lengths of postOrderList and inOrderList must be equal.');
const rootVal = postOrderList[postOrderList.length - 1];
const root = new TreeNode<T>(rootVal);
if (postOrderList.length === 1) return root;
const inOrderRootIdx = inOrderList.indexOf(rootVal);
if (inOrderRootIdx === -1) {
throw new Error(`Value ${rootVal} not found in inOrderList. Failed to construct binary tree`);
}
const leftInOrderList = inOrderList.slice(0, inOrderRootIdx);
const rightInOrderList = inOrderList.slice(inOrderRootIdx + 1);
const leftTreeSize = leftInOrderList.length;
const leftPostOrderList = postOrderList.slice(0, leftTreeSize);
const rightPostOrderList = postOrderList.slice(leftTreeSize, postOrderList.length - 1);
root.left = buildTree(leftPostOrderList, leftInOrderList);
root.right = buildTree(rightPostOrderList, rightInOrderList);
return root;
}
深度优先与广度优先
深度优先搜索
/**
* @param root
* @param target
* @returns isFind
*/
function depthSearch(root: TreeNode, target: string): boolean {
if (root === null) return false;
if (root.value === target) return true;
const left = depthSearch(root.left!, target);
const right = depthSearch(root.right!, target);
return left || right;
}
广度优先搜索
/**
* @param root
* @param target
* @returns isFind
*/
function breadthFirstSearch(root: TreeNode, target: string): boolean {
if (root === null) return false;
const queue: Array<TreeNode> = [];
queue.push(root);
while (queue.length > 0) {
const currRoot = queue.shift();
if (!currRoot) break;
if (currRoot.value === target) return true;
if (currRoot.left !== null) queue.push(currRoot.left);
if (currRoot.right !== null) queue.push(currRoot.right);
}
return false;
}
二叉树对比
判断是否相同
/**
* @param treeA
* @param treeB
* @returns isEqual
*/
const compareTree = (treeA: TreeNode | null, treeB: TreeNode | null): boolean => {
// 终止条件1:两个节点都为空 → 相等
if (treeA === null && treeB === null) return true;
// 终止条件2:一个为空、一个不为空 → 不相等
if (treeA === null || treeB === null) return false;
// 终止条件3:节点值不相等 → 不相等
if (treeA.value !== treeB.value) return false;
// 二叉树可能有扭转情况
const res1 = compareTree(treeA?.left!, treeB?.left!) && compareTree(treeA?.right!, treeB?.right!);
const res2 = compareTree(treeA?.left!, treeB?.right!) && compareTree(treeB?.left!, treeA?.right!);
return res1 || res2;
};
二叉树的diff算法
辅助类型
// 改变
const enum ChangeType {
DEL = '删除',
ADD = '增加',
UPD = '修改',
}
// 父节点不变的节点类型
type InfoWithParent = {
type: ChangeType;
origin: NodeType;
now: NodeType;
parent: NodeType; // 单 parent 属性
};
// 父节点改变的节点类型
type InfoWithOldNewParent = {
type: ChangeType;
origin: NodeType;
now: NodeType;
oldParent: NodeType; // 旧父节点
newParent: NodeType; // 新父节点
};
// 节点信息类型
type info = InfoWithOldNewParent | InfoWithParent;
// 二叉树节点类型
type NodeType = TreeNode | null;
// 二叉树对比
function diffTree(treeA: NodeType, treeB: NodeType): Array<info> {
// 收集差异信息的数组
const diffList = new Array<info>();
// 二叉树对比主方法
function recursiveDiff(
treeA: NodeType,
treeB: NodeType,
rootA: NodeType = null,
rootB: NodeType = null
) {
// 说明两颗树引用是相同的(包括都为 null),说明没有差异
if (treeA === treeB) return;
// 新增
if (treeA === null && treeB !== null)
return handleDiff(ChangeType.ADD, treeA, treeB, rootA, rootB, diffList);
// 删除
if (treeA !== null && treeB === null)
return handleDiff(ChangeType.DEL, treeA, treeB, rootA, rootB, diffList);
// 修改: 对比值是否变化
if (treeA?.value !== treeB?.value)
handleDiff(ChangeType.UPD, treeA, treeB, rootA, rootB, diffList);
// 递归左右子树
recursiveDiff(treeA?.left!, treeB?.left!, treeA, treeB);
recursiveDiff(treeA?.right!, treeB?.right!, treeA, treeB);
}
// 差异信息收集方法
function handleDiff(
type: ChangeType,
oldNode: NodeType,
newNode: NodeType,
oldParent: NodeType,
newParent: NodeType,
diffList: info[]
) {
const oldParentVal = oldParent?.value;
const newParentVal = newParent?.value;
// 先比较父节点是否变化
if (oldParentVal === newParentVal) {
diffList.push({
type,
origin: oldNode,
now: newNode,
parent: newParent,
});
} else {
diffList.push({
type,
origin: oldNode,
now: newNode,
oldParent,
newParent,
});
}
}
recursiveDiff(treeA, treeB);
return diffList;
}
原创
二叉树
本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。



评论交流
欢迎留下你的想法