NET平臺下帶權限控制的TreeView控件節點生成算法

 

一、引言

在應用系統開發中,TreeView是一種使用頻率很高的控件。它的主要特點是能夠比較清晰地實現分類、導航、瀏覽等功能。因而,它的使用方法與編程技巧也一直受到技術人員的關注。隨着應用需求的變化,在很多情況下我們需要實現數據顯示的權限控制,即用戶看到的數據是經過過濾的,或是連續值,或是一些離散的值。就TreeView而言,原先可能顯示出來的是完整的具有嚴格父子關係得節點集,而經權限過濾後所要顯示的節點可能會變得離散,不再有完整的繼承關係。本文針對這一問題,通過對已有實現方法進行分析,提出改進算法。所附示例程序進一步解釋了算法設計思想。

 

二、三種常見生成方式的簡單分析

如文[2,3]所述,TreeView的生成基本上有三種方式:

1.         界面設計時在TreeView設計器或者代碼中直接填充TreeView節點。
這種方式通過拖放控件的方式生成樹,應用範圍窄,是一種非編程方式;

2.         XML文件中建立樹形結構。
這種方式通過XML文件()生成樹,從形式上來說,這種方式是比較直觀的。因爲XML本身就是一棵“樹”,在.NET 平臺下TreeView的自動生成代碼中,TreeView的實際內容也是由XML表示的。此外,基於XML文件生成樹對異構環境下的分佈式應用具有重要意義。事實上,利用XML作爲通用數據傳遞格式已得到普遍認可;

3.         從數據庫中得到數據(在.NET中,我們可以理解爲一個數據集),建立樹形結構。
這種方式通過父子關係遞歸生成樹,是最容易理解的一種編程實現方式。一般是自頂向下遞歸生成,得到廣泛應用。

這裏,我們不妨舉一個實際的例子來說明一下,假設我們有這樣的數據集(可以看作是一個公司的部門列表):

 

TagValue

ContentValue

ParentID

G01

行銷部

 

G02

顧問部

 

G03

研發部

 

G04

測試部

 

GS01

行銷一部

G01

GS02

行銷二部

G01

GS03

行銷三部

G01

GSL01

行銷一部北京辦

GS01

GSL02

行銷一部上海辦

GS01

GS04

顧問一部

G02

GS05

顧問二部

G02

GS06

研發一部

G03

GS07

研發二部

G03

GS08

測試一部

G04

GS09

測試二部

G04

GSL03

研發一部杭州分部

GS06

GSL04

研發一部西安分部

GS06

1  示例數據集

其中,TagValue是節點的實際值,ContentValue是用戶界面上節點顯示的值或者說標籤值,ParentID是節點的父節點的TagValue。若節點爲根節點,一般設ParentID爲空或等於本身的TagValue

       默認情況下,我們可以按照下面的算法把所有的節點裝配成一棵樹,

算法1:通過父子關係遞歸生成樹基本算法

l         Step 0:數據準備,給定數據集。
一般來說數據集遵循這樣的格式,即(TagValue,ContentValue,ParentID);

l         Step 1:給定待增加子節點的節點(初始時一般爲根節點),記作CurNode,以及待增加節點的ParentID值(初始時爲根節點的ParentID),記作CurParentID;

l         Step 2:在數據集中查找具有指定ParentID值的所有節點,得到節點集objArr[]
if (objArr == null)
   return;
else
{
  //
遍歷節點集
  for(int i=0; i<objArr.Length;i++)
  {
    
objArr[i]添加爲CurNode的子節點,同時遞歸(即將objArr[i]作爲CurNodeobjArr[i]TagValue作爲CurParentIDgoto Step 1;
  }
}

 

最終可以得到下圖所示的TreeView



1 TreeView效果圖

這種方法的缺陷在於"由父節點及子節點"的遍歷順序意味着每個子節點的父節點必須存在,否則將搜索不到,即可能出現斷層現象。在很多實際應用中,我們發現這種實現方式不能完全奏效,最典型的情況就是當需要對衆節點所表徵的實際值(比如機構列表,人員列表,資源列表等)進行權限控制時,這時往往從數據庫中篩選出來的數據集中節點會出現斷層現象。比如我們假設設定權限時給定數據如表2,即把第一行“行銷部”去掉(注:權限過濾操作已超出本文討論的範圍,這裏假定數據集已準好),則運用算法1生成的TreeView如圖2所示。

TagValue

ContentValue

ParentID

G02

顧問部

 

G03

研發部

 

G04

測試部

 

GS01

行銷一部

G01

GS02

行銷二部

G01

GS03

行銷三部

G01

GSL01

行銷一部北京辦

GS01

GSL02

行銷一部上海辦

GS01

GS04

顧問一部

G02

GS05

顧問二部

G02

GS06

研發一部

G03

GS07

研發二部

G03

GS08

測試一部

G04

GS09

測試二部

G04

GSL03

研發一部杭州分部

GS06

GSL04

研發一部西安分部

GS06

2 給定數據集


2 TreeView效果圖

可以看到,這裏產生了節點遺漏現象。一般來說,我們可以從兩方面入手去解決問題,一方面可以修正數據集,另一方面則可以修改生成樹算法。顯然直接修正數據集是很複雜的,且會帶來效率問題。而單方面修改生成樹算法也是不是很好(即把遺漏的節點直接插到根節點下),因爲這時會出現父輩和晚輩同級的現象。

三、通過深度編號遞歸生成樹算法

回顧到已有的一些方法(文[1~5]),其中基於節點深度生成樹的方法給我們一些啓發,我們在構造數據集時可以增加深度字段,但這裏的深度不是簡單的層級號,是一個擴展了的概念,具體地說其實是一個深度編號,它與父輩編號存在一定的對應關係。比如表1所示的數據集可以作如下編號:

TagValue

ContentValue

ParentID

DepthID

G01

行銷部

 

a001

G02

顧問部

 

a002

G03

研發部

 

a003

G04

測試部

 

a004

GS01

行銷一部

G01

a001001

GS02

行銷二部

G01

a001002

GS03

行銷三部

G01

a001003

GSL01

行銷一部北京辦

GS01

a001001001

GSL02

行銷一部上海辦

GS01

a001001002

GS04

顧問一部

G02

a002001

GS05

顧問二部

G02

a002002

GS06

研發一部

G03

a003001

GS07

研發二部

G03

a003002

GS08

測試一部

G04

a004001

GS09

測試二部

G04

a004002

GSL03

研發一部杭州分部

GS06

a003001001

GSL04

研發一部西安分部

GS06

a003001002

3 帶深度編號的數據集

其中,DepthID即是節點的深度編號。生成深度編號的過程其實也不復雜,首先我們可以制定編號的規則,比如層級編號的前綴、編碼長度、起始值等。當給某個節點編號時,只要找到所在層級的最大編號,然後增1。具體實現過程這裏不再細述。

於是,我們很自然地想到借鑑算法1的思想設計基於深度編號的生成樹程序。這時,我們可以根據當前節點的深度編號尋找其後代節點集,但要給出一個最大跨度(可以理解爲最高級與最低級間的間隔級數),因爲不可能無限制地找下去。這種方法可以部分程度上彌補"由父節點及子節點"的遍歷的缺陷,因爲當出現斷層時會沿着編號繼續往後找。但是還是會可能漏掉,比如我們給定數據集(把“研發一部”過濾掉):

TagValue

ContentValue

ParentID

DepthID

G01

行銷部

 

a001

G02

顧問部

 

a002

G03

研發部

 

a003

G04

測試部

 

a004

GS01

行銷一部

G01

a001001

GS02

行銷二部

G01

a001002

GS03

行銷三部

G01

a001003

GSL01

行銷一部北京辦

GS01

a001001001

GSL02

行銷一部上海辦

GS01

a001001002

GS04

顧問一部

G02

a002001

GS05

顧問二部

G02

a002002

GS07

研發二部

G03

a003002

GS08

測試一部

G04

a004001

GS09

測試二部

G04

a004002

GSL03

研發一部杭州分部

GS06

a003001001

GSL04

研發一部西安分部

GS06

a003001002

4 給定數據集

在生成樹過程中,當從“研發部”(a003)往下找子節點時,找到的應該是“研發二部”(a003002),因爲它是最近的節點。而下面的順序就是沿着“研發二部”再往下找,顯然不可能找到“研發一部杭州分部”和“研發一部西安分部”,因爲編號規則不一樣,這樣生成的樹同樣會漏掉節點。

我們提出一種新的算法,即打破傳統的遍歷順序,採用由底向上的遍歷順序。形象地說,傳統的方法是通過一個既有根節點或父節點來不斷衍生新的子節點(如圖3(a)所示),而新的算法是通過不斷聚集節點,形成子樹集,最後匯成一棵樹(如圖3(b)所示)。


3 TreeView節點生成流程示意圖

 

算法2:由底向上按層次(深度)遍歷法生成樹算法

l         Step 0:數據準備,給定數據集(TagValue,ContentValue,DepthID), TagValue是節點的實際值,ContentValue是節點顯示的值或者說標籤值,DepthID是節點的深度編號。若節點爲根節點,一般其DepthID長度爲最短。給定最大深度iMaxDepLen和最小深度iMinDepLen。給定用於存儲當前子樹的Hashtable;

l         Step 1:給定當前遍歷的層級長度iCurDepLen,初始設爲iMaxDepLen;

l         Step 2:在數據集中根據給定iCurDepLen查找滿足條件的層級,得到該層級的節點集objArr[]
if (objArr == null)
   return;
else
{
  //
遍歷節點集
  for(int i=0; i<objArr.Length;i++)
  {
   Step 2.1
查找objArr[i]的父節點,若無父節點,直接加入,goto Step 2.2;若有父節點,先查找父節點是否已在Hashtable中。若有,將其從Hashtable中移出並記爲tempParNode;否則生成新節點tempParNodegoto Step 2.3
   Step 2.2
若當前節點objArr[i]不在Hashtable中,在Hashtable中添加objArr[i]continue;
   Step 2.3
若當前節點objArr[i]不在Hashtable中,根據objArr[i]生成節點tempNode;否則,將其從Hashtable中移出,並記爲tempNode;將tempNode插到tempParNode中,並將存入Hashtable

  }
}

l         Step 3:若當前層級iCurDepLen大於最小層級iMinDepLen,則繼續回溯,將iCurDepLen1並作爲當前iCurDepLengoto Step 2;否則goto Step 4.

l         Step 4:在得到用Hashtable存儲的節點表後(實際上是一子樹表),遍歷Hashtable,將各棵子樹插入TreeView.

 

在該算法中,我們一開始便計算好數據集中節點深度編號的最小長度和最大長度,目的是爲了不盲目搜索。但如果數據集中每一層級的深度編號是固定長的,則可以更簡化搜索過程。存放臨時子樹的Hashtable的鍵值是當前子樹根節點的Tag值,這樣的好處是查找相當方便,不需要在TreeView中遍歷一個個節點。所以,每次處理上一層級的節點,只需看其父節點在不在Hashtable中,若在將其插入子樹,否則增加Hashtable項。

附錄示例程序實現了這一算法,這裏介紹一下關鍵的幾個函數。

函數形式及其參數解釋

功能

PopulateCompleteTree(ref System.Windows.Forms.TreeView objTreeView,DataSet dsSource,string strTreeCaption,int iTagIndex,int iContentIndex,int iDepthIndex)

1.        objTreeView是最終要生成的TreeView

2.        dsSource是給定數據集;

3.        strTreeCaptionTreeView根節點的名稱;

4.        iTagIndex是數據集中TagValue字段的列號;

5.        iContentIndex是數據集中ContentValue字段的列號;

6.        iDepthIndex是數據集中DepthID字段的列號;

1.        採用層次(深度)遍歷法生成樹主調函數;

2.        調用CollectNodes(DataSet dsSource,int iTagIndex,int iContentIndex,int iDepthIndex,int iCurDepLen,int iMinDepLen,ref Hashtable objArrNode)

CollectNodes(DataSet dsSource,int iTagIndex,int iContentIndex,int iDepthIndex,int iCurDepLen,int iMinDepLen,ref Hashtable objArrNode)

1.        dsSourceiTagIndexiContentIndexiDepthIndex同上;

2.        iCurDepLen指當前層級深度編號長度;

3.        iMinDepLen指最小深度即最頂層深度編號長度;

4.        objArrNode指用於存放中間子樹的Hashtable

1.        從底往上聚集節點;

2.        調用  LookupParentNode(DataSet dsSource,int iDepthIndex,string strSubDepth,int iTagIndex,int iContentIndex)

LookupParentNode(DataSet dsSource,int iDepthIndex,string strSubDepth,int iTagIndex,int iContentIndex)

1.      dsSourceiTagIndexiContentIndexiDepthIndex同上;

2.      strSubDepth指當前節點的深度編號(因爲是遞歸查找)

1.        查找最近的上控層級,因爲有可能父節點層級不存在。

 

 

此時若給定數據集(我們把“研發部”和“行銷一部”過濾掉),

TagValue

ContentValue

ParentID

DepthID

G01

行銷部

 

a001

G02

顧問部

 

a002

G04

測試部

 

a004

GS02

行銷二部

G01

a001002

GS03

行銷三部

G01

a001003

GSL01

行銷一部北京辦

GS01

a001001001

GSL02

行銷一部上海辦

GS01

a001001002

GS04

顧問一部

G02

a002001

GS05

顧問二部

G02

a002002

GS07

研發二部

G03

a003002

GS08

測試一部

G04

a004001

GS09

測試二部

G04

a004002

GSL03

研發一部杭州分部

GS06

a003001001

GSL04

研發一部西安分部

GS06

a003001002

5 給定數據集

則生成樹如下圖所示,


4 TreeView效果圖

這正是我們需要的結果。

當然,有時爲了結構的需要,我們還會採取所謂“中立”的方法。比如對於本文所提的TreeView控件節點生成問題,如果不想再寫算法去生成深度編號,那麼我們還可以通過給數據集增加標誌位的方法,即用標誌位來標識數據是否已被篩選。在運用傳統算法生成樹後,再檢查一下是否有未被篩選的數據,若有則查找其祖輩節點,將其插入祖輩節點。不過這裏的“查找祖輩節點”是在TreeView上進行的,當節點很多時其效率肯定沒有直接在數據集上搜索高。

另外,深度編號的引入不僅會給生成樹帶來方便,還可以讓權限設置更靈活。具體到我們的示例來說,一般如果我們要把某些部門過濾掉,那麼會把這些部門一個一個挑出來,我們稱之爲“離散值設置方式”。而當系統結構龐大時,我們更希望挑選一個區間,比如把一個部門及其下控的n級過濾掉,這是一個“連續值設置方式”,這時包含層級概念的深度編號可以很好地解決這個問題。實際的系統開發中,我們也發現採用這種方式是切實可行的。

 

四、其他TreeView生成方式

前面提到TreeView還可以通過XML文件()生成。這種方式實現的關鍵是構造出一個類似於TreeViewXML文檔或字符串出來。其基本思想應該與前面討論的算法是相似的,只是在程序實現上稍微複雜一些(其中,XML節點的索引可以基於文檔對象模型(DOM)來做)。另外還要注意的是,有很多的第三方TreeView控件,他們所支持的XML文檔的格式是不盡相同的。限於篇幅,本文不詳細討論具體實現過程。

五、小結

       本文主要討論了.NET平臺下TreeView控件節點生成程序設計,結合已有方法和實際需求,對設計方法進行了研究,給出了比較完整的解決方法。

在樹的具體應用中,除了生成樹之外,節點的增、刪、改、查甚至節點的升級和降級都是很常見的。本質上說,這些操作所涉及的是與業務相關的數據庫操作,所以在採用“由底向上按層次(深度)遍歷法”生成的TreeView中,這些操作的實現與傳統方法是一致的,額外的操作無非是添加或修改深度編號。當然,實際需求是變化多端的,相應算法的設計與分析也是無止境的。

 

參考文獻(Reference:

[1] Zane Thomas. DataViewTree for Windows Formshttp://www.abderaware.com/WhitePapers/ datatreeview.htm

[2] 李洪根. 樹形結構在開發中的應用, http://www.microsoft.com/china/community/Column/ 21.mspx

[3] 李洪根. .NET平臺下Web樹形結構程序設計, http://www.microsoft.com/china/community/ Column/30.mspx

[4] Don Schlichting. Populating the TreeView Control from a Database, http://www.15seconds. com/issue/030827.htm

[5] HOW TO: Populate a TreeView Control from a Dataset in Visual Basic .NET, http://support. microsoft.com/?kbid=320755

[6] Scott Mitchell. Displaying XML Data in the Internet Explorer TreeView Controlhttp://aspnet. 4guysfromrolla.com/articles/051403-1.aspx

 


-------------
source code:

using System;
using System.Data;
using System.Windows.Forms;
using System.Collections;


namespace PopTreeView
{
 /// <summary>
 /// TreeOperator 的摘要說明。
 /// </summary>
 public class TreeOperator
 {
  public TreeOperator()
  {
   //
   // TODO: 在此處添加構造函數邏輯
   //
  }


  /// <summary>
  /// 採用層次(深度)遍歷法生成樹
  /// </summary>
  /// <param name="objTreeView">目標樹</param>
  /// <param name="dsSource">數據集</param>
  /// <param name="strTreeCaption">樹顯示名</param>
  /// <param name="iTagIndex">值索引</param>
  /// <param name="iContentIndex">內容索引</param>
  /// <param name="iDepthIndex">層次索引</param>
  public static void PopulateCompleteTree(ref System.Windows.Forms.TreeView objTreeView,DataSet dsSource,string strTreeCaption,int iTagIndex,int iContentIndex,int iDepthIndex)
  {
   //從底層開始遍歷,開闢一個HashTable(以Tag值爲關鍵字),存放當前計算的節點
   objTreeView.Nodes.Clear();
   int iMaxLen = GetMaxDepthLen(dsSource,iDepthIndex);
   int iMinLen = GetTopDepthLen(dsSource,iDepthIndex);
   Hashtable objArrNode = new Hashtable();
   CollectNodes(dsSource,iTagIndex,iContentIndex,iDepthIndex,iMaxLen,iMinLen,ref objArrNode);

   TreeNode objRootNode = new TreeNode(strTreeCaption);
   
   //在得到節點表後,插入樹
   foreach(object objNode in objArrNode.Values)
   {
    TreeNode objNewNode = new TreeNode();
    objNewNode = (TreeNode)objNode;
    objRootNode.Nodes.Add(objNewNode);
   }

   objTreeView.Nodes.Add(objRootNode);
  }

  
  /// <summary>
  /// 從底往上聚集節點
  /// </summary>
  /// <param name="dsSource"></param>
  /// <param name="iTagIndex"></param>
  /// <param name="iContentIndex"></param>
  /// <param name="iDepthIndex"></param>
  /// <param name="iCurDepLen"></param>
  /// <param name="iMinDepLen"></param>
  /// <param name="objArrNode"></param>
  private static void CollectNodes(DataSet dsSource,int iTagIndex,int iContentIndex,int iDepthIndex,int iCurDepLen,int iMinDepLen,ref Hashtable objArrNode)
  {
   //收集節點
   System.Data.DataView dv;
   System.Windows.Forms.TreeNode tempNode;
   
   //查找給定層節點
   int i=iCurDepLen;
   do
   {
    dv = new DataView(dsSource.Tables[0]);
    string strExpr = "LEN(TRIM("+dsSource.Tables[0].Columns[iDepthIndex].ColumnName+"))="+Convert.ToString(i);
    dv.RowFilter = strExpr;
    i--;
   }while(i>=iMinDepLen && dv.Count<=0);
   iCurDepLen = i+1;

   #region 逐層回溯,收集節點
   foreach(System.Data.DataRowView drow in dv)
   {
    //查找父節點
    string[] strArrParentInfo = LookupParentNode(dsSource,iDepthIndex,drow[iDepthIndex].ToString().Trim(),iTagIndex,iContentIndex);
    string strTagValue = drow[iTagIndex].ToString().Trim();
    string strContentValue = drow[iContentIndex].ToString();

    //若無父節點,直接加入
    if (strArrParentInfo == null)
    {
     //當前節點不在Hashtable中
     if (objArrNode[strTagValue]==null)
     {
      //添加當前節點
      tempNode = new TreeNode(strContentValue);
      tempNode.Tag = strTagValue;
      objArrNode.Add(strTagValue,tempNode);
     }
    }
    else //有父節點,此時先查找父節點是否已在Hashtable中
    {
     string strParTagValue = strArrParentInfo[0].Trim();
     string strParContentValue = strArrParentInfo[1].Trim();

     //父節點已在Hashtable中
     if (objArrNode[strParTagValue]!= null)
     {
      //當前節點不在Hashtable中
      if (objArrNode[strTagValue]==null)
      {
       tempNode = new TreeNode(strContentValue);
       tempNode.Tag = strTagValue;
      }
      else
      {
       //取出並移除該節點,然後插入父節點
       tempNode = new TreeNode();
       tempNode =(TreeNode)objArrNode[strTagValue];
       objArrNode.Remove(strTagValue);
      }

      //插入到父節點中
      TreeNode tempParNode = new TreeNode();
      tempParNode = (TreeNode)objArrNode[strParTagValue];
      tempParNode.Nodes.Add(tempNode);
      objArrNode[strParTagValue] = tempParNode;
     }
     else //父節點不在Hashtable中
     {
      //當前節點不在Hashtable中
      if (objArrNode[strTagValue]==null)
      {
       tempNode = new TreeNode(strContentValue);
       tempNode.Tag = strTagValue;
      }
      else
      {
       //取出並移除該節點,然後插入父節點
       tempNode = new TreeNode();
       tempNode = (TreeNode)objArrNode[strTagValue];
       objArrNode.Remove(strTagValue);
      }

      //創建父節點並將當前節點插入到父節點中
      TreeNode tempParNode = new TreeNode(strParContentValue);
      tempParNode.Tag = strParTagValue;
      tempParNode.Nodes.Add(tempNode);
      objArrNode.Add(strParTagValue,tempParNode);
     }
    }
   }
   #endregion
   
   //還有未遍歷的層
   if (iCurDepLen>iMinDepLen)
   {
    CollectNodes(dsSource,iTagIndex,iContentIndex,iDepthIndex,iCurDepLen-1,iMinDepLen,ref objArrNode);
   }
   
  }


  /// <summary>
  /// 查找父親節點
  /// </summary>
  /// <param name="dsSource"></param>
  /// <param name="iDepthIndex"></param>
  /// <param name="strSubDepth"></param>
  /// <param name="iTagIndex"></param>
  /// <param name="iContentIndex"></param>
  /// <returns>找到返回由Tag值,內容值和深度值組成的字符串數組,否則返回null</returns>
  private static string[] LookupParentNode(DataSet dsSource,int iDepthIndex,string strSubDepth,int iTagIndex,int iContentIndex)
  {
   System.Data.DataView  dv;
   int iSubLen = strSubDepth.Length;
   
   if (iSubLen<=1)
   {
    return null;
   }
   
   int i=1;
   do
   {
    dv = new DataView(dsSource.Tables[0]);
    string strExpr ="TRIM("+dsSource.Tables[0].Columns[iDepthIndex].ColumnName+") = '"+strSubDepth.Substring(0,iSubLen-i)+"'";
    dv.RowFilter = strExpr;
    i++;
   }while(i<iSubLen && dv.Count<=0);

   if (dv.Count<=0)
   {
    return null;
   }
   else
   {
    string[] strArr = {dv[0][iTagIndex].ToString(),dv[0][iContentIndex].ToString(),dv[0][iDepthIndex].ToString()};
    return strArr;
   }
  }

  
  /// <summary>
  /// 得到最大深度值(深度的長度)
  /// </summary>
  /// <param name="dsSource">數據集</param>
  /// <param name="iDepthIndex">深度索引(列號)</param>
  /// <returns>最大深度值</returns>
  private static int GetMaxDepthLen(DataSet dsSource,int iDepthIndex)
  {
   DataRowCollection objRowCol = dsSource.Tables[0].Rows;
   int iMax = objRowCol[0][iDepthIndex].ToString().Trim().Length;
   
   foreach(DataRow objRow in objRowCol)
   {
    int iCurlen = objRow[iDepthIndex].ToString().Trim().Length;
    if (iMax<iCurlen)
    {
     iMax = iCurlen;
    }
   }

   return iMax;
  }


  /// <summary>
  /// 得到最小深度值(深度的長度)
  /// </summary>
  /// <param name="dsSource">數據集</param>
  /// <param name="iDepthIndex">深度索引(列號)</param>
  /// <returns>最小深度值</returns>
  private static int GetTopDepthLen(DataSet dsSource,int iDepthIndex)
  {
   DataRowCollection objRowCol = dsSource.Tables[0].Rows;
   int iMin = objRowCol[0][iDepthIndex].ToString().Trim().Length;
   
   foreach(DataRow objRow in objRowCol)
   {
    int iCurlen = objRow[iDepthIndex].ToString().Trim().Length;
    if (iMin>iCurlen)
    {
     iMin = iCurlen;
    }
   }

   return iMin;
  }


 }
}


-----------
memo: 本文最初我想在刊物上發表的,篇幅很長,這裏有所刪節。歡迎指正、交流!


相關文章
對該文的評論
CSDN 網友 ( 2005-01-05)
可以定義一個節點對象類,然後用TreeNode[] 構造一個樹對象,這樣可能會更清晰一點
-----------------------------------
public class TreeNode
{
protected bool isLeaf     = false; //是否爲葉子節點
protected int nodeID      = -1;    //節點ID
protected int nodeDeep    = -1;     //節點深度
protected int nodeOrder   = -1;    //節點排序(可選屬性)
protected int seekTimes   = 0;     //該節點被遍歷次數
protected string nodeName = "unNamedNode"; //節點名稱

//節點的父節點,root-Node's Parent Node is NULL
protected TreeNode nodeParent = null;

//.... other treeNode properties description   

//constructor
public TreeNode(...)
{//...}

//..
}

public class Tree
{
  protected TreeNode[]  tnodes;
  //...

  //constructor
  public Tree(...)
 {//...}
}
CSDN 網友 ( 2004-12-23)
請問同級樹節點,如果需要排序怎麼辦呢,你的這樹就沒法實現了吧
CSDN 網友 ( 2004-12-22)
我這裏是遞歸做的,在DOWN數據的時候,沒有權限的,就不DOWN下來!!
newcardmm ( 2004-12-20)
聯繫電話:010-82645151 詳情參見:http://www.f c s o f t.com.cn
什麼是eform開發平臺? 
     eform是基於瀏覽器的表單自定義工具,eform是頁面設計工具,eform內含大量構件.不用寫一行代碼便能用eform開發出來常見的功能點. 
 
 使用eForm平臺有如下好處: 
     1、用eform平臺開發能降低開發人員的技術門檻,使很低水平的人就能開發一個軟件項目中常見的功能.例如數據庫的數據增刪改查打印等等,而這部分功能往往也佔居了一個軟件項目的大部分.這樣一個軟件項目開發成員中可以有一大部分人是中專生甚至是高中生就能勝任.從而大大降低了整個軟件項目的開發成本.另一方面因爲低水平的開發人員很容易招聘到,這樣也使軟件項目更加容易完成. 
 
     2、用eform平臺開發的代碼一致性比較好,以後維護升級方便.因爲只有個性化的功能才需要編寫事件代碼.所以代碼量很少,大量的調用底層的代碼,這樣代碼的集成度高.以後維護升級時修改的代碼量非常少. 
 
 
     3、用eform平臺開發能大大提高開發效率.eform平臺採用對常見的功能和控件內置的方法,使得開發一些常見的功能(如數據庫的增刪改查,樹控件,表格控件)非常容易方便.幾乎不用寫一行代碼.直接通過控件的拖拉然後再設置屬性和事件即可完成.開發程序的工作就象是打字員的工作一樣.(如圖所示開發效率對比示意圖)  
 
     4、用eform平臺開發能很好地應對軟件開發項目成員的流動的問題.因爲程序員的離職而造成整個項目癱瘓的事例很多.而用eform平臺,因爲大家都是採用同一模式開發的表單,因而一個人開發的表單很容易被另一個人看懂和使用.這樣就使開發人員的流動造成的影響大大降低.企業不再受制於人. 
     5、用eform平臺開發可以使項目不再沒完沒了,無法關閉.因爲可以培訓最終用戶中的精英,讓他們掌握eform平臺的使用方法,這樣大多需求他們便可以自己做好,而不用麻煩軟件開發商了. 
 
     eform的設計思路是將數據庫程序開發中常用的控制或功能點在eform平臺中設計好,通過簡單的設置參數或屬性即可調用.而遇到很個性化的功能點則可以用傳統的代碼方式進行開發.因爲一個數據庫程序開發中大量是增,刪,改,查,打印,報表,圖表,數據校驗等常見的功能點,而這些功能點在eform平臺中都做好了,只要簡單地設置一下即可完成這些功能點,而且這個設置過程也是可視化的,有相應的設置界面.這樣做這些常見的功能點就非常簡單快速.而少量的特別的功能點又可通過寫代碼的方式來完成.也就是說在一張表單中可以一部分功能是直接通過簡單的設置一下來完成,另一部分功能是用代碼來硬寫出來的.這樣就達到了常見的功能可以直接調用eform底層的api來實現以提高開發效率,但一個表單又不限定只能實現這些常見功能,你也可隨意地用代碼來進行無限擴充.這樣就達到了既提高了開發效率又能實現很複雜的功能. 
      eform開發平臺分爲eform.j2ee和eform.net兩個版本.eform.j2ee是用java編寫的,面向j2ee應用.eform.net是用.net編寫的,面向.net應用.實際上整個eform開發平臺共有三部分的代碼,① 一部分是htc js dhtml等前臺的代碼,② 一部分是java的代碼,③ 一部分是.net的代碼(c#語言的),其中java的代碼完成的功能和.net的代碼完成的功能完全相同.用①和②就組成了eform.j2ee版本,用① 和③ 就組成了eform.net.這樣就得到了兩個版本.由此可知,eform.j2ee和eform.net的接口和操作是完全相同的.只是運行環境和使用的編程語句不同罷了.這樣做的好處是當需要從j2ee平臺轉到.net平臺或是從.net平臺轉到j2ee的平臺時,使用eform編寫的表單和程序可以完全保留下來直接使用.可以輕鬆地跨越當今兩大主流的開發平臺. 
     使用eform開發平臺開發出來的表單可以直接在瀏覽器中運行,不但如此,而且其設計工具也是在瀏覽器中運行的.也就是說,開發人員也是在IE中(拖拉控件)開發的.開發人員再也不用爲了搭建開發環境而裝一大堆軟件了,這一點對於遠程協作開發非常有利. 
     eform內置了常見的大量的開發構件,如樹控件,表格,圖表控件,打印控件,上傳控件,查詢等,也內置了象單表輸入,一對多表輸入等常見的數據庫程序的功能點.通過使用這些可以大大提高開發的速度,降低開發這些常見功能的門檻,只需知道很少的知識便可以開發.使用eform生成的表單結構和格式一致,非常便於以後的維護升級. 
      eform開發平臺開發出來的表單可以脫離eform平臺單獨運行,也很容易和其它程序進行集成.一個項目的程序往往是大量常用功能用eform平臺開發,而少量功能用其它方式開發.然後把它們集成在一起而成的. 
     eform開發平臺是專門爲軟件開發商或需要開發數據庫程序的人而設計的.它採用開放版權的銷售方式.對於用戶開放100%的源代碼,也就是說將eform開發平臺的源代碼全部提供給用戶,同時還包括相應的開發文檔和典型示例都提供給用戶,而且用戶用eform開發平臺開發出來的程序可以自由分發.用戶購買了eform後,就相當於eform是自己開發出來的一樣.而且北京方成公司還提供一年的免費服務和技術支持. 
     eform的銷售沒有任何加密和license之說.是一種特別的銷售方式.銷售的過程實際上是完成知識和價值的轉移的過程.相當於方成公司幫用戶開發了一個平臺然後再幫助用戶把它使用起來,用戶使用eform開發的軟件可以自由銷售,和方成公司沒有任何關係,更不需要再收費用.由此可見,購買eform和自已招聘員工開發一個平臺相比,無論是時間還是費用以及風險都是購買eform比較合算. 
 

聯繫電話:010-82645151 詳情參見:http://www.9job.info
針對各類求職者,我們將用數碼攝像機將您的自我介紹、特長展示、工作實況、生活場景等拍攝下來,製成具有專業水平的短片,並配有文字說明、音樂等。充分展示您的學識、技能、才藝、素質,使您的個人簡歷帶有強烈的個人色彩及吸引力,讓您在衆多求職者中脫穎而出。
 
   優惠期間:我們將免費爲您拍攝,爲期一個月。 

tomtop ( 2004-12-16)
文章真是不錯!講解得很清楚!加入對二叉樹的原理解釋就更佳!

算法實現部分的代碼可以優化!採用遞歸實現遍歷節點!
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章