二叉树搜索的实现
实现二叉树的搜索功能,包括深度优先搜索(DFS)和广度优先搜索(BFS)两种方式
问题
在二叉树中搜索指定的节点值,需要实现两种常见的搜索算法:
- 深度优先搜索(DFS):包括前序、中序、后序遍历
- 广度优先搜索(BFS):按层级遍历
给定一个二叉树和目标值,判断树中是否存在该值的节点。
解答
// 定义二叉树节点
class TreeNode {
constructor(val) {
this.val = val;
this.left = null;
this.right = null;
}
}
// 深度优先搜索 - 递归实现
function searchDFS(root, target) {
// 基础情况:节点为空
if (root === null) {
return false;
}
// 找到目标值
if (root.val === target) {
return true;
}
// 递归搜索左子树和右子树
return searchDFS(root.left, target) || searchDFS(root.right, target);
}
// 深度优先搜索 - 迭代实现(使用栈)
function searchDFSIterative(root, target) {
if (root === null) {
return false;
}
const stack = [root];
while (stack.length > 0) {
const node = stack.pop();
// 找到目标值
if (node.val === target) {
return true;
}
// 先压入右子节点,再压入左子节点(保证左子树先被访问)
if (node.right) {
stack.push(node.right);
}
if (node.left) {
stack.push(node.left);
}
}
return false;
}
// 广度优先搜索 - 使用队列
function searchBFS(root, target) {
if (root === null) {
return false;
}
const queue = [root];
while (queue.length > 0) {
const node = queue.shift();
// 找到目标值
if (node.val === target) {
return true;
}
// 将左右子节点加入队列
if (node.left) {
queue.push(node.left);
}
if (node.right) {
queue.push(node.right);
}
}
return false;
}
// 返回搜索路径的DFS实现
function searchPathDFS(root, target, path = []) {
if (root === null) {
return null;
}
// 将当前节点加入路径
path.push(root.val);
// 找到目标值,返回路径
if (root.val === target) {
return path;
}
// 在左子树中搜索
const leftPath = searchPathDFS(root.left, target, [...path]);
if (leftPath) {
return leftPath;
}
// 在右子树中搜索
const rightPath = searchPathDFS(root.right, target, [...path]);
if (rightPath) {
return rightPath;
}
return null;
}
使用示例
// 构建测试二叉树
// 1
// / \
// 2 3
// / \ \
// 4 5 6
const root = new TreeNode(1);
root.left = new TreeNode(2);
root.right = new TreeNode(3);
root.left.left = new TreeNode(4);
root.left.right = new TreeNode(5);
root.right.right = new TreeNode(6);
// 测试深度优先搜索(递归)
console.log(searchDFS(root, 5)); // true
console.log(searchDFS(root, 7)); // false
// 测试深度优先搜索(迭代)
console.log(searchDFSIterative(root, 6)); // true
console.log(searchDFSIterative(root, 8)); // false
// 测试广度优先搜索
console.log(searchBFS(root, 3)); // true
console.log(searchBFS(root, 9)); // false
// 测试搜索路径
console.log(searchPathDFS(root, 5)); // [1, 2, 5]
console.log(searchPathDFS(root, 6)); // [1, 3, 6]
console.log(searchPathDFS(root, 7)); // null
// 性能对比示例
const startDFS = performance.now();
searchDFS(root, 6);
const endDFS = performance.now();
console.log(`DFS耗时: ${endDFS - startDFS}ms`);
const startBFS = performance.now();
searchBFS(root, 6);
const endBFS = performance.now();
console.log(`BFS耗时: ${endBFS - startBFS}ms`);
关键点
- 递归 vs 迭代:DFS可以用递归或栈实现,递归代码更简洁,但可能栈溢出;迭代使用显式栈,更可控
- DFS特点:空间复杂度O(h),h为树高度;适合搜索深层节点或需要回溯的场景
- BFS特点:空间复杂度O(w),w为树最大宽度;适合搜索浅层节点或需要层级信息的场景
- 时间复杂度:两种方法都是O(n),n为节点总数,最坏情况需要遍历所有节点
- 提前终止:找到目标值后立即返回,避免不必要的遍历
- 路径记录:如需记录搜索路径,需要在递归过程中维护路径数组
- 边界处理:注意处理空树和空节点的情况
- 数据结构选择:DFS用栈(或递归调用栈),BFS用队列
目录