国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

【小技巧】如何判斷樹形結構產生循環

2019-11-14 16:19:22
字體:
來源:轉載
供稿:網友

在呈現層級數據為一個樹形視圖(TreeView)的時候,經常會遇到一個問題,就是要判斷這些層級數據會不會造成循環,不然在構造樹形的時候會出現堆棧溢出(StackoverflowException)的錯誤。

那么如何判斷是否循環呢?尤其在保存層級數據是通過父節點Id的遞歸方式來保存的情況下(保存層級數據還有一種方式就是層級化的Id)。兩種保存方式都必須要求每個節點數據都具有唯一的Id。

之前自己寫過一種簡單的判斷方法,昨天又要重新實現類似算法(且數據結構不太一樣),就打算看看網絡上有沒有一種更好的方式。結果搜索后,居然看到假設樹形層級的一個限值,如果超過限值就停止構造。這樣的解決辦法看著也是醉了。

下面簡單介紹一下我用過的兩種解決辦法。

第一種最簡單的方式就是每次新增子節點都判斷要新增的節點的Id是否出現在當前節點的父節點鏈上。即檢查當前節點的父祖節點的Id是否包含打算要添加節點的Id(且一直檢查到根節點,檢查的具體算法可以使用循環或者遞歸的方式)。但是這種方式要求構建數據模型的時候,必須包含一個Parent的屬性。(具體代碼就省略了……,也懶得翻之前的代碼)。(2014-11-25 23:52:46更新:還是補充點代碼,見下)

public interface IHasparent<T>{    T GetParent();}public interface IHasId<T>{    T GetId();}/// <summary>/// 在自己和所有父輩層級中是否包含此Id(用于判斷是否循環)/// </summary>/// <typeparam name="TData">實現了IHasParent和IHasId的對象</typeparam>/// <typeparam name="TId">Id的類型</typeparam>/// <param name="self">當前對象</param>/// <param name="id">需判斷的Id</param>/// <returns></returns>public static bool ExistInAncestry<TData, TId>(this TData self, TId id)    where TData : class, IHasParent<TData>, IHasId<TId>{    var current = self;    while (current != null)    {        if (Equals(current.GetId(), id)) return true;        current = current.GetParent();    }    return false;}

如果沒有Parent屬性的情況下,又要如何判斷呢?就是昨天我使用的第二種方式:構造一個輔助字典集合來記錄所有樹形分支上的所有Id,然后判斷這些Id是否有重復。下面的代碼片段基本可以解釋這種用法:

public List<PartNode> GenerateTree(){    var nodes = new List<PartNode>();        //創建循環判斷字典,Key是所有層級上的節點索引構造的字符串,Value是這個分支上所有的節點Id(本例為Code)    var cycleCheckerDict = new Dictionary<string, HashSet<string>>();    cycleCheckerDict.Add("-1", new HashSet<string>(new[] { Parts[0].Code }));        AddNodes(Parts[0], nodes, 1, cycleCheckerDict, "-1");    return nodes;}private void AddNodes(PartNodeInfo parent, List<PartNode> nodes, double parentQuantity, Dictionary<string, HashSet<string>> cycleCheckerDict, string parentCycleCheckerKey){    int nodeIndex = -1;        var groupsByCode = from part in Parts                       where part.ParentCode == parent.Code && !part.IsOutsourcingSubstitute                       group part by part.Code;    foreach (var group in groupsByCode)    {        var part = group.ToList()[0];        var node = new PartNode        {            Amount = Setting.IsUnitAmount ? part.Quantity : group.Sum(o => o.Quantity) / parentQuantity,            Code = part.Code,            Description = part.Description,            Name = part.Name,            Specification = part.Specification,            Unit = UnitMappings[part.Unit],            Weight = part.Weight,            WeightUnit = part.WeightUnit        };        nodes.Add(node);        nodeIndex++;         //判斷是否循環        var parentCycleCheckerIdChain = cycleCheckerDict[parentCycleCheckerKey];        if (parentCycleCheckerIdChain.Contains(part.Code))        {            ErrorLog.AppendLine(string.Format("零部件 {0}({1}) 會造成樹形循環",                part.Name, part.Code));            continue;        }        var currentCycleCheckerKey = parentCycleCheckerKey + "," + nodeIndex;        var currentCycleCheckerIdChain = new HashSet<string>(parentCycleCheckerIdChain);        currentCycleCheckerIdChain.Add(part.Code);        cycleCheckerDict.Add(currentCycleCheckerKey, currentCycleCheckerIdChain);        //添加下級零部件        node.Nodes = new List<PartNode>();        AddNodes(part, node.Nodes, correctQuantity, cycleCheckerDict, currentCycleCheckerKey);    }    cycleCheckerDict.Remove(parentCycleCheckerKey);//刪除遺留Id記錄鏈}

當然,第一種方法要比第二種方法簡便(甚至效率也可能會更好,我沒有實際測試,也沒有計算算法復雜度),且第一種方法易于封裝為通用的函數。

最后留一個問題,如何在樹形上顯示循環的數據?


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 双鸭山市| 石台县| 昌吉市| 敦化市| 海兴县| 湟中县| 五寨县| 高雄县| 明水县| 石首市| 桂东县| 宽甸| 罗平县| 台江县| 土默特左旗| 白河县| 常熟市| 潜山县| 阳泉市| 黄龙县| 苏尼特右旗| 澎湖县| 上栗县| 兴文县| 曲周县| 南和县| 襄垣县| 乌兰察布市| 屯门区| 鞍山市| 长寿区| 偃师市| 锡林郭勒盟| 商丘市| 阳曲县| 阳泉市| 芮城县| 嘉善县| 黄梅县| 阿荣旗| 三台县|