Overview
Algorithm courses ask for three things that lectures often blur together: knowing what an algorithm does, arguing that it is correct, and analyzing how much time and memory it uses. We separate them. First we trace the algorithm on a small input, often with a drawn array, tree or graph. Then we identify the invariant that makes it work. Finally we count operations to reach an asymptotic bound in Big-O, Big-Omega or Big-Theta notation.
Debugging is a separate skill that is rarely taught explicitly. We use a scientific approach: reproduce the failure reliably, reduce it to a minimal example, form a hypothesis about the cause, test it with a debugger, an assertion or a log statement, and confirm the fix with a regression test. You drive the process in sessions so the method becomes yours.
For researchers, the same skills apply to scientific code: a simulation that gives different results on each run, a numerical routine that loses precision, or a pipeline that silently drops records. We help you locate the defect and add tests so it cannot return unnoticed.
Who it’s for and the problems it solves
This is for you if you are…
- Computer science students in data structures, algorithms or discrete mathematics courses
- Students from other disciplines taking a required algorithms or programming module
- Candidates practicing algorithmic problem-solving for technical interviews
- Researchers whose simulation or analysis code produces results they do not trust
- Developers who want a structured debugging method rather than trial and error
Common problems we help with
- You can follow a lecture on Dijkstra or quicksort but cannot reproduce it on a new input
- Recurrences such as T(n) = 2T(n/2) + n feel like magic, and you are unsure when the master theorem applies
- Dynamic programming problems seem impossible to set up: you cannot see the subproblems
- Your program passes the provided tests but fails hidden ones, and you have no idea where to look
- A bug appears intermittently, or only on large inputs, and print statements have not helped
What’s included — and what isn’t
Included
- Step-by-step traces of algorithms on small inputs, with diagrams of arrays, trees, heaps and graphs
- Sorting and searching: insertion, merge, quick and heap sort, binary search and its boundary conditions
- Graph algorithms: BFS, DFS, topological sort, Dijkstra, Bellman–Ford, minimum spanning trees (Prim, Kruskal), union–find
- Algorithm design paradigms: divide and conquer, greedy choice and exchange arguments, dynamic programming, backtracking
- Correctness reasoning with loop invariants, induction and termination arguments
- Complexity analysis: counting operations, asymptotic notation, recurrences, the master theorem, amortized analysis
- Data structure trade-offs: arrays, linked lists, hash tables, balanced trees, heaps and tries
- Guided debugging of your own code using minimal reproducible examples, debuggers, assertions, logging and tests
- Review of your algorithm write-ups or pseudocode for clarity and rigor
Not included
- Solving graded problem sets, programming assignments or online judge tasks that count toward your grade
- Sitting algorithm exams, timed coding assessments or interviews for anyone
- Formal verification with proof assistants for certification purposes (we can explain the concepts)
What you receive
- Annotated traces
- Worked traces of the algorithms we covered, on inputs of your choice, showing the state at each step so you can check your own traces against them.
- Complexity worksheet
- A short set of practice problems on recurrences and operation counting, similar to but different from your course exercises, with worked solutions released after you attempt them.
- Debugging log
- A record of the hypotheses we tested, the tools used and what each revealed, which doubles as a template for your next bug.
- Regression tests
- For research and professional code, the tests that reproduce the defect and confirm the fix, added to your test suite.
How it works
Pin down the question
Tell us which algorithm, concept or bug you are stuck on, and share lecture notes or the relevant code. We check what you already understand with a quick trace or question.
Build intuition
We work through a small example visually and relate each step to the pseudocode, then vary the input to expose edge cases.
Formalize
We state the invariant or recurrence, reason about correctness and derive the complexity bound together.
Apply to your code
For debugging, you reproduce the failure, shrink it and test hypotheses with the debugger while we guide the process.
Practice
You attempt parallel problems independently and we review your reasoning in a follow-up session.
Techniques for reasoning and debugging
- Loop invariants
- A property true before and after each iteration, used to prove algorithms such as insertion sort and binary search correct via initialization, maintenance and termination.
- Recurrence solving
- Recursion trees, substitution and the master theorem for divide-and-conquer recurrences, including the cases where the master theorem does not apply.
- Amortized analysis
- Aggregate, accounting and potential methods, explained with dynamic arrays and union–find.
- Minimal reproducible example
- Removing code and data until the failure remains with nothing else, which usually reveals the cause and makes any remaining question easy to ask.
- Bisection
- Binary search over inputs, code regions or Git history (git bisect) to find where behavior changes.
- Assertions and structured logging
- Checking invariants at runtime and logging state with levels and context instead of scattered print statements.
- Property-based and differential testing
- Comparing your implementation with a simple brute-force version on many random inputs (for example with Hypothesis in Python) to find failing cases automatically.
Why Big-O is not the whole story
Asymptotic notation describes growth, not speed on your input. An O(n log n) algorithm with a large constant can lose to an O(n²) algorithm on small arrays, which is why many library sorts switch to insertion sort for short runs. Cache behavior, memory allocation and input distribution matter too. We teach you to derive the bound and then check it against measurements, so both your exam answers and your real code make sense.
A debugging routine you can reuse
Most time lost in debugging is spent changing code before understanding the failure. The routine below is what we practice in sessions until it becomes habit.
- Reproduce: find an input that fails every time and write it down
- Reduce: shrink the input and the code until removing anything makes the bug disappear
- Hypothesize: state one specific cause you can test
- Observe: use a breakpoint, assertion or log line to test that hypothesis, not to browse
- Fix and pin: correct the cause, then keep the failing input as a regression test
Dynamic programming without the mystery
Dynamic programming becomes manageable when you define the subproblem in words first, write the recurrence, identify the base cases and decide the evaluation order. We practice this with classic problems such as longest common subsequence, knapsack and edit distance, then compare memoized recursion with bottom-up tables and discuss how to reconstruct the solution, not only its value.
What the work can look like
Illustrative example — fictional data
Sample explanation: why this binary search loops forever
An undergraduate brought a Java binary search that hangs on some inputs. The code and explanation below are invented to illustrate how we walk through a bug without writing the student's assignment.
Reproduce and reduce
The loop uses while (lo < hi) with mid = (lo + hi) / 2 and sets lo = mid when the target is larger. With lo = 3 and hi = 4, mid is 3, so lo never changes. The smallest failing input is a two-element array where the target is the second element.
The invariant view
We state the intended invariant: if the target exists, it lies in the half-open range [lo, hi). Updating lo = mid breaks progress because mid is already excluded as a candidate; the consistent update is lo = mid + 1. The student then re-derives the other branch themselves.
Pin it with a test
The student adds JUnit tests for arrays of length 0, 1 and 2 and for targets at both ends, and runs them after every change. We also mention that (lo + hi) / 2 can overflow for very large int indices, and why lo + (hi - lo) / 2 avoids it.
Illustrative example with invented code. In sessions we explain the concept and the student makes the changes to their own work.
What you’ll need to provide
- Course notes, textbook chapter references (for example CLRS or Sedgewick) or the problem you are studying
- For debugging: the code, the failing input, expected and actual output, and how you run it
- Any course policy on collaboration or outside help
- Your preferred language for examples (Python, Java, C++ or pseudocode)
You don’t need everything ready to ask for a quote — tell us what you have and we’ll say what’s missing.
What affects price and turnaround
Price depends on
- Number and difficulty of topics, from introductory sorting to advanced graph or amortized analysis
- Live tutoring sessions versus written explanations and worksheets
- For debugging: size of the codebase and difficulty of reproducing the environment
- Whether regression tests or a written debugging report are required
Turnaround depends on
- How quickly a failing case can be reproduced on our side
- Your exam or submission date and availability for sessions
- Access to course materials that define notation and expectations
Every project is quoted individually. How we price our work.
Questions people ask before starting
Can you solve my algorithms problem set?
No. We explain the techniques involved, work through comparable problems from textbooks or our own examples, and review your reasoning or proofs once you have attempted them. This keeps the work yours and prepares you for exams where you have no help.
Do you use pseudocode or a real language?
Whichever your course uses. Many algorithm courses expect CLRS-style pseudocode in exams; programming courses expect Python, Java or C++. We can move between them and explain how array indexing conventions (0-based versus 1-based) affect the details.
My code passes all visible tests but fails hidden ones. How can you help?
We teach you to think like the hidden tests: empty inputs, single elements, duplicates, negative numbers, very large values and worst-case orderings. We also show how to write a brute-force checker and compare it against your solution on random inputs, which is one of the fastest ways to find the failing case yourself.
Can you help with technical interview preparation?
Yes, as coaching: we cover common patterns (two pointers, sliding window, BFS on grids, dynamic programming, heaps), practice explaining your approach aloud, and review your solutions for correctness and complexity.
I am a researcher and my simulation gives different results each run. Is that a bug?
Not necessarily. Unseeded random number generators, parallel reductions with floating-point addition, and unordered data structures can all cause run-to-run differences. We help you distinguish expected variability from defects, fix seeds where appropriate, and add tests with tolerances suited to floating-point results.