测试用表
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;