/// <summary> /// See https://www.youtube.com/watch?v=sD1IoalFomA /// The graph has to be rooted. Since the algorithm is intended to work for trees I made an adjustment /// such that I process branching nodes only once. /// Otherwise we would process the same sub graphs again and again. /// </summary> public GraphNode FindCommonAncestor(GraphNode node1, GraphNode node2) { if (_preprocessData is null) { _preprocessData = PreprocessLeastCommonAncestor(); } var index1 = _preprocessData.GraphNodeToIndex[node1]; var index2 = _preprocessData.GraphNodeToIndex[node2]; var from = Math.Min(index1, index2); var to = Math.Max(index1, index2); var lcaIndex = -1; var minDepth = int.MaxValue; for (var i = from; i <= to; i++) { if (_preprocessData.Depth[i] < minDepth) { minDepth = _preprocessData.Depth[i]; lcaIndex = i; } } return(_preprocessData.EulerPath[lcaIndex]); }
public void UpdateGraph(string hash, IEnumerable <string> allParents) { lock (_lockObj) { _preprocessData = null; // GraphNode for the given hash. var node = GetOrAddNode(hash); // Update parents and child relationships foreach (var parentHash in allParents) { node.Parents.Add(GetOrAddNode(parentHash)); var parent = GetOrAddNode(parentHash); parent.Children.Add(GetOrAddNode(hash)); } } }
private LeaseCommonAncestorPreprocessData PreprocessLeastCommonAncestor() { void TraverseDepthFirst(LeaseCommonAncestorPreprocessData preprocessData, GraphNode node, int currentDepth = 0) { if (preprocessData.AlreadyProcessed.Add(node) is false) { return; } preprocessData.Record(node, currentDepth); foreach (var child in node.Children) { TraverseDepthFirst(preprocessData, child, currentDepth + 1); preprocessData.Record(node, currentDepth); } } var data = new LeaseCommonAncestorPreprocessData(); var root = AllNodes.Single(node => !node.Parents.Any()); TraverseDepthFirst(data, root); return(data); }