|
我們在使用樹形結構數據時,常常需要遍曆整棵樹或某一支下的所有結點,用於查找、打印等功能。因為樹形結構不同於數組、lianbiaodengjiandanshujujiegou,taxiangshuzhiyiyangmeigegenjiediankeyijuyouduogezijiedian,wuxianyanzhan,yincixuyaozhuanmendesuanfaqubianli。shuxingjiegoudebianliyouhenduozhongfangfa,xiamianwomenyizijinqiaojiankongzutairuanjian(以下簡稱為“RealInfo”)為例,簡單講解函數遞歸在這種遍曆方法中的應用。
在RealInfo中,“樹形控件”是表示樹狀結構數據的組件,“自由報表”是表示表格數據的組件,這兩種組件自身都提供了一些常用方法。我們現在實現這樣的功能:將樹形控件中的指定分支數據打印在自由報表中。可以利用窗口自定義函數的遞歸功能。
樹形控件中的數據顯示方式如下圖所示:

每個結點以結點編碼為唯一標識,每個結點可以顯示一個字符串作為結點文本(詳見RealInfo聯機幫助)。
本例中,我們將樹形結構數據打印在自由報表上,其效果如下圖所示:

每個根結點打印完成後,遇到子結點時打印位置自動向右、向下移動一個單元格;遇到兄弟結點時打印位置向下移動一個單元格。
現(xian)在(zai)我(wo)們(men)開(kai)始(shi)分(fen)析(xi)算(suan)法(fa)。我(wo)們(men)知(zhi)道(dao),樹(shu)的(de)遍(bian)曆(li)是(shi)指(zhi)沿(yan)著(zhe)某(mou)條(tiao)搜(sou)索(suo)路(lu)線(xian),依(yi)次(ci)對(dui)樹(shu)中(zhong)每(mei)個(ge)結(jie)點(dian)均(jun)做(zuo)一(yi)次(ci)且(qie)僅(jin)做(zuo)一(yi)次(ci)訪(fang)問(wen)。這(zhe)樣(yang),我(wo)們(men)把(ba)遍(bian)曆(li)過(guo)程(cheng)想(xiang)象(xiang)成(cheng)為(wei)一(yi)次(ci)單(dan)程(cheng)旅(lv)行(xing),出(chu)發(fa)點(dian)是(shi)樹(shu)的(de)根(gen)結(jie)點(dian),然(ran)後(hou)按(an)先(xian)自(zi)左(zuo)向(xiang)右(you)、然後自上而下的順序,先後經過每個結點,最後走到最下方的葉子結點處。
我們可以采用這樣的遍曆方式:
1) 當所在結點具有子結點時,那麼按自左向右原則,接著訪問它的第一個子結點,直到所在結點沒有子結點為止。
2) 當所在結點沒有子結點,但具有兄弟結點時,那麼按自上向下原則依次訪問它的兄弟結點。
3) 當所在結點沒有子結點,而且沒有兄弟結點時,那麼按自上向下原則訪問它父結點的兄弟結點。
分析這個過程並觀察樹形結構,我們會發現,每個父結點可以擁有n(n>=0)個子結點,若將這n個子結點看作父結點,則每個父結點仍然具有n個子結點。由此看來,每一支數據乃至整棵樹都可以看作是有限個父-子結構的組合。在樹的遍曆過程中,總是不斷的重複“父→子”這一訪問方式,因此我們可以提取這一方式形成一個函數,並利用函數遞歸來完成整個遍曆。
這個函數用於根據輸入的父結點編碼和起始打印位置將其所有子結點打印出來。算法如下:
函(han)數(shu)首(shou)先(xian)判(pan)斷(duan)輸(shu)入(ru)結(jie)點(dian)是(shi)否(fou)具(ju)有(you)子(zi)結(jie)點(dian),如(ru)果(guo)沒(mei)有(you)則(ze)返(fan)回(hui),如(ru)果(guo)有(you)則(ze)取(qu)得(de)子(zi)結(jie)點(dian)列(lie)表(biao),然(ran)後(hou)循(xun)環(huan)打(da)印(yin)每(mei)個(ge)子(zi)結(jie)點(dian)並(bing)遞(di)歸(gui)調(tiao)用(yong)自(zi)身(shen)函(han)數(shu)打(da)印(yin)其(qi)子(zi)結(jie)點(dian),當(dang)一(yi)個(ge)結(jie)點(dian)a的子結點打印完畢並返回後按相同規則依次打印的a結點的兄弟結點,直到所有兄弟結點打印完畢為止。
工程製作過程如下:
1) 新建窗口,創建樹形控件,起名為“tree”;創建自由報表起名為“report”;創建一個按鈕。
2) 創建窗口函數(用於得到指定結點的子結點編碼數組):
func_GetAllChildNodeKey(Tree& treeObj, String& strFatherNodeKey, String Array& strArrChildNodeKeys) As Int

代碼如下:
int nChildNodeCount = 0;
string strNodeKeyTemp = "";
int i = 0;
strArrChildNodeKeys.Clear();
nChildNodeCount = #treeObj.GetNodeCount(strFatherNodeKey);
for i=0 to nChildNodeCount
if strFatherNodeKey=="" then
strNodeKeyTemp = IntToStr(i,10);
else
strNodeKeyTemp = strFatherNodeKey + "." + IntToStr(i,10);
endif
strArrChildNodeKeys.Add(strNodeKeyTemp);
next
return nChildNodeCount;
3) 創建窗口函數(用於遞歸打印指定結點的子結點,不打印自身結點):
func_PrintToReport(String strFatherNodeKey, Int nCol, Int nRow, Int& nRowOffSet) As Int

代碼如下:
string strArrChildNodeKeys[];
string strNodeText = "";
int nCount = 0;
int i = 0;
func_GetAllChildNodeKey(#tree,strFatherNodeKey,strArrChildNodeKeys);
nCount = strArrChildNodeKeys.GetCount();
if nCount>0 then
if #report.ColCount()
#report.AddCol(1);
endif
for i=0 to nCount
if #report.RowCount()
#report.AddRow(1);
endif
strNodeText = #tree.GetNodeTxt(strArrChildNodeKeys[i]); //打印本結點
#report.SetTxt(nCol,nRow+nRowOffset,strNodeText);
nRowOffset = nRowOffset + 1;
nRowOffset = func_PrintToReport(strArrChildNodeKeys[i]
,nCol+1,nRow,nRowOffset); //遞歸
next
endif
return nRowOffset;
4) 創建窗口函數(用於打印初始結點自身,並啟動遞歸函數):func_Print()
代碼如下:
int nRowOffSet = 0;
#report.DelTailCol(#report.ColCount());
#report.DelTailRow(#report.RowCount());
#report.AddCol(1);
#report.AddRow(1);
#report.SetTxt(1,1,#tree.GetNodeTxt(#tree.GetCurSelNodeKey()));
func_PrintToReport(#tree.GetCurSelNodeKey(),2,2,nRowOffSet);
5) 在按鈕中鼠標點擊動作中輸入:func_Print();
6) 運(yun)行(xing)並(bing)查(zha)看(kan)效(xiao)果(guo)。運(yun)行(xing)時(shi),不(bu)選(xuan)擇(ze)樹(shu)結(jie)點(dian),點(dian)擊(ji)按(an)鈕(niu)後(hou)報(bao)表(biao)中(zhong)打(da)印(yin)出(chu)整(zheng)棵(ke)樹(shu),因(yin)為(wei)根(gen)結(jie)點(dian)文(wen)本(ben)為(wei)空(kong),所(suo)以(yi)報(bao)表(biao)第(di)一(yi)列(lie)為(wei)空(kong)。選(xuan)中(zhong)任(ren)意(yi)一(yi)個(ge)樹(shu)結(jie)點(dian)後(hou),報(bao)表(biao)中(zhong)打(da)印(yin)出(chu)本(ben)分(fen)支(zhi)所(suo)有(you)結(jie)點(dian),包(bao)含(han)本(ben)結(jie)點(dian)。
效果圖如下:

本文以RealInfoweili,jiangshuleyizhongtongguohanshudiguitiaoyonglaishixianshuxingjiegoushujubianlidefangfa,qizhongdiguihanshutishixianledayinzhidingjiediandezijiediangongneng。benfangfashiyongyushaoliangshuxingjiegoushujudebianli,dangshujuliangguodashixuyaozuojinyibuyouhua。
|