对树进行节点汇总,要先汇子节点.再汇父节点(逐级汇总

17 篇文章 0 订阅
16 篇文章 0 订阅

create table tb(ID int,  PID int,  NAME varchar(10) , VALUE int,  [LEVEL] int  )
insert into tb values(0 ,    null,  'x1' ,    null,      1)
insert into tb values(1 ,    0  ,  'x2' ,    null,      2)
insert into tb values(2 ,    1  ,  'x3' ,    100 ,      3)
insert into tb values(3 ,    1  ,  'x4' ,    200 ,      3)
go
每个节点有对应的值,现要对这个树进行逐级汇总,有什么高效的解决方案?
有高手提供了汇总函数(function f_cid)进行汇总的办法,但在数据量较大时运行时间过长,有没有更高效的处理方法?

create table tb(ID int, PID int, NAME varchar(10) , VALUE int,   [LEVEL] int  )
insert into tb values(0 ,     null,   'x1' ,     null,       1)
insert into tb values(1 ,     0   ,   'x2' ,     null,       2)
insert into tb values(2 ,     1   ,   'x3' ,     100 ,       3)
insert into tb values(3 ,     1   ,   'x4' ,     200 ,       3)
go

--查询指定节点及其所有子节点的函数
create function f_cid(@ID int) returns @t_level table(id int , level int)
as
begin
 
declare @level int
 
set @level = 1
 
insert into @t_level select @id , @level
 
while @@ROWCOUNT > 0
 
begin
   
set @level = @level + 1
   
insert into @t_level select a.id , @level
   
from tb a , @t_Level b
   
where a.pid = b.id and b.level = @level - 1
 
end
 
return
end
go

--0
select sum(value) as [0] from tb a , f_cid(0) b where a.id = b.id 
/*
0          
-----------
300

(所影响的行数为 1 行)
*/

--1
select sum(value) as [1] from tb a , f_cid(1) b where a.id = b.id
/*
1          
-----------
300

(所影响的行数为 1 行)
*/

--2
select sum(value) as [2] from tb a , f_cid(2) b where a.id = b.id
/*
2          
-----------
100

(所影响的行数为 1 行)
*/

--3
select sum(value) as [3] from tb a , f_cid(3) b where a.id = b.id
/*
3          
-----------
200

(所影响的行数为 1 行)
*/

drop table tb
drop function f_cid

上面漏了,句子不完整,补上
select PID,ID,name,(select sum(value) from tb a inner join f_cid(m.ID) b on a.id = b.id) val
from tb m

 

 

 

 

上次浅谈了树的遍历之后,这次再浅谈一下树的汇总。此处的汇总是指将树中某节点的数据按指定的汇集到它的父节点中。例如,可以将树节点中的数值累加到它的父节点中。仍如树的遍历一文,我将使用两种简单的算法,递归与和迭代,来实现这一功能

1. 树节点
仍然沿用树的遍历一文中的TreeNode/GenericTreeNode,为便于阅读,将GenericTreeNode中的若干关键属性展示如下,

public   class  GenericTreeNode < T >   implements  TreeNode {

    
private  T userObject  =   null ;

    
private  TreeNode parent  =   null ;

    
private  List < GenericTreeNode < T >>  children  =   new  ArrayList < GenericTreeNode < T >> ();

    ......
}


2. 递归法
仍然先从最简单的递归法开始,

public   static  Double recursiveGatherValue(GenericTreeNode < Double >  node) {
    Double sumValue 
=   null ;
    
if  (node.isLeaf()) {
        
return  node.getUserObject();
    } 
else  {
        sumValue 
=  node.getUserObject();
        List
< GenericTreeNode < Double >>  children  =  node.getChildren();
        
for  ( int  i  =   0 , size  =  children.size(); i  <  size; i ++ ) {
            Double bufGatherValue 
=  recursiveGatherValue(children.get(i));
            sumValue 
+=  bufGatherValue;
        }
    }

    node.setUserObject(sumValue);
    
return  sumValue;
}

递归法还是一如既往的简单易懂。与递归遍历树相比,递归汇总树的程序基本上没大的变化,我就不赘述了...

3. 迭代法
与迭代遍历树相比,迭代汇总树的程序有一些明显的变化。当初在思考迭代法时,有个问题一直困绕着我:如何将下级节点的值赋给它的父节点,并且父节点的值要不断的进行累加。在JavaWorld@TW中提出这个问题之后,很快就得到了清晰的解答(真的很感谢社区里的大大们)。毫无疑问,用迭代法遍历一棵树时需要使用一个栈,但同时,为了维护节点与汇总值之间的关系,还需要另一个栈进行辅助。具体程序如下所示,

public   static   void  iterativeGatherValue(GenericTreeNode < Double >  node) {
    Stack
< NodeValueTuple < Double >>  nodeValueStack  =   new  Stack < NodeValueTuple < Double >> ();
    Stack
< GenericTreeNode < Double >>  nodeStack  =   new  Stack < GenericTreeNode < Double >> ();

    nodeStack.push(node);
    Double sumValue 
=   new  Double( 0.0D );
    
while  ( ! nodeStack.isEmpty()) {
        GenericTreeNode
< Double >  bufNode  =  nodeStack.pop();
        
if  (bufNode  ==   null ) {
            NodeValueTuple
< Double >  bufNodeValueTuple  =  nodeValueStack.pop();
        bufNodeValueTuple.node.setUserObject(sumValue);

        Double bufValue 
=  bufNodeValueTuple.node.getUserObject();
            sumValue 
+=  bufValue;
        } 
else   if  (bufNode.isLeaf()) {
            sumValue 
+=  bufNode.getUserObject();
        } 
else  {
            nodeValueStack.add(
new  NodeValueTuple < Double > (bufNode, sumValue));

            sumValue 
=   new  Double( 0.0D );
            nodeStack.push(
null );
            nodeStack.addAll(bufNode.getChildren());
        }
    }
}

在遍历树的过程中,会将某节点N与它的汇总值一同置入一个栈(nodeValueStack)中,当节点N有子节点时,则将它的子节点及其汇总值也置入栈中,节点N与它的子节点之间使用一个NULL值进行分隔;如果节点N是叶节点则累加汇总值;如果节点N为NULL,则表示子节点们的汇总已结束,
NodeValueTuple是一个二元组,用于维护节点与它的汇总值之间的关系,代码如下所示,

public   class  NodeValueTuple < V >  {

    
public   final  GenericTreeNode < V >  node;
    
public   final  V value;

    
public  NodeValueTuple(GenericTreeNode < V >  node, V value) {
        
this .node  =  node;
        
this .value  =  value;
    }
}

在上述的汇总中,均只累加了叶节点中的数值,而不管分枝节点和根节点本身所拥有的数值。如果要累加这些节点本身的数值,应该如何做呢?大家自己做做看吧,肯定非常简单 ^_^

4. 小结
树的汇总肯定是一个十分常见的应用,除了汇总数据之外,我们还可以汇集节点中的对象,如汇总挂载在节点上的集合对象中的元素,使得父节点能够拥有所有子节点所拥有的元素。上述方法的效率应该不算低,主要是因为所有的树节点只需要访问一次。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值