Skip to content
Skilled.dev

Language preparation

C# coding interview preparation: collections, tuples, and traversal

Practice C# dictionary presence, nullable tuple results, widened arithmetic, queue traversal, priority ordering, and UTF-16 text contracts.

Published · 5 min read

C# interview code benefits from explicit collection and result contracts. A dictionary lookup can miss even though its out parameter has a perfectly valid zero value. A nullable result can represent no answer without inventing an impossible index. A priority queue has different ordering from an ordinary FIFO queue.

The examples use standard .NET collections and nullable value tuples. They are checked on .NET 10, while the collection operations shown have been available in earlier supported runtimes. Match the lesson's starter signature and available runtime before selecting a newer API or package.

Match the collection to the repeated operation

Use an array for fixed-size indexed data, a List for a resizable sequence, a Dictionary for key-to-value state, and a HashSet for membership. Queue and Stack expose FIFO and LIFO schedules directly. Removing the first element of a List repeatedly does not provide the same operation model as dequeuing from a queue.

Generic collections preserve their element types. List<int> stores integer values without requiring a boxed object for each element, unlike a collection whose element type is object. A list of reference-type nodes still contains references, so copying that list does not clone the nodes.

Choose the map entry's meaning before coding. It may hold a frequency, a previous index, a distance, or a cached answer. Default zero has a different interpretation in each case, and a successful lookup should not be inferred from the value alone.

Preserve absence and widen before subtraction

Two Sum returns a nullable pair of indexes. A successful pair can begin at zero, while null means no matching distinct positions. TryGetValue supplies the presence bit separately from the stored integer index.

using System;
using System.Collections.Generic;

static class CSharpInterview
{
    public static (int First, int Second)? TwoSum(int[] values, int target)
    {
        var previous = new Dictionary<long, int>();
        for (int index = 0; index < values.Length; index++)
        {
            long complement = (long)target - values[index];
            if (previous.TryGetValue(complement, out int earlier))
                return (earlier, index);
            previous[(long)values[index]] = index;
        }
        return null;
    }

    public static List<int> BfsOrder(Dictionary<int, List<int>> graph, int start)
    {
        var queue = new Queue<int>();
        var seen = new HashSet<int> { start };
        var order = new List<int>();
        queue.Enqueue(start);
        while (queue.Count > 0)
        {
            int node = queue.Dequeue();
            order.Add(node);
            if (!graph.TryGetValue(node, out var neighbors)) continue;
            foreach (int neighbor in neighbors)
                if (seen.Add(neighbor)) queue.Enqueue(neighbor);
        }
        return order;
    }
}

The inputs are non-null, and graph neighbor lists contain integer labels. A missing adjacency key means no outgoing edges. Two Sum checks the complement before storing the current index, so an element cannot match itself. Duplicate values at different positions remain usable.

Casting target to long before subtracting makes every difference of two int values fit in the chosen arithmetic type. Assigning an already-overflowed int expression to long would be too late. This argument depends on the bounded int inputs; changing the function to arbitrary long inputs requires another overflow policy.

The TryGetValue documentation describes its success flag and absent-value behavior. If a dictionary stores nullable reference values, presence and a non-null payload can be separate questions. The domain in the example stores only integer indexes, so the bool is sufficient.

Separate a nullable result from an exception

The pair result describes an ordinary unsuccessful search. Exceptions are more appropriate when an input violates a stated precondition or an operation cannot fulfill its contract. Decide whether no match is routine before making callers catch an exception for it.

Pattern matching can inspect a nullable tuple's successful case, or a caller can check HasValue before reading Value. A forced read of Value on an absent result throws. The types make the branch visible, but the caller still has to take it.

For a function returning several possible outcomes, a dedicated result type may express the cases more clearly than sentinel numbers. Keep the representation proportional to the exercise. A short pair search rarely needs a broad error framework before the underlying invariant is correct.

Checked arithmetic settings can also change how an overflowing operation fails. Give the algorithm an explicit range argument or use a checked operation where failure belongs to its contract. BigInteger can represent larger exact values, but its arithmetic cost grows with the represented magnitude; replacing int with it does not preserve a constant-work numeric model automatically.

Distinguish queue order from priority order

The BFS marks a neighbor when enqueuing it. A second parent or a cycle therefore cannot add it again. It visits reachable vertices in hop layers under equal edge costs, with expected O(V + E) work under the usual bounded-key hash model and O(V) retained collection state.

A PriorityQueue<TElement, TPriority> instead removes the lowest priority first. Equal priorities do not guarantee arrival order. If deterministic ties are required, include a sequence number in the priority, such as a (priority, sequence) tuple, or supply a comparison with the required tie rule. The priority queue reference documents this ordering.

Use CompareTo or a suitable comparer for numeric order. A comparator implemented as subtraction can overflow. Reversing the subtraction to obtain descending order retains that risk. Test minimum and maximum values alongside duplicate priorities.

Make equality and text units deliberate

Dictionary keys need an equality and hashing policy consistent with the problem. String keys can use an explicit StringComparer when case sensitivity or ordinal comparison is part of the contract. A mutable key whose hash-relevant state changes after insertion can make lookup behavior unsuitable for the algorithm.

A C# string uses UTF-16 code units for Length and indexing. A supplementary Unicode scalar can occupy two char values. For code-point processing, Rune-based traversal provides a different unit; user-perceived grapheme clusters require another segmentation decision. A lowercase-English constraint can justify a small frequency array, while arbitrary text cannot silently inherit that alphabet assumption.

For mutable collections, remember which layer was copied. An array or list copy preserves element values, including object references. A two-dimensional jagged array should create independent inner rows when later cell mutation is supposed to affect only one row.

Practice with contracts that expose mistakes

Test Two Sum with index zero, repeated values, empty input, no match, negative values, and extreme int values. Compare with an exhaustive pair reference using widened addition so the test itself does not overflow. Validate the returned pair rather than requiring one particular answer when several pairs are valid.

For BFS, test a cycle, shared neighbors, an isolated start, and a vertex that occurs only as a neighbor. Verify each reachable label appears once. Then solve bracket matching, Two Sum, and BFS with the editor's provided C# signature when that language is available. Explain the collection state, absent case, arithmetic range, and total retained memory before moving to a harder problem.

Worked lesson walkthroughs

58 reading lessons with code examples in this language.

Concepts

Concept lesson

Arrays in C#

Learn array insertion in C#. Trace a copied result, explain shifting costs, and check empty inputs and endpoint positions without changing the original.

Concept lesson

Big O time and space complexity in C#

Analyze time and space complexity in C# with a suffix-copy example. Count nested work, returned storage, and allocation instead of counting loops alone.

Concept lesson

Hash Tables in C#

Count frequencies with a hash map in C#. Preserve input order, distinguish missing keys from zero, and explain expected lookup time and retained storage.

Concept lesson

Strings in C#

Practice string construction in C# with a repeat-text example. Explain builders, copies, Unicode representation, output size, and boundary cases.

Concept lesson

Palindromes, permutations, and anagrams in C#

Check an ASCII-normalized palindrome in C#. Trace two-pointer comparisons, case handling, ignored characters, and the limits of the text contract.

Concept lesson

Recursion in C#

Trace recursive factorial in C# from the base case through returning calls. Explain termination, stack space, integer limits, and boundary tests.

Concept lesson

Logarithmic time in C#: repeated halving and search

Understand logarithmic loops in C# by counting integer halvings. Test powers of two, explain rounding, and derive the exact step count.

Concept lesson

Binary search in C#: invariants and boundary tests

Write first-occurrence binary search in C#. Trace half-open bounds, duplicate values, missing targets, and the random-access assumption behind O(log n).

Concept lesson

Sorting in C#

Return a numerically sorted copy in C#. Preserve duplicates and the original input, choose the right library operation, and account for copied storage.

Concept lesson

Linked Lists in C#

Find a linked list's middle in C# with slow and fast runners. Define the even-length result, handle an empty list, and separate construction from traversal.

Concept lesson

Pointers in C#

Trace shared mutation and reference rebinding in C#. Follow two aliases through one object update and a new allocation, with explicit ownership notes.

Concept lesson

Queues in C#

Practice FIFO queue behavior in C#. Compare efficient front removal with a head cursor, preserve repeated values, and count fill-and-drain costs.

Concept lesson

Stacks in C#

Practice LIFO stack behavior in C#. Trace push and pop, separate empty state from zero payloads, and explain node ownership and end-operation costs.

Concept lesson

Trees in C#

Compute binary-tree edge height in C#. Read heap-indexed nullable slots, handle an empty root, and account for reachable nodes and recursion depth.

Concept lesson

Tree traversal in C#: in-order, pre-order, and post-order

Trace in-order binary-tree traversal in C#. Explain nullable child positions, visit order, recursion space, and when the output is actually sorted.

Concept lesson

Binary Search Trees in C#

Search a binary search tree in C#. Follow one ordered path, handle absent children, and relate search cost to height without assuming balance.

Concept lesson

Graphs in C#

Read graph adjacency lists in C# and compute out-degrees. Define vertex IDs, parallel edges, self-loops, representation costs, and input assumptions.

Concept lesson

Depth-First Search in C#

Implement iterative DFS in C#. Preserve listed neighbor order, skip repeated visits, trace a shared descendant, and explain pending-stack costs.

Concept lesson

Breadth-First Search in C#

Implement BFS in C# with FIFO order and enqueue-time discovery. Trace graph layers, cycles, shared descendants, and visited-storage costs.

Concept lesson

Subproblems and Overlapping Subproblems in C#

Count naive Fibonacci calls in C#. Trace overlapping states, include base-case work, and identify the cache key before applying memoization.

Concept lesson

Memoization in C#

Memoize Fibonacci in C# and count distinct computed states. Preserve cached zero, define cache lifetime, and explain remaining recursion depth.

Concept lesson

Bottom-Up vs. Top-Down in C#

Build a bottom-up Fibonacci table in C#. Follow dependency order, test zero input, and compare full-table storage with rolling-state alternatives.

Concept lesson

Dynamic Programming in C#

Solve minimum coin count in C#. Trace the recurrence, represent unreachable states, test a greedy counterexample, and explain amount-based table costs.

Data structure implementations

Implementation tutorial

Implement a dynamic array in C#

Implement a map-backed teaching array in C#. Trace push, pop, insertion, and removal, including logical length, missing indices, and obsolete entries.

Implementation tutorial

Implement a hash table in C#: collisions, updates, and removal

Build a hash table in C# with separate chaining. Follow colliding keys through lookup, update, and deletion, and explain the limits of a fixed teaching hash.

Implementation tutorial

Implement a linked list in C#: head, tail, and node identity

Build a doubly linked list in C#. Trace prepend and append, verify reciprocal links and both endpoints, and explain ownership and retention.

Implementation tutorial

Implement a queue in C#: FIFO links and empty transitions

Implement a linked queue in C#. Trace FIFO order, the last removal, and refilling an empty queue while preserving first and last endpoint invariants.

Implementation tutorial

Implement a stack in C#: push, pop, and ownership

Implement a linked stack in C#. Follow the top link through push, peek, and pop, including empty state, returned nodes, and retained chains.

Implementation tutorial

Implement a binary search tree in C#: insert and search

Build a binary search tree in C#. Trace insertion and search, preserve duplicate keys on the right, and test skewed shapes without assuming balancing.

Implementation tutorial

Implement a graph in C#: adjacency lists and edge removal

Build an adjacency-list graph in C#. Trace directed and undirected edges, self-loops, parallel entries, and removal of incoming and outgoing edges.

Practice problems

Practice walkthrough

Two Sum in C#: all index pairs with Dictionary

Return all index pairs with a C# Dictionary and TryGetValue. Widen complement arithmetic, preserve index zero, and produce an int[][] result.

Practice walkthrough

Valid brackets in C#: matching delimiters with Stack<char>

Validate bracket nesting with Stack<char> in C#. Guard Pop, compare delimiters in order, ignore other characters, and keep calls independent.

Practice walkthrough

Product of array except self in C#: long arrays and prefix products

Solve Product Array in C# with long[], prefix/suffix passes, and no division. Explain initialization, checked arithmetic, zeros, and input preservation.

Practice walkthrough

Counting sort for ages in C#: worked solution

Sort bounded ages with a frequency table, preserving duplicate values. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Best stock profit in C#: one transaction

Find the best single chronological stock trade with one scan. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Most frequent email in C#: hash-map solution

Count email occurrences and return the unique most frequent address. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

First Bad Version in C#: boundary binary search

Find the first true value in a monotone version predicate. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Reverse a linked list in C#: pointer walkthrough

Reverse a linked list by moving nodes from an untouched suffix to a reversed prefix. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Remove kth node from end in C#: zero-based deletion

Delete the zero-based kth node from the end of a linked list. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge sorted linked lists in C#: Combine Blockchains

Merge two sorted linked chains while preserving duplicate timestamps. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count Islands in C#: DFS flood-fill walkthrough

Count side-connected land components with a destructive flood fill of the working grid. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate a binary search tree in C#: inherited bounds

Validate every binary-tree node against bounds inherited from all its ancestors. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Binary tree maximum depth in C#: count edges

Measure a binary tree’s longest root-to-leaf path in edges. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Lowest common ancestor in C#: return the original node

Find the lowest common ancestor in a general binary tree and return the original node. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Generate string permutations in C#: positional results

Generate positional string permutations, retaining repeated results for repeated characters. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Group palindrome permutations in C#: counts and keys

Filter palindrome permutations, then group retained strings by character multiset. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Merge intervals in C#: Interview Scheduler

Merge sorted interval coverage with an explicit touching-endpoint rule. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Min-max stack in C#: constant-time extrema

Support stack extrema through histories that retain duplicate record values. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

BFS shortest path in C#: Pilot a Rocket

Find an unweighted shortest route and reconstruct it from predecessor links. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Maze reachability in C#: memoized down/right search

Memoize directed maze reachability while keeping unknown distinct from false. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a rotated sorted array in C#: Shifted Search

Search a distinct rotated sorted array by identifying an ordered half. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Search a sorted matrix in C#: staircase walkthrough

Eliminate a row or column at each step in a row-and-column sorted matrix. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

House Robber in C#: maximum nonadjacent sum

Maximize a nonadjacent selection with two rolling prefix optima. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Sum decimal digits in C#: recursive solution

Sum decimal digits through a decreasing recursive integer decomposition. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Count 1, 2, or 3-step paths in C#: Three Moves

Count ordered one-, two-, and three-step routes with rolling dynamic programming. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Coin change in C#: minimum-coin dynamic programming

Minimize unlimited coin usage with a bottom-up amount table. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Validate Sudoku in C#: rows, columns, and subsquares

Validate filled Sudoku entries against rows, columns, and subsquares. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.

Practice walkthrough

Spiral matrix traversal in C#: Walk a Matrix

Traverse a rectangle in clockwise spiral order using shrinking boundaries. Follow the C# implementation, its correctness argument, worked example, complexity, and boundary tests.