What Makes a Good Algorithm? Characteristics and How to Judge One
A good algorithm is one that is correct for every valid input, uses as little time and memory as the problem allows, and can be read and maintained by someone else. Correctness comes first, because a fast wrong answer is worthless. Efficiency comes second and is measured with Big O notation. Readability comes third and decides whether the algorithm can be maintained in a real codebase. Everything else, such as robustness and generality, refines those three.
Characteristics of an Algorithm vs Characteristics of a Good One
Two different lists share these words, and exam questions mix them up. The characteristics of an algorithm are the five conditions any procedure must meet to count as an algorithm at all. The characteristics of a good algorithm are the quality criteria used to compare two algorithms that both qualify.
| Characteristics of an algorithm (must have) | Characteristics of a good algorithm (quality) |
|---|---|
| Input: zero or more values | Correctness on all valid inputs |
| Output: at least one value | Low time complexity |
| Definiteness: each step is exact | Low space complexity |
| Finiteness: it stops | Readability and simplicity |
| Effectiveness: each step is basic | Robustness on edge cases, and generality |
If a question asks for "characteristics of algorithm in data structure", give the left column. If it asks "what makes a good algorithm", give the right column.
Correctness
Correct means the right output for every valid input, not only the usual input. The test cases that break algorithms are the edges: an empty list, one element, duplicates, negative numbers, the largest allowed size. A sorting algorithm that fails on an already sorted list is not correct. Correctness is checked by reasoning about the invariant, the fact that stays true on every pass of a loop, and then by tests that target the edges.
Time Complexity
Time complexity states how running time grows as the input grows. It is written in Big O notation, which keeps only the fastest-growing term. The table below is the scale a reviewer uses.
| Complexity | Name | Example | Rough limit for n in one second |
|---|---|---|---|
| O(1) | Constant | Hash table lookup | Any |
| O(log n) | Logarithmic | Binary search | Any |
| O(n) | Linear | One pass over an array | About 100 million |
| O(n log n) | Linearithmic | Merge sort | About 10 million |
| O(n^2) | Quadratic | Nested loop over pairs | About 10,000 |
| O(2^n) | Exponential | Trying every subset | About 25 |
A good algorithm is the one lowest on this table that still solves the problem. Moving from O(n^2) to O(n log n) is what most interview follow-up questions are asking for.
Space Complexity
Space complexity states how much extra memory the algorithm uses beyond the input. Merge sort needs O(n) extra space for its temporary arrays. Heap sort sorts in place with O(1) extra space. Recursion counts too: a recursive depth-first search uses O(h) stack space for a tree of height h. When two algorithms have the same time complexity, the one with lower space complexity is usually the better one.
Readability
Readable means another engineer can follow it without asking you. Clear names, one job per function, and no clever tricks that save two lines. A slightly slower algorithm that everyone understands often beats a faster one nobody wants to change. In an interview, readability is also what lets the interviewer follow your reasoning.
Robustness and Generality
A robust algorithm handles bad input without crashing: it checks for null, empty, and out-of-range values and returns a defined result. A general algorithm solves a family of problems, not one instance. Binary search is general because it works on any sorted array with any comparable elements.
A Worked Comparison
The task: report whether a list of n integers contains a duplicate.
def has_duplicate_slow(values): for i in range(len(values)): for j in range(i + 1, len(values)): if values[i] == values[j]: return True return False def has_duplicate_fast(values): seen = set() for value in values: if value in seen: return True seen.add(value) return False
Both are correct. The first is O(n^2) time and O(1) space. The second is O(n) time and O(n) space. For 100,000 values the first does about 5 billion comparisons and the second about 100,000. The second is the good algorithm for any list larger than a few dozen items, and it is also easier to read. That is the usual outcome: the better structure gives both speed and clarity.
Key Takeaways
- A good algorithm is correct on every valid input first, then efficient in time and space, then readable.
- Keep the two lists apart: input, output, definiteness, finiteness and effectiveness define an algorithm; correctness, complexity and readability grade one.
- Use the Big O table to judge whether a solution fits the input size before you write it.
- Practice replacing O(n^2) solutions with O(n log n) or O(n) ones in Grokking the Coding Interview.
- Learn the structures that make the faster versions possible in Grokking Data Structures for Coding Interviews.
- For optimization problems where the good algorithm is a dynamic programming one, see Grokking Dynamic Programming.

GET YOUR FREE
Coding Questions Catalog

$99

$197

$72