void PrintPathInfoNode(NAryNode root) { if (root == null) { return; } NAryNode current = null; var remainings = new Stack <NAryNode>(new[] { root }); while (remainings.Any()) { current = remainings.Pop(); Console.WriteLine(GetName(current)); if (current.HasChildren) { foreach (NAryNode child in current.Children) { remainings.Push(child); } } } }
void TraverseNAryTree <T>(NAryNode <T> node, Action <NAryNode <T> > action) { if (node == null) { return; } NAryNode <T> current = null; var remainings = new Stack <NAryNode <T> >(new[] { node }); while (remainings.Any()) { current = remainings.Pop(); action.Invoke(current); if (current.HasChildren) { foreach (NAryNode <T> child in current.Children) { remainings.Push(child); } } } }
public void AddChild(NAryNode node) { if (!Children.Contains(node)) { Children.Add(node); } }
public void Run() { String input = GetDataPath("file-1.txt"); Dbg("GetPathInfo on File", GetPathInfo(input)); input = DataDirPath; Dbg("GetPathInfo on Directory", GetPathInfo(input)); Dbg("PrintDirContents", String.Empty); PrintDirContents(input); NAryNode pathInfoNode = GetPathInfoNode(input); Dbg("GetPathInfoNode without recursion", pathInfoNode); PrintPathInfoNode(pathInfoNode); }
NAryNode GetPathInfoNode(String path) { if (String.IsNullOrEmpty(path)) { return(null); } NAryNode root = ConvertPathToNAryNode(path, 0); var remainings = new Stack <NAryNode>(new[] { root }); while (remainings.Any()) { NAryNode current = remainings.Pop(); if (current.Children == null) { var content = current.Content as PathDir; IEnumerable <String> children = GetChildren(content.Path); current.HasChildren = children != null && children.Any(); if (!current.HasChildren) { current.Children = null; } else { current.Children = new List <NAryNode>(); foreach (String child in children) { NAryNode childNode = ConvertPathToNAryNode(child, current.Depth + 1); remainings.Push(childNode); current.Children.Add(childNode); } } } } return(root); }
String GetName(NAryNode item) => (item.Depth > 0 ? "|__".PadLeft(item.Depth * 3, ' ') : String.Empty) + (item.Content as PathDir).Name;
public static int MinCoverSet(NAryNode root) { // 如果这棵树只有孤零零的根节点,那只需要覆盖这个节点就行了。 if (root.Children == null || root.Children.Count == 0) { return(1); } // 查找节点的父节点用。 var parentDict = new Dictionary <NAryNode, NAryNode>(); // 判断节点是否为叶节点用。 var isLeafDict = new Dictionary <NAryNode, bool>(); var q = new Queue <NAryNode>(); q.Enqueue(root); // 首先遍历这棵树,填充 parentDict 和 isLeafDict。 // 非递归 BFS 我就不用解释了吧。 while (q.Count > 0) { var node = q.Dequeue(); if (node.Children != null && node.Children.Count > 0) { isLeafDict[node] = false; foreach (var child in node.Children) { parentDict[child] = node; q.Enqueue(child); } } else { isLeafDict[node] = true; } } q.Clear(); // 第一步,将所有的叶节点的父节点添加到待处理队列。 foreach (var leaf in isLeafDict.Where(kv => kv.Value).Select(kv => kv.Key)) { if (parentDict.ContainsKey(leaf)) { q.Enqueue(parentDict[leaf]); } } var selected = new List <NAryNode>(); // 然后将这些节点放入预选列表,同时将它们的的爷爷节点加入待处理队列。 while (q.Count > 0) { var node = q.Dequeue(); if (!selected.Contains(node)) { selected.Add(node); } if (parentDict.ContainsKey(node)) { var parent = parentDict[node]; if (parentDict.ContainsKey(parent)) { q.Enqueue(parentDict[parent]); } } } // 这个数组的作用是防止枚举时更改集合(selected)内容导致抛出 InvalidOperationException。 var tmpArray = selected.ToArray(); // 简化预选列表,删除所有符合以下两条的节点: // 1. 其父节点在列表中; // 2. 其所有子节点在列表中。 foreach (var node in tmpArray) { bool isParentInList; bool areAllChildrenInList; if (parentDict.ContainsKey(node)) { var parent = parentDict[node]; isParentInList = selected.Contains(parent); } else { // root isParentInList = true; } if (node.Children != null && node.Children.Count > 0) { // 你看 LINQ 是不是可以极大地简化代码呢? areAllChildrenInList = node.Children.All(child => selected.Contains(child)); } else { // Not likely. throw new ApplicationException(); } if (isParentInList && areAllChildrenInList) { selected.Remove(node); } } // 最后看看还剩下多少咯。 return(selected.Count); }