using System; using System.Collections; using System.Collections.Generic; using System.Linq; namespace Advanced.Algorithms.DataStructures { /// /// A segment tree implementation. /// public class SegmentTree : IEnumerable { private readonly int length; private readonly T[] input; private readonly T[] segmentTree; /// /// The operation function pointer. /// Example operations are Sum, Min, Max etc. /// private readonly Func operation; /// /// Default value to eliminate node during range search. /// Default value for Sum operation is 0. /// Default value for Min operation is Max Value (i.e int.Max if T is int). /// default value for Max operation is Min Value(i.e int.Min if T is int). /// private readonly Func defaultValue; /// /// Constructs a segment tree using the specified operation function. /// Operation function is the criteria for range queries. /// For example operation function can return Max, Min or Sum of the two input elements. /// Default value is the void value that will eliminate a node during operation comparisons. /// For example if operation return min value then the default value will be largest value (int.Max for if T is int). /// Or default value will be 0 if operation is sum. /// Time complexity: O(n). /// public SegmentTree(T[] input, Func operation, Func defaultValue) { if (input == null || operation == null) { throw new ArgumentNullException(); } this.input = input.Clone() as T[]; var maxHeight = Math.Ceiling(Math.Log(input.Length, 2)); var maxTreeNodes = 2 * (int)(Math.Pow(2, maxHeight)) - 1; segmentTree = new T[maxTreeNodes]; this.operation = operation; this.defaultValue = defaultValue; length = input.Length; construct(input, 0, input.Length - 1, 0); } private T construct(T[] input, int left, int right, int currentIndex) { if (left == right) { segmentTree[currentIndex] = input[left]; return segmentTree[currentIndex]; } var midIndex = getMidIndex(left, right); segmentTree[currentIndex] = operation(construct(input, left, midIndex, 2 * currentIndex + 1), construct(input, midIndex + 1, right, 2 * currentIndex + 2)); return segmentTree[currentIndex]; } /// /// Gets the operation aggregated result for given range of the input. /// Time complexity: O(log(n)). /// public T RangeResult(int startIndex, int endIndex) { if (startIndex < 0 || endIndex > length - 1 || endIndex < startIndex) { throw new ArgumentException(); } return getRangeResult(startIndex, endIndex, 0, length - 1, 0); } private T getRangeResult(int start, int end, int left, int right, int currentIndex) { //total overlap so return the value if (left >= start && right <= end) { return segmentTree[currentIndex]; } //no overlap, so return default if (right < start || left > end) { return defaultValue(); } //partial overlap so dig in var midIndex = getMidIndex(left, right); return operation(getRangeResult(start, end, left, midIndex, 2 * currentIndex + 1), getRangeResult(start, end, midIndex + 1, right, 2 * currentIndex + 2)); } private int getMidIndex(int left, int right) { return left + ((right - left) / 2); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public IEnumerator GetEnumerator() { return input.Select(x => x).GetEnumerator(); } } }