Constant segment of an array 2026-10-31
A contiguous segment whose elements all have the same value. Its longest suffix after scanning a new element extends the previous constant suffix when the adjacent values agree, and otherwise starts again with length one. Keeping the largest suffix length seen gives a linear time longest-segment scan.
Filtering a list by unfolding 2026-10-31
For a predicate , normalize the state to the first remaining passing element with
seek = dropWhile (not . p). Stop on an empty state, otherwise emit its head and continue from seek (tail state). The invariant is that every nonempty state starts with a passing element. An initial seek establishes it; removing the emitted head and seeking again preserves it. Each rejected run is traversed once, giving linear time in the examined input prefix under constant-cost predicate tests.For positive integers , the floor function condition is equivalent to the displayed integer inequalities. Since , a witness satisfies . Finding the least with and checking the strict upper bound therefore decides membership by binary search. Scanning successive values with a persistent increasing witness index instead enumerates all values below a bound in linear time arithmetic-operation count. Exact integer comparison avoids floating-point errors near an integer boundary.
Linear flattening of a binary tree 2026-10-24
For a leaf-labelled binary tree, define
go (One x) acc = x:acc and go (Two l r) acc = go l (go r acc), then apply go t []. The invariant in the title follows by structural induction and associativity of list concatenation. It proves preservation of leaf order. Each leaf supplies one output constructor and each internal node requires constant bookkeeping, giving linear time in the number of nodes. By contrast, flattening both subtrees and concatenating their results can copy prefixes of sizes on a left-branching tree, taking quadratic time. The accumulator is the already-known suffix in a difference list representation. List-based set representation 2026-10-31
Represent a finite set by a list of its elements. Allowing repeated elements makes set union cheap by list concatenation; imposing uniqueness requires membership checks. A representation sorted by a total order and containing no duplicates permits set union and set intersection by a simultaneous scan in linear time. These are different representation invariants for the same abstract set.
Longest bitonic contiguous segment 2026-10-31
A linear time scan can retain the earliest start of the current monotone suffix, the earliest start of the current bitonic suffix, the most recent nonzero direction and the endpoint of its most recent strict step. At a strict direction reversal, the new bitonic suffix begins at the old monotone start; the new monotone suffix begins at the old strict-step endpoint. Zero differences leave these boundaries unchanged. The suffix invariants show that no longer valid suffix is discarded, and recording the largest length gives a global optimum using constant auxiliary storage.
Lower weighted median 2026-10-31
For ordered distinct values with positive weights summing to one, choose the first value whose cumulative weight reaches one half. The preceding cumulative weight is strictly below one half, and the remaining mass is at most one half, proving the displayed conditions. Stopping on equality chooses the lower value when a cumulative weight is exactly one half. Sorting followed by a scan takes time; median of medians partitioning with a residual weight target gives deterministic linear time.
Maximum priority queue Created 2026-10-24 Updated 2026-10-31
A priority queue whose distinguished entry has largest key. A max-heap implements maximum inspection in constant time, and insertion, extraction of a maximum and increasing the key of a located entry in running time. An entry handle needs its current heap index to support that last bound; searching by an arbitrary key can take linear time. Multiple entries can have equal keys without requiring a particular tie order.
Maximum-subarray segment summary 2026-10-24
For a segment, store its total sum , maximum possibly-empty prefix sum , maximum possibly-empty suffix sum and maximum possibly-empty internal contiguous sum . Adjacent segment summaries combine byA prefix either stays on the left or includes the whole left segment and a right prefix. A suffix has the symmetric decomposition. An internal segment either stays in one child or crosses the boundary as a left suffix followed by a right prefix. These cases prove the formulas, including empty candidates. Store the corresponding endpoints to recover a winning segment. This constant-size summary gives a linear time divide-and-conquer algorithm for the empty-allowed maximum subarray problem.
The accounting method assigns each operation a charge, its amortized cost, that can differ from its actual cost. Cheap operations save the excess charge as credits associated with the data structure or its entries. An expensive operation spends these previously saved credits. The essential invariant is that the credit balance never becomes negative.
If the initial balance is zero, operation has actual cost , charge , and leaves balance , thenThus a nonnegative credit balance makes the total charge an upper bound on the total actual cost for every operation sequence. In particular, a constant time charge proves a linear time total. The argument is a deterministic amortized analysis; it requires no probability distribution on the operations. A nonzero initial balance must be included as an additive term in the bound.
Use two permanent stacks, and , and one temporary stack, . At operation boundaries is empty. Maintain the representation invariant that the deque, read from front to back, is the top-to-bottom contents of followed by the bottom-to-top contents of . Thus the tops of and are the two accessible ends. Maintain their sizes using two counters, requiring only additional storage in Big O notation.
Adding at the front pushes onto ; adding at the back pushes onto . Removing at an end whose permanent stack is nonempty simply pops that stack. If both permanent stacks are empty, report an empty deque.
- Move the top elements of onto .
- Move all remaining elements of onto .
- Move all elements of back onto .
- Pop and return the top of .
The parked elements are the back half of the deque; the unparked elements are the front half. Moving the front half onto reverses their stack order and exposes the correct front element. Parking and restoring the back half reverses it twice, preserving its original order in . The temporary stack is empty again. For a back removal with empty, exchange the roles of and throughout. Rebalance approximately half the elements, so both ends retain accessible elements after a large transfer.
For the amortized analysis, count each push or pop as one unit and use the potential method withInitially the deque is empty, so , and is always nonnegative. An ordinary insertion or removal costs one unit and changes the size difference by at most one, giving amortized cost at most .
For a rebalancing removal, let . The parked half makes two transfers per element and the other half makes one; each transfer is a pop and a push. Including the final removal, the actual cost isBefore rebalancing the absolute size difference is . Immediately after the split it is at most , and after the removal it is at most . HenceSize tests and counter updates change only these constants. Summing the amortized costs and using nonnegative final potential proves amortized running time for all four operations, with three stacks and additional storage. An individual rebalancing operation can still take linear time in the current size.
A doubly linked list supports backward traversal and removal of a known linked list node in constant time, because that node supplies its own predecessor. In a singly linked list, removing a known node generally requires finding its predecessor by a forward scan unless the caller already supplies it. Inserting before a known node has the same advantage.
The cost is another pointer per node, more link assignments on every update, and a stronger consistency invariant to maintain. HenceThis does not make locating an arbitrary data value faster: a search still takes linear time in the worst case.
First include the closing edge exactly once:The recursive version and right fold both sum the Euclidean distances between consecutive vertices, including the last-to-first pair. They take linear time. The empty-list convention gives zero, although an empty list is not a valid polygon.
edges :: Polygon -> [(Point,Point)]
edges [] = []
edges ps = zip ps (tail ps ++ [head ps])
perimeter :: Polygon -> Double
perimeter = total . edges
where total [] = 0
total ((p,q):es) = distBetween p q + total es
perimeterFold :: Polygon -> Double
perimeterFold = foldr (\(p,q) s -> distBetween p q + s) 0 . edgesHere “centroid” means the vertex centroid, the arithmetic mean of the listed vertices, rather than the uniform-area centroid of the polygon's interior:The nonempty input restriction prevents division by zero; computing both sums and the length takes linear time.
centroid :: Polygon -> Point
centroid [] = error "centroid: empty vertex list"
centroid ps = (sx/n,sy/n)
where
(sx,sy) = foldr (\(x,y) (u,v) -> (x+u,y+v)) (0,0) ps
n = fromIntegral (length ps)Count each data constructor once, including each
Two:size :: Tree a -> Int
size (One _) = 1
size (Two l r) = 1 + size l + size rStructural induction verifies the count: a leaf contributes one; an internal node contributes itself and the disjoint occurrences in its two subtrees. Fully evaluating the result takes linear time in the number of nodes. This type signature uses machine
Int as usual in the examination; Integer avoids overflow for arbitrarily large inputs.Maintain a residual cumulative-weight target , initially , while discarding whole partitions. Choose pivots by an ordinary median of the current entries, not by their weights:
selectWeighted(S, t):
if |S| == 1: return the sole record of S
p = deterministic unweighted median by value in S
L = records with value < p.value
R = records with value > p.value
ell = sum of weights in L
if t <= ell:
return selectWeighted(L, t)
if t <= ell + p.weight:
return p
return selectWeighted(R, t - ell - p.weight)
answer = selectWeighted(all records, 1/2)The invariant is , with the desired answer the first cumulative weight reaching inside the current ordered subset. The first branch uses
<=, which is essential: if the target equals the total left mass, the answer is the largest appropriate left-side value, not the pivot. The other branches preserve the residual target by subtracting exactly the mass discarded on the left.A median of medians routine finds the exact unweighted median in deterministic linear time. Partitioning and summing also take linear time, and each recursive call retains at most entries. Therefore . The algorithm is worst-case linear, not merely expected linear under a random pivot. Exact comparisons to the weight threshold are assumed; floating-point rounding at exactly one half would need explicit treatment.
The following shared step consumes exactly one input element per recursive call of The helper's input consists of nonempty runs. If its first run starts with
group1:prependRun :: Eq a => a -> [[a]] -> [[a]]
prependRun x xss = (x:same):rest
where
~(same,rest) = case xss of
[] -> ([],[])
run@(y:_):runs
| x == y -> (run,runs)
| otherwise -> ([],xss)
group1 :: Eq a => [a] -> [[a]]
group1 [] = []
group1 (x:xs) = prependRun x (group1 xs)x, prepend x to that run; otherwise create a new singleton run. Structural induction proves that these are the maximal equal runs in order. The irrefutable pair binding ~(same,rest) preserves lazy evaluation: the first output group exposes its initial x without inspecting the recursively grouped tail. Thus even an infinite constant input can expose the first group and each finite prefix of that group. Finite inputs take linear time when equality tests take constant time.A compatible
Ord instance supplies a total order, so use comparison sort to put equal values together in time and then group adjacent equals in linear time. To preserve the first-occurrence group ordering of part (f), first tag every element with its original index and stable sorting by value. Group the sorted records by value; stability preserves their internal input order. Each group's first record carries its smallest original index. Sort the groups by that index and finally remove the tags. The second sort has at most records, so the total remains time and storage. Merely returning the value-sorted groups could reorder the classes, so the index step matters when preserving the exact earlier convention. The stronger interface evades the equality-only lower bound by permitting order comparisons.For finite even lists, these definitions take at most linear time:The length calculation and the prefix split each take time. The second operation consumes two constructors per step and emits one pair. Under the stated even-length precondition, neither definition encounters an unmatched final element.
halve :: [a] -> ([a],[a])
halve xs = splitAt (length xs `div` 2) xs
pairs :: [a] -> [(a,a)]
pairs [] = []
pairs (x:y:xs) = (x,y) : pairs xs
pairs [_] = error "pairs: odd length"