郭其先生利用DeepSeek实现的PostgreSQL递归CTE实现DFS写法

测试用表

sql 复制代码
CREATE TABLE tree_nodes (
    id INT PRIMARY KEY,
    parent_id INT REFERENCES tree_nodes(id),
    name VARCHAR(50)
);

INSERT INTO tree_nodes VALUES 
(1, NULL, '根节点'),
(2, 1, '子节点1'),
(3, 1, '子节点2'),
(4, 2, '孙子节点1'),
(5, 2, '孙子节点2'),
(6, 3, '孙子节点3');

使用递归 CTE 实现 DFS:

sql 复制代码
WITH RECURSIVE dfs AS (
    -- 锚点:从根节点开始
    SELECT 
        id,
        parent_id,
        name,
        1 AS depth,
        ARRAY[id] AS path,
        ARRAY[name]::text[] AS path_names,
        FALSE AS is_cycle
    FROM tree_nodes
    WHERE parent_id IS NULL
    
    UNION ALL
    
    -- 递归部分:深度优先遍历
    SELECT 
        tn.id,
        tn.parent_id,
        tn.name,
        d.depth + 1,
        d.path || tn.id,
        d.path_names || tn.name,
        tn.id = ANY(d.path) AS is_cycle
    FROM tree_nodes tn
    JOIN dfs d ON tn.parent_id = d.id
    WHERE NOT d.is_cycle  -- 防止循环
)
-- 按深度优先顺序输出
SELECT 
    id,
    parent_id,
    name,
    depth,
    path,
    path_names
FROM dfs
ORDER BY path;

使用栈模拟 DFS

sql 复制代码
WITH RECURSIVE dfs_stack AS (
    -- 初始栈:包含根节点
    SELECT 
        1 AS step,
        id AS current_node,
        name,
        ARRAY[id] AS stack,
        ARRAY[]::INT[] AS visited,
        'visit' AS action
    FROM tree_nodes
    WHERE parent_id IS NULL
    
    UNION ALL
    
    -- 模拟栈操作:弹出、压入
    SELECT 
        d.step + 1,
        CASE 
            -- 如果有未访问的子节点,访问第一个
            WHEN EXISTS (
                SELECT 1 FROM tree_nodes tn 
                WHERE tn.parent_id = d.current_node 
                AND tn.id != ALL(d.visited)
            ) THEN (
                SELECT tn.id 
                FROM tree_nodes tn 
                WHERE tn.parent_id = d.current_node 
                AND tn.id != ALL(d.visited)
                ORDER BY tn.id
                LIMIT 1
            )
            -- 否则回溯
            ELSE d.stack[array_length(d.stack, 1) - 1]
        END,
        tn.name,
        CASE 
            -- 访问新节点:压栈
            WHEN EXISTS (
                SELECT 1 FROM tree_nodes tn 
                WHERE tn.parent_id = d.current_node 
                AND tn.id != ALL(d.visited)
            ) THEN d.stack || (
                SELECT tn.id 
                FROM tree_nodes tn 
                WHERE tn.parent_id = d.current_node 
                AND tn.id != ALL(d.visited)
                ORDER BY tn.id
                LIMIT 1
            )
            -- 回溯:出栈
            ELSE d.stack[1:array_length(d.stack, 1) - 1]
        END,
        d.visited || d.current_node,
        CASE 
            WHEN EXISTS (
                SELECT 1 FROM tree_nodes tn 
                WHERE tn.parent_id = d.current_node 
                AND tn.id != ALL(d.visited)
            ) THEN 'push'
            ELSE 'pop'
        END
    FROM dfs_stack d
    LEFT JOIN tree_nodes tn ON tn.id = d.current_node
    WHERE array_length(d.stack, 1) > 0  -- 栈不为空时继续
)
SELECT 
    step,
    current_node,
    name,
    stack,
    action,
    visited
FROM dfs_stack
ORDER BY step;
相关推荐
迷途之人不知返15 小时前
算法系列2:滑动窗口
算法
Herbert_hwt16 小时前
C语言零基础入门:循环控制与数据类型详解
c语言·数据结构·算法
Tisfy17 小时前
LeetCode 3090.每个字符最多出现两次的最长子字符串:二重循环 / 滑动窗口
算法·leetcode·字符串·题解·模拟·双指针·滑动窗口
-dzk-17 小时前
【技巧】LC 136.只出现一次的数字
算法·异或
有点。17 小时前
C++二叉树二(练习题)
数据结构·c++·算法·图论
专注仿真18 小时前
问答大模型技术方案算法实现-RAPTOR树构建算法与BEG集成使用
python·算法
zlinear数据采集卡19 小时前
数据采集卡从入门到精通(10):采样率与分辨率的核心关系——反比律、架构分布与过采样
arm开发·嵌入式硬件·算法·fpga开发·架构·开源
GeekZHR20 小时前
C语言指针进阶补充6:动态内存管理、mem系列内存函数、复杂指针声明,一次补齐指针的“三大盲区“
java·c语言·算法·指针
Herbert_hwt20 小时前
第七章 Java深入理解枚举类型
java·开发语言·算法
.道阻且长.20 小时前
8.LeetCode算法习题讲解--滑动窗口--长度最小的子数组
算法·leetcode·职场和发展