# 深度优先搜索

比较次数: 0

已访问节点:

# 算法原理

深度优先搜索(Depth-First Search,简称DFS)是一种用于遍历或搜索树或图的算法。它的基本思想是从一个顶点开始,沿着一条路径一直走到底,然后回溯到上一个节点,继续探索其他路径,直到访问完所有可达的顶点。

# 算法步骤

  1. 从起始顶点开始,标记它为已访问
  2. 递归地访问该顶点的每个未访问的相邻顶点
  3. 如果某个相邻顶点没有未访问的相邻顶点,则回溯到上一个顶点
  4. 重复步骤2和3,直到所有顶点都被访问

# 代码实现

// 递归实现(邻接表表示)
function dfs(graph, start, visited = new Set()) {
  // 标记当前顶点为已访问
  visited.add(start);
  console.log(start);  // 访问顶点
  
  // 递归访问所有未访问的相邻顶点
  for (const neighbor of graph[start]) {
    if (!visited.has(neighbor)) {
      dfs(graph, neighbor, visited);
    }
  }
}

// 非递归实现(使用栈)
function dfsIterative(graph, start) {
  const visited = new Set();
  const stack = [start];
  
  while (stack.length > 0) {
    const vertex = stack.pop();
    
    if (!visited.has(vertex)) {
      visited.add(vertex);
      console.log(vertex);  // 访问顶点
      
      // 将未访问的相邻顶点压入栈中
      for (const neighbor of graph[vertex]) {
        if (!visited.has(neighbor)) {
          stack.push(neighbor);
        }
      }
    }
  }
}

// 在二叉树中使用DFS
class TreeNode {
  constructor(val) {
    this.val = val;
    this.left = null;
    this.right = null;
  }
}

// 前序遍历(根-左-右)
function preorderDFS(root) {
  if (root === null) return;
  
  console.log(root.val);  // 访问根节点
  preorderDFS(root.left); // 遍历左子树
  preorderDFS(root.right);// 遍历右子树
}

// 中序遍历(左-根-右)
function inorderDFS(root) {
  if (root === null) return;
  
  inorderDFS(root.left);  // 遍历左子树
  console.log(root.val);   // 访问根节点
  inorderDFS(root.right); // 遍历右子树
}

// 后序遍历(左-右-根)
function postorderDFS(root) {
  if (root === null) return;
  
  postorderDFS(root.left); // 遍历左子树
  postorderDFS(root.right);// 遍历右子树
  console.log(root.val);   // 访问根节点
}

# 复杂度分析

# 时间复杂度

  • O(V + E),其中V是顶点数,E是边数
  • 对于树形结构:O(n),其中n是节点数

# 空间复杂度

  • 递归实现:O(h),h是图的深度或树的高度(递归调用栈的开销)
  • 非递归实现:O(V),需要visited集合和栈来存储顶点

# 算法特点

# 优点

  • 实现简单,容易理解
  • 空间复杂度相对较低
  • 适合搜索深层次的结构
  • 可以用于检测环和寻找路径

# 缺点

  • 可能会陷入很深的分支
  • 不一定能找到最短路径
  • 对于广度很大的图,效率可能不如BFS

# 适用场景

  1. 遍历树形结构
  2. 查找图中的路径或环
  3. 解决迷宫问题
  4. 拓扑排序
  5. 查找连通分量

# 实际应用

  1. 文件系统遍历
  2. 游戏中的路径查找
  3. 编译器的语法分析
  4. 社交网络中的关系链分析

# 优化方案

  1. 剪枝:在某些条件下提前结束搜索
  2. 记忆化:存储已经计算过的结果
  3. 迭代加深:逐步增加搜索深度
// 带剪枝的DFS示例(查找目标值)
function dfsWithPruning(graph, start, target, visited = new Set()) {
  if (start === target) {
    return true;  // 找到目标,提前结束搜索
  }
  
  visited.add(start);
  
  for (const neighbor of graph[start]) {
    if (!visited.has(neighbor)) {
      if (dfsWithPruning(graph, neighbor, target, visited)) {
        return true;  // 在某个分支中找到目标
      }
    }
  }
  
  return false;  // 当前分支未找到目标
}

// 迭代加深DFS
function iterativeDeepeningDFS(graph, start, maxDepth) {
  for (let depth = 0; depth <= maxDepth; depth++) {
    const visited = new Set();
    if (dfsWithDepth(graph, start, depth, visited)) {
      return true;  // 在当前深度找到目标
    }
  }
  return false;  // 在限定深度内未找到目标
}

function dfsWithDepth(graph, vertex, depth, visited) {
  if (depth === 0) {
    return vertex === target;  // 到达目标深度,检查是否为目标顶点
  }
  
  visited.add(vertex);
  
  for (const neighbor of graph[vertex]) {
    if (!visited.has(neighbor)) {
      if (dfsWithDepth(graph, neighbor, depth - 1, visited)) {
        return true;
      }
    }
  }
  
  visited.delete(vertex);  // 回溯时移除访问标记
  return false;
}