悬赏捉拿算法高手(1000大洋)(300分)

S

szhhp

Unregistered / Unconfirmed
GUEST, unregistred user!
1、有数据表如下:
项目ID 父项目ID 项目名称
0A * 在建项目
0B * 完工项目
SH_1 0A 上海项目1
SH_2 0A 上海项目2
SH_3 0B 上海项目3
SZ_1 0A 深圳项目1
SZ_2 0B 深圳项目2
SZ_3 0A 深圳项目3
.......


从父子的层次上是不固定的,可能2层,也可能4-5层或更多
记录数可能高达5000。

2、要求:用TREEVIEW显示,所以要将表织成树(利用“项目ID”,和“父项目ID”)
由于层次不定,记录过多,所以,要求算法得当,避免影响速度,让人无法忍受。
并且要一次全部织完。
请高手赐教。
 
呵呵,看了吓一跳[:D]
 
不好办,每次增加时,都避免不了查找上级节点。
最好是先只增加第一级,当用点了节点,再增加此节点的下一层节点,
这样每次花的时间就不会太多了。
 
这个我曾经实现一个类似的程序,那是一个棋谱查询程序,
采用了牺牲空间的方法
每一条纪录都包括所有的上级节点信息
即根,第一级,第二级,第三级。。。。。。。。
查找的sql根据要查的级数动态生成
记录数〉50000,平均耗时《1秒
 
用dxdbtreeview搞定!
 
原始表的顺序组织不合理,用程序先生成一个临时表,
按以下顺序(层次结构,父-子结构)排列就可很轻松地完成:

当然可以按层次结构直接生成文本文件,这样最后 TTreeView.LoadFromFile(...)即可

项目ID 父项目ID 项目名称
0A * 在建项目
SH_1 0A 上海项目1
SH_2 0A 上海项目2
SZ_1 0A 深圳项目1
0B * 完工项目
SH_3 0B 上海项目3
SZ_2 0B 深圳项目2
SZ_3 0A 深圳项目3
 
to bfun:
你的方法可行,但我要求一次织完。
to 白马小将;
你的方法无疑是最快的,可数据表结构不能更改,如果组织临时表,还是一个算法问题。
to sung_001:
如果想用控件,我就不提问了:),我想把问题搞懂。
to jsxjd:
这个临时表如何生成?我要问的其实就是这个问题。要求算法精练有效率。

还请各位大虾继续》》》》》》
 
好像是个很老的问题了,帮你提前吧!
 
先生成第一层的所有结点(例如:A),并在A结点下增加一个temp结点,当click某个结点时,先删除temp
结点再生成他的一层子结点(B),同样在B子结点下再增加一个temp,如果A没有子结点,那么A就是叶结点
程序如下:
procedure Tform1.addchild(node:Ttreenode);
begin
query3.close;
//query3的内容自己写
query3.open;
while not query3.Eof do
begin
new(a);
a^.xmdh:=query3.fieldbyname('xmdh').asinteger;
a^.xmmc:=query3.fieldbyname('xmmc').value;
v_node:=treeview1.Items.AddChildObject(node,a^.xmmc,a);
treeview1.Items.AddChild(v_node,'temp');
inc(i);
query3.Next;
end;

end;

procedure deltemp(node:Ttreenode);
var
t:Ttreenode;
begin
if node.Count=1 then
begin
t:=node.getFirstChild;
if t.Text='temp' then t.Delete;
end;
end;

M$就是使用的该算法!!!!
别忘记给俺分分:p
 
忘了告诉大家了,项目ID是没有规则的,也就是说顶级项目ID也不是固定的。

TO jack_4826:
分暂时不能给你:)
因为在众多记录中,选出定级记录的方法,你没告诉我:)
 
这种繁琐的事情,还是你自己干吧,
别指望别人
 
库的扫描次数,应该是层次数!!!!!!
 
这一问题已经得到圆满解决,只需对表扫描两次
应该不会再有更高效率!!!!!!!!
调试成功!!!!!


1. 父子关系表生成树

// 此程序专门用于父子关系表生成树。
// 在查找父节点时使用了两分法。
// 在 5000个节点的情况下最多找 13 14次,
// 而且这一查找是在内存中进行的,大大提高了效率
// 表字段说明
// id 项目标识
// pId 相应父项目标识
// ItemName 项目名称
// 表中项目顺序要求,父项目在子项目前

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, DBTables, Db, ComCtrls, Grids, DBGrids, ADODB,
commctrl; //需要使用这个单元

type
TForm1 = class(TForm)
TreeView1: TTreeView;
Button1: TButton;
ADOTable1: TADOTable;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}
type MyItem=Record
id:string;
p:HTreeItem; //树节点句柄,也是节点的唯一标识。而且节省空间
end;
var Its:array of MyItem;

function getIdIndex(var Its:array of MyItem;nStart,nEnd:integer;id:string):Integer;
////// 两分法搜索节点,这是效率的基础
var
n:integer;
begin
Result:=-1;
if nEnd<nStart then exit;
if (nEnd=nStart) then begin
if its[nEnd].id =id then Result:=nEnd;
Exit;
end;

if (id<its[nStart].id) or (id>its[nEnd].id) then exit;

n:=((nStart+nEnd) shr 1);
if id > its[n].id then Result:=getIdIndex(its,n+1,nEnd,id)
else Result:=getIdIndex(its,nStart,n,id);

end;
procedure TForm1.Button1Click(Sender: TObject);
var
n,nItems:integer;
s,st,sName:String;
Node:TTreeNode;
begin
with AdoTable1 do
begin
//取出所有非叶节点Id,因为这些节点可能会重复使用
//这些节点都在父节点 pid 中。
active:=false;
indexName:='AAA';
indexFieldNames:='pId';
active:=true;

First;
s:='';
n:=0;
while not(eof) do begin
st:=trim(FieldbyName('pId').asstring);
if (st<>'*') and (st<>s) then begin
inc(n);
setLength(Its,n);
Its[n-1].id:=st;
Its[n-1].p:=nil;
s:=st;
end;
next;
end;
nItems:=n;

//这时生成树,只要按表中原记录顺序扫描一遍就行
active:=false;
indexName:='';
indexFieldNames:='';
active:=true;

first;
while not(eof) do begin
s:=trim(FieldbyName('Id').asstring);
st:=trim(FieldbyName('pId').asstring);
sName:=trim(FieldbyName('ItemName').asstring);

if (st='*') then begin //处理根节点,肯定还没有添加
node:=Treeview1.Items.AddChild(nil,sName);
n:=getIdIndex(Its,0,nItems-1,s); //应该都能找到
Its[n].p:=node.itemid;
end else begin
n:=getIdIndex(Its,0,nItems-1,st); //找到根节点
node:=Treeview1.Items.GetNode(Its[n].p);
node:=Treeview1.Items.AddChild(node,sName);
n:=getIdIndex(Its,0,nItems-1,s); //找到节点
if n>=0 then its[n].p:=node.ItemId ;
//没找到的是叶节点,不需要处理
end;
next;
end;
end;
setLength(its,0);
end;

end.
 
一点小补充:
头尾分别加上Treeview1.Items.beginUpdate和Treeview1.Items.endUpdate还能提高速度。
 
to szhhp:

项目ID 父项目ID 项目名称
0A * 在建项目
0B * 完工项目
SH_1 0A 上海项目1
SH_2 0A 上海项目2
SH_3 0B 上海项目3
SZ_1 0A 深圳项目1
SZ_2 0B 深圳项目2
SZ_3 0A 深圳项目3

query如下:
最上面一层肯定没有父项ID或者有固定ID,以上图为例,父项ID='*'
select * from table
where 父项ID='*';

这样查出所有的第一层结点,按照我说的方法生成第一层,然后
click某一结点,得到该结点的项目ID(这个应该不难),假如为点击OA,
那么他的项目ID=‘OA’下面的查询这样写:
select * from table
where 父项ID='OA';

这样又可以把点击的结点的第一层子结点查到,再生成TREEVIEW,
如此递归。
 
用弟归一次搞掂!!不管多少级都都可以
新增后查出父节点写入数据库即可,如果怕数据多影响速度,可先生成一级目录树,当点击
节点时再生成下级目录数。
 
我觉得递归不妥,这里涉及对数据库的移动操作。
 
假如你的数据库是oracle的话,那么它自己的sleect语句就支持connect by可以连起来
 
顶部