求 前序遍历 左右值的算法

[复制链接]
查看11 | 回复9 | 2013-9-12 15:57:33 | 显示全部楼层 |阅读模式
本帖最后由 pastime_Wang 于 2013-1-10 15:01 编辑
需求是这样的,我想根据前序遍历的规则,得到每个节点的左值和右值
表结构:
Type_idNameParentLftRgt
1 商品0
118
2 食品1
211
3 肉类2
3 6
4 猪肉3
4 5
5 蔬菜类 2
710
6 白菜5
8 9
7 电器1 1217
8 电视机 7 1314
9 电冰箱 7 1516
遍历结构:
      
1商品18
     +---------------------------------------+

2食品11
12电器17

+-----------------+
+---------------------+
3肉类6
7蔬菜类10
13电视机14 15电冰箱16
4猪肉5 8白菜9
补充说明:是要根据 parent 字段关系,根据前序遍历的规则
即 start with parent = 0connect by parent = prior type_id, 遍历出来的结果计算Lft 左值和Rgt右值,上面的例子是我最终要的结果,但不知道lft和rgt这2个字段的数据如何计算出来?




回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
这和前序遍历没关系吧,看你的规则,完全是按照商品(1-18)包含食品(2-11)和电器(12-17),向下递推的这种关系来的,感觉表结果不好。
回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
估计是要用递归with了
回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
with t as (
select 'shangpin' name, 1 l, 18 r from dual union all
select 'shipin' name, 2 l, 11 r from dual union all
select 'roulei' name, 3 l, 6 r from dual union all
select 'zhurou' name, 4 l, 5 r from dual union all
select 'sucai' name, 7 l, 10 r from dual union all
select 'baicai' name, 8 l, 9 r from dual union all
select 'dianqi' name, 12 l, 17 r from dual union all
select 'dianshi' name, 13 l, 14 r from dual union all
select 'bingxiang' name, 15 l, 16 r from dual),
t1(name, l, r, p_l) as (
select
name, l, r, 0 p_l
from
t
where
l=1
union all
select
t.name, t.l, t.r, t1.l
from
t, t1
where
t.l=t1.l+1
or
t.r=t1.r-1)
select
*
from
t1
order by p_l, l;
复制代码
回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
没看明白要干啥
回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
是要根据 parent 字段关系,根据前序遍历的规则
即 start with parent = 0connect by parent = prior type_id, 遍历出来的结果计算Lft 左值和Rgt 右值,上面的例子是我最终要的结果,但不知道lft和rgt这2个字段的数据如何计算出来?
回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
哥啊,也是05年的老鸟了,问问题把结果问成了已知,把已知问成了结果。。。
回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
udfrog 发表于 2013-1-10 15:15
哥啊,也是05年的老鸟了,问问题把结果问成了已知,把已知问成了结果。。。



回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层

我总算看明白了,楼主是要按深度优先输出树,然后用笔在树的外围画轮廓,求画的顺序。
下面的写法有个隐含前提就是CONNECT BY按照深度优先输出(直接取ROWNUM),然后通过比较前后节点的LEVEL来决定是否要回溯。
WITH DATA AS (
SELECT 1 AS type_id, '商品' AS name ,0AS parent FROM DUAL
UNION ALL SELECT 2 AS type_id, '食品',1AS parent FROM DUAL
UNION ALL SELECT 3 AS type_id, '肉类',2AS parent FROM DUAL
UNION ALL SELECT 4 AS type_id, '猪肉',3AS parent FROM DUAL
UNION ALL SELECT 5 AS type_id, '蔬菜类',2AS parent FROM DUAL
UNION ALL SELECT 6 AS type_id, '白菜',5AS parent FROM DUAL
UNION ALL SELECT 7 AS type_id, '电器',1AS parent FROM DUAL
UNION ALL SELECT 8 AS type_id, '电视机',7AS parent FROM DUAL
UNION ALL SELECT 9 AS type_id, '电冰箱',7AS parent FROM DUAL
)
,d AS (
SELECT data.*,LEVEL lvl,ROWNUM seq,CONNECT_BY_ROOT(type_id) root_id
FROM DATA
START WITH parent = 0
CONNECT BY parent = PRIOR type_id
ORDER SIBLINGS BY type_id-------- 同一父亲的子节点按type_id顺序
)
,t AS (
SELECT d2.*
,LAG(lvl,1,0) OVER(PARTITION BY root_id ORDER BY seq) last_lvl
,LAG(type_id) OVER(PARTITION BY root_id ORDER BY seq) last_id
FROM (SELECT d.* FROM d
UNION ALL ---- 拼上一个伪节点,使得最后的右侧轮廓能够画完。lvl=0肯定小于上一点的深度,所以会触发回溯
SELECT NULL AS type_id,NULL AS name,0 AS parent,0 AS lvl,seq+1 AS seq,root_id

FROM d
WHERE seq=(SELECT MAX(seq) FROM d)
) d2
)
,t2 AS (
SELECT t.*
,CASE WHEN lvlt.parent

)
END||','||type_id AS str
FROM t
)
,t4 AS
(SELECT back_id,MAX(lft) lft,MAX(rgt) rgt
FROM (SELECT t3.*

,CASE WHEN type_id=back_id THEN ROWNUM END AS lft

,CASE WHEN type_id=back_id THEN NULL ELSE ROWNUM END AS rgt

FROM (SELECT t2.*

,REGEXP_SUBSTR(str,'[^,]+',1,cnt) back_id

FROM t2

,(SELECT LEVEL cnt FROM DUAL CONNECT BY LEVEL<=(SELECT MAX(lvl) FROM t))

WHERE REGEXP_SUBSTR(str,'[^,]+',1,cnt) IS NOT NULL

ORDER BY seq,cnt

) t3
)
GROUP BY back_id
)
SELECT data.type_id,data.name,t4.lft,t4.rgt
FROM t4 JOIN data ON t4.back_id=data.type_id;

回复

使用道具 举报

千问 | 2013-9-12 15:57:33 | 显示全部楼层
本帖最后由 pastime_Wang 于 2013-1-11 14:43 编辑
谢谢newkid的回复!
我的表 数据量大概有100w条,但root 节点只有一个(也就是只有一棵树),由于每次都会有新增或删除的类别(type_id),所以每次都要重新计算左、右值!
如果采用newkid的方法,是否效率上很低呢?
我的思路: 采用proc, 从root节点按照“深度优先”遍历这颗树,采用“递归”的方式,
1、初始化 左值Lft =1
2、从root 节点开始(parent = 0),按“深度优先” 遍历这颗树,若下层节点是叶子节点,当前节点 右值Rgt = 左值Lft +1
3、否则 左值Lft = 左值Lft +1,在继续向下遍历
这是一个“递归”的过程,不知道是否可行?
采用varry数组是不是性能会更高些?
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行