Blog
Mert Özen's blog posts on software development, backend, frontend, and DevOps.
Why Algorithms and Data Structures Still Matter: A Roadmap for .NET Developers
The right data structure keeps the same code standing as the data grows: 100 ISBN lookups in a 100,000-book catalog take 9.7 ms with a List, 6.5 µs with binary search on a sorted array and 0.93 µs with a Dictionary. The post explains the difference between an abstract data type and its implementation, the series' xUnit and BenchmarkDotNet workflow and the 22-post roadmap.
Arrays and Dynamic Arrays in C#: Contiguous Memory, Indexing and Writing Your Own List
A dynamic array is an array that moves to a new array twice its size when it fills up; 1,000,000 Adds copy only 1,048,572 elements in total, so the cost per element is constant. We write our own DynamicArray<T> with xUnit tests, explain why a removed slot must be cleared for the GC, and show with BenchmarkDotNet that walking a 4,000×4,000 grid column by column is 3 times slower than row by row.
Linked Lists in C#: Nodes, Sentinels and Classic Problems
A linked list removes a node you hold in O(1) but pays for a separate object per element: 100,000 ints take 400 KB in a List and 4.8 MB in a LinkedList<int>, and walking 1 million scattered nodes is 259 times slower than a List. We write singly and doubly linked lists with sentinels, in-place reverse, Floyd's cycle detection, the middle node and a sorted merge, all with xUnit tests.
Stacks, Queues and Ring Buffers in C#: Your Own Implementations and Real Uses
What makes a queue O(1) on an array is the circular buffer: instead of shifting elements, the head and tail indexes advance and wrap around; a queue built with List.RemoveAt(0) is 497 times slower than Queue<T> with 100,000 orders. We write an array-based stack, a circular queue that unwraps the ring as it grows, a bounded ring buffer with an overflow policy, bracket validation, undo/redo and a monotonic stack, all with xUnit tests.
Recursion in C#: Base Case, Call Stack, StackOverflowException and Converting to Iteration
In .NET a StackOverflowException cannot be caught and kills the whole process; on an 8 MB main thread an empty method called itself about 130,650 times and the catch block never ran. We cover the anatomy of recursion, call stack frames, converting recursive code to iteration with an explicit Stack<T>, and depth defences such as JSON MaxDepth (64) and EnsureSufficientExecutionStack, with xUnit tests and real output.
How Hash Tables Work: Writing Your Own HashMap in C#, Collisions and Load Factor
A hash table computes a bucket index from the key's hash code and resolves collisions with chaining or open addressing; a GetHashCode that always returns 42 makes a 10,000-key Dictionary 967 times slower. We write our own HashMap with the same internals as .NET's Dictionary (buckets + entries, free list, prime sizes), linear probing with tombstones, look at randomized string hashing and build a Bloom filter, all with xUnit tests.
Array and Hashing Patterns: Two Pointers, Sliding Window, Prefix Sum and Frequency Maps
Computing once and keeping what a nested loop recomputes takes two sum from O(n²) to O(n): on 100,000 prices brute force takes 1.69 seconds and the Dictionary version 0.32 ms. We cover frequency maps and CountBy, two pointers, fixed and variable-width sliding windows, a rate limiter tested with TimeProvider, and counting ranges that sum to k with a prefix sum plus a Dictionary, all with xUnit tests.
Sorting Algorithms 1: Insertion Sort, Merge Sort and Stability
Array.Sort is not stable: sorting 200 date-ordered orders by customer scrambles each customer's dates, while merge sort and LINQ OrderBy keep them. We cover insertion sort (6 times faster than Array.Sort on 10,000 nearly sorted elements), top-down and bottom-up merge sort, the idea of external merge sort and stability, with xUnit tests and BenchmarkDotNet measurements.
Quick Sort, Introsort and Counting/Radix Sort: How .NET's Array.Sort Sorts
Array.Sort uses introsort: quick sort, heapsort once the depth passes 2·log n, insertion sort for 16 elements or fewer; that is why a textbook quick sort with a last-element pivot becomes 749 times slower on 10,000 sorted elements while Array.Sort is unaffected. We cover Lomuto and Hoare partition, median-of-three, the Ω(n log n) lower bound, counting and radix sort that beat Array.Sort 11–12 times, and the a - b comparer trap, with tests.
Binary Search in Depth: Lower Bound, Off-by-One and Binary Search on the Answer
The way to write binary search correctly is against an invariant, such as the half-open range [lo, hi); it makes at most 20 comparisons on 1 million elements, but a single boundary bug such as lo = mid sends it into an infinite loop. We write lower and upper bound, look at Array.BinarySearch's ~index return value, use binary search on the answer to find the minimum truck capacity and search a time range in a log, all with xUnit tests.
Trees and Binary Search Trees: Insert, Delete and Traversal in C#
Search and insert in a binary search tree cost as much as the tree's height; inserting the same 10,000 IDs in sorted order turns the tree into a chain and 1,000 lookups become 221 times slower than in a randomly built tree. We write insert, find, the three cases of delete, recursive and iterative traversals, correct BST validation, and build a tree in O(n) from a flat category table with a Dictionary and serve it as JSON from an ASP.NET Core minimal API, with tests.
Balanced Trees: AVL, Red-Black and the B-Tree Behind Database Indexes
A balanced tree keeps its height at O(log n) whatever the insert order: with 10,000 sorted ids a plain BST is 10,000 levels tall, an AVL tree 14. We write AVL insert and rotations in C#, look at SortedSet's red-black tree through reflection, and inspect the levels and leaf chain of a real B+tree index in PostgreSQL with pageinspect.