郭其先生利用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;
相关推荐
橘颂TA9 小时前
【剑斩OFFER】算法的暴力美学——力扣 227 题:基本计算机Ⅱ
c++·算法·leetcode·职场和发展·结构于算法
信奥卷王9 小时前
2025年12月GESPC++二级真题解析(含视频)
算法
Z1Jxxx9 小时前
输入n个数进行排序,要求先按奇偶后按从小到大的顺序排序
数据结构·算法
乐迪信息9 小时前
乐迪信息:船体AI烟火检测,24小时火灾自动预警
人工智能·物联网·算法·目标检测·语音识别
Z1Jxxx9 小时前
整除整除整除
开发语言·c++·算法
尽兴-9 小时前
MySQL 中一条 SQL 的执行流程详解
sql·mysql·adb·dba
Swift社区9 小时前
LeetCode 471 编码最短长度的字符串
算法·leetcode·职场和发展
没有天赋那就反复9 小时前
JAVA length
java·开发语言·算法
Tisfy9 小时前
LeetCode 0712.两个字符串的最小ASCII删除和:反向思维保留最大(动态规划)
算法·leetcode·动态规划·字符串·dp·子序列