17 Coding Challenges to Sharpen Your Critical Thinking
Practice 17 coding challenges that build algorithmic reasoning, from Two Sum and stacks to graph search and dynamic programming. Learn how to work through each one.

Coding challenges can give you practice decomposing a problem, comparing approaches, tracing state, and explaining why an algorithm works. This guide covers 17 problems, the reasoning pattern behind each, and a practice method that makes the work more useful than simply memorizing solutions. It is a practical learning rationale, not a claim that these exact exercises have been proven to improve general critical thinking.
Start with a plain-language restatement, write down constraints, solve a simple version, then look for a better approach. For every problem, test ordinary inputs, boundaries, and adversarial cases. Before coding an optimization, explain why it preserves correctness.
1. Find the Missing Number
Problem: An array contains distinct numbers from 0 through n with one value missing. Find it. This trains you to identify an invariant: the complete range has a known sum, n(n+1)/2. Subtract the array sum. For very large values, the sum can overflow in fixed-width integer languages; XORing all expected and observed values avoids that risk. Clarify whether the range begins at zero, whether values are distinct, and whether the input can be empty.
2. Two Sum
Given an array and a target, return indices of two values that add to it. The baseline checks every pair in O(n²) time and O(1) extra space. A hash map reduces expected time to O(n) with O(n) space: as you scan, check whether target minus the current number has appeared, then store the current number and index. Check whether the problem permits reusing an element, whether there may be multiple answers, and whether the answer must be indices or values. Lookup before insertion avoids pairing an item with itself.

3. Palindromic Substrings
For a string, count or find its palindromic substrings. The key observation is that every palindrome has a center: a character for odd lengths or a gap for even lengths. Expand outward while the characters match. This takes O(n²) time and O(1) extra space; dynamic programming also records matching intervals but can use O(n²) space. State whether repeated occurrences count separately: in “aaa,” the substrings at different positions are distinct occurrences.
4. Reverse a Linked List
Reverse a singly linked list by redirecting each next pointer. Iteratively, keep previous, current, and next_node; save the next node before changing the pointer. The algorithm is O(n) time and O(1) space. Recursion is another way to express the reversal, but uses O(n) call-stack space and may exceed the stack limit on long lists. Test empty and one-node lists, and ensure the old head terminates with a null pointer.
5. Valid Parentheses
Determine whether brackets are correctly matched and nested. Push each opening bracket onto a stack; for a closing bracket, the top must be its matching opener. Finish with an empty stack. This takes O(n) time and O(n) space in the worst case. A string starting with a closer is immediately invalid. If the input includes ordinary characters, establish whether to ignore them or reject them; examples often differ on this detail.
6. Container With Most Water
Given nonnegative heights, choose two lines that hold the most water. The area is the distance between indices times the shorter height. Begin at both ends, then move the pointer at the shorter line: keeping that height while shrinking width cannot improve area, while moving the taller side cannot raise the limiting height. This proof-driven rule yields O(n) time and O(1) space. Fewer than two lines produce zero area if the interface defines a result for that case.
7. Word Ladder
Find the shortest transformation from a start word to an end word, changing one letter at a time, with intermediate words drawn from a dictionary. Model words as graph vertices and valid one-letter changes as edges. Breadth-first search finds a shortest path in an unweighted graph. Generate neighbors by replacing each character with letters, or precompute wildcard buckets such as h*t. Track visited words to avoid cycles. Check word lengths and whether the end word must appear in the dictionary. Bidirectional BFS can reduce explored states when the search space is large.
8. Count Inversions
An inversion is a pair i < j where values[i] > values[j]. A direct pair count is O(n²). During merge sort, when a right-half value is chosen before remaining left-half values, each of those left values forms an inversion. This gives O(n log n) time and O(n) auxiliary space. Use a wide enough integer type: the maximum count is n(n−1)/2. For equal values, use the comparison that preserves the problem’s strict “greater than” definition.
9. Least Recently Used Cache
Design a cache with capacity, get, and put, evicting the least recently used key. Combine a hash map from keys to nodes with a doubly linked list ordered from most to least recently used. Move accessed or updated nodes to the front; evict from the tail. Both operations are expected O(1). Decide how capacity zero behaves, whether updating an existing key changes its recency, and what a missing get returns. Sentinel head and tail nodes simplify insertion and removal logic.
10. Sudoku Validator
Check whether a partially filled Sudoku board violates row, column, or subgrid uniqueness. Traverse once while maintaining a set per row, column, and box. For a 9-by-9 board, the work is O(81), effectively constant; generalized boards take O(n²) time. Empty cells do not participate. Validate dimensions and allowed symbols if inputs are not guaranteed well-formed. A validator checks consistency, not whether the puzzle has a solution or a unique solution.
11. Find All Anagrams in a String
Return starting positions where a pattern’s anagram occurs in a string. Use a sliding window with the same length as the pattern and maintain character frequencies. Update the counts as one character enters and another leaves. With a fixed alphabet, the work is O(n) and frequency storage is constant; for unrestricted Unicode, define character semantics and use a map. Return no positions if the pattern is longer than the string. Empty-pattern behavior should be specified explicitly.
12. Trapping Rain Water
Given bar heights, compute trapped water. At each index, water depends on the smaller of the tallest boundaries on either side, minus the current height. A two-pointer method tracks left and right maxima, processing the side with the smaller boundary; that side’s maximum determines its trapped amount. It runs in O(n) time and O(1) extra space. A prefix/suffix maximum method is easier to derive but uses O(n) space. Negative heights are usually invalid; large totals may need a wide integer type.
13. Merge k Sorted Lists
Combine k sorted linked lists into one sorted list. A min-heap holds the current head of each nonempty list. Repeatedly remove the smallest node, append it, and add that node’s successor. For N total nodes, this takes O(N log k) time and O(k) heap space. Divide-and-conquer pairwise merging has the same asymptotic time. Handle zero lists and empty lists, and decide whether to reuse nodes or allocate a new output list.
14. First Missing Positive
Find the smallest positive integer absent from an unsorted array in O(n) time and O(1) extra space. The answer must be between 1 and n+1. Place each value x in position x−1 when 1 ≤ x ≤ n, swapping until each useful value is positioned or a duplicate prevents progress. Then scan for the first index i where values[i] ≠ i+1. Carefully stop on duplicates to avoid infinite swapping. Ignore zero, negatives, and values greater than n.
15. Course Schedule
Given courses and prerequisite pairs, determine whether all courses can be completed. Treat each prerequisite as a directed edge and detect a cycle. Depth-first search can use unvisited, visiting, and completed states; encountering a visiting node proves a cycle. Kahn’s algorithm repeatedly removes nodes with in-degree zero and succeeds if every node is removed. Both approaches are O(V+E). Confirm edge direction from the input definition, and account for disconnected components and duplicate edges.
16. Word Search
Find whether a word can be traced through adjacent cells in a grid without reusing a cell. Run depth-first search from matching starting cells, mark a cell as used for the current path, and backtrack afterward. The worst-case work is O(RC·3^L) for word length L after the first step, with recursion depth O(L). Reject words longer than the number of cells. Check whether diagonal movement is allowed; the common version permits only horizontal and vertical neighbors. Restore the board after search if modifying it in place.

17. Maximal Rectangle in a Binary Matrix
Find the largest all-one rectangle in a binary matrix. Treat each row as the base of a histogram: increment column heights for ones and reset to zero for zeros. For each row, compute the largest histogram rectangle with a monotonic stack of increasing heights. The total time is O(RC), with O(C) extra space. Include a zero-height sentinel or explicitly flush the stack after each row. Empty matrices, ragged rows, and nonbinary characters need defined handling.
A repeatable method for solving coding challenges
- Restate the task. Write down inputs, outputs, and what counts as a valid answer in your own words.
- Extract constraints. Input size, value ranges, ordering, duplicates, memory limits, and mutation rules determine which approaches fit.
- Build a baseline. A direct solution makes the behavior concrete. Record its time and space cost.
- Find the bottleneck. Ask what repeated work the baseline performs. Consider a hash map, stack, heap, two pointers, graph traversal, or a maintained invariant.
- Explain correctness. State the invariant or proof for the optimized step. For example, why is it safe to move the shorter pointer in Container With Most Water?
- Test deliberately. Use a small table containing a normal case, a boundary case, and a case designed to break a careless implementation.
- Review complexity and implementation details. Include auxiliary data structures, recursion depth, integer range, and required output ordering.
In Python, a useful frequency-counter pattern for the anagram challenge is:
from collections import Counter
def find_anagrams(text: str, pattern: str) -> list[int]:
if not pattern or len(pattern) > len(text):
return []
need = Counter(pattern)
window = Counter(text[:len(pattern)])
result = []
if window == need:
result.append(0)
for right in range(len(pattern), len(text)):
entering = text[right]
window[entering] += 1
leaving = text[right - len(pattern)]
window[leaving] -= 1
if window[leaving] == 0:
del window[leaving]
if window == need:
result.append(right - len(pattern) + 1)
return result
if __name__ == "__main__":
assert find_anagrams("cbaebabacd", "abc") == [0, 6]
assert find_anagrams("abab", "ab") == [0, 1, 2]
print("Examples passed")
This version is directly runnable with Python 3. It compares frequency maps at each step, which is simple but can cost more than a constant-sized comparison when the alphabet is broad. For a known small alphabet, track how many character counts currently match to avoid comparing the full maps each time. The empty-pattern result above is an explicit convention; adapt it to the problem statement.
Choose a progression that exposes new patterns
| Stage | Challenges | Question to ask |
|---|---|---|
| Foundations | Missing number, Two Sum, valid parentheses, reverse linked list | What state must I retain as I scan? |
| Pattern building | Palindromic substrings, container, anagrams, rain water | Can I reduce repeated work with a window or pointers? |
| Graphs and search | Word Ladder, Course Schedule, Word Search | What are the states, transitions, and visited rules? |
| Data structure design | LRU cache, merge k lists, maximal rectangle | Which operations must be fast, and what structure supports them? |
| Optimization and proof | Count inversions, first missing positive, Sudoku validator | What invariant makes this improvement correct? |
For each attempt, save the initial idea, the failing test that changed your mind, and the complexity of your final solution. Revisit the problem after several days and derive the method again. If you want more structured practice, EMKC organizes practical exercises by difficulty and describes support for 17 languages. Codewars offers community kata, browser tests, ranks, peer solutions, and 55+ languages; its displayed platform activity figures can change. A book option is Exercises for Programmers: 57 Challenges to Develop Your Coding Skills from PragProg. Check current availability and price before buying.
Capture a challenge project’s output
Some exercises lead to a small visual project: a Sudoku board, a pathfinding grid, or a histogram visualization. If you are checking how a rendered page looks at different viewport sizes, take screenshots as one part of the review. A screenshot can show layout and rendering; it cannot establish that your algorithm is correct. Keep unit tests for behavior and use the image to inspect presentation.
Or skip the browser setup
For a screenshot of a public page, ScreenshotNeo provides a single-request API. See the ScreenshotNeo API documentation for request options.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Cookie banners are accepted and removed before capture, along with known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, failed loads, timeouts, and cache hits are not billed; response headers report the page verdict and billing status. An MCP server exposes screenshot, page-info, and PDF tools to AI agents. The free plan includes 1,000 screenshots each month with no card; paid plans start at $5 for 3,000. Try ScreenshotNeo and sign up for 1,000 free screenshots a month, no card required.
Troubleshooting your solutions
| Symptom | Likely cause | Fix |
|---|---|---|
| Works on examples, fails hidden tests | Assumed constraints, duplicates, or output format | Re-read the specification and add boundary and adversarial cases. |
| Timeout on large input | Quadratic baseline or repeated traversal | Measure the dominant loop; consider hashing, sorting, a heap, or a monotonic structure. |
| Wrong result for duplicates | Equality and strict inequality were conflated | Check the problem definition, especially inversions, anagrams, and Two Sum reuse. |
| Recursion limit or stack overflow | Deep list, grid, or graph path | Use an iterative traversal or bound the recursion depth where appropriate. |
| Infinite loop in an in-place algorithm | Swapping a duplicate into the same position | Stop when the destination already contains the value, as in First Missing Positive. |
| Integer overflow | Intermediate sum or pair count exceeds the type | Use a wider type or an overflow-safe formulation such as XOR for the missing-number variant. |
| State leaks between test cases | Visited sets, counters, or output lists reused | Initialize per invocation or explicitly reset all mutable state. |
Reliability, performance, and cost of practice
These exercises have no runtime service cost; the practical costs are time, attention, and possibly a paid learning resource. Optimize only after establishing correctness and identifying a real complexity issue. Prefer the simplest approach that meets stated limits, then preserve a reference implementation or tests when rewriting it. For interview preparation, explain trade-offs aloud: an O(n) hash-map method may use more memory than an O(n²) scan, and an in-place algorithm may be harder to reason about than one using extra storage.
Programming puzzles are also used to evaluate program synthesis. Microsoft Research’s 2021 publication introduced Python Programming Puzzles as a dataset for evaluating program synthesis, spanning tasks from string manipulation to Tower of Hanoi, dynamic programming, and factoring. The publication reported performance figures on its own benchmark, and a small user study found positive correlation between puzzle-solving performance and coding experience. Those results provide context for programming puzzles as a research tool; they do not prove that this particular set improves broad critical-thinking ability.
FAQ
Which challenge should a beginner start with?
Try Valid Parentheses or Two Sum, then compare your solution with a baseline and explain the data structure choice.
Should I memorize these patterns?
Learn to recognize when a pattern applies, but derive its invariant and test its assumptions each time.
How many challenges should I solve in a session?
One carefully reviewed problem can be more useful than several rushed attempts. Spend time understanding a failed approach and revisiting it later.
Are these good interview exercises?
They cover common algorithmic patterns, but interview relevance varies by role and company. Practice clarifying requirements and communicating trade-offs, not only producing code.