Tip: how to judge the generation cycle of the tree structure and how to judge the Tree Structure
When presenting hierarchical data as a TreeView, we often encounter a problem that is to determine whether these hierarchical data will generate loops, otherwise, a stack overflow (StackoverflowException) error occurs during tree construction.
So how can we determine whether it is a loop? Especially when the hierarchical data is saved recursively by the parent node Id (the hierarchical Id is another way to save the hierarchical data ). Each node must have a unique Id for both storage methods.
I have previously written a simple judgment method. Yesterday I had to re-implement similar algorithms (and the data structure is not the same). I plan to see if there is a better way on the network. After the result is searched, we can see a limit of the tree level. If the limit is exceeded, the construction will be stopped. This solution is also drunk.
The following describes two solutions I have used.
The first simplest way is to determine whether the Id of the node to be added appears on the parent node chain of the current node each time a subnode is added. That is, check whether the Id of the parent node of the current node contains the Id of the node to be added (and always check to the root node. The specific algorithm of the check can be cyclic or recursive ). However, this method requires that the data model must contain a Parent attribute. (The specific code is omitted ......, ). (Updated at 23:52:46 on February 25,: add some code, as shown below)
Public interface IHasParent <T> {T GetParent ();} public interface IHasId <T> {T GetId ();} /// <summary> /// determine whether the Id is contained in the self and all parent levels (used to determine whether the Id is cyclic) /// </summary> /// <typeparam name = "TData"> implements IHasParent and IHasId objects </typeparam> /// <typeparam name = "TId"> id type </typeparam> /// <param name = "self"> current object </param> /// <param name = "id"> Id to be determined </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 ;}
How can we determine if the Parent attribute is not available? This is the second method I used yesterday: Construct an auxiliary dictionary set to record all IDs on all tree branches, and then determine whether these IDs are repeated. The following code snippet can basically explain this usage:
Public List <PartNode> GenerateTree () {var nodes = new List <PartNode> (); // create a circular judgment dictionary. The Key is a string constructed by the node indexes at all levels, value is the Id of all nodes in this branch (Code in this example) 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> nod Es, 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 ++; // judge whether to cycle var parentCycleCheckerIdChain = cycleCheckerDict [parentCycleCheckerKey]; if (parentCycleCheckerIdChain. contains (part. code) {ErrorLog. appendLine (string. format ("parts {0} ({1}) will cause a tree loop", part. name, part. code); continue;} var currentCycleCheckerKey = parentCycleCheckerKey + "," + nodeIndex; var currentCycleCheckerIdChain = new HashSet <string> (parentCycleCheckerIdChain); currentCycleCheckerIdChain. add (part. code); cycleCheckerDict. add (currentCycleCheckerKey, currentCycleCheckerIdChain); // Add a lower-level component node. nodes = new List <PartNode> (); AddNodes (part, node. nodes, correctQuantity, cycleCheckerDict, currentCycleCheckerKey);} cycleCheckerDict. remove (parentCycleCheckerKey); // Delete the legacy Id record chain}
Of course, the first method is simpler than the second method (or even better, I have no actual tests or computational algorithm complexity ), the first method is easy to encapsulate as a common function.
The last question is how to display cyclic data in a tree?