When solving any DSA problem, it helps to systematically analyze algorithms, data structures, and optimization opportunities. Use this checklist to think clearly and code efficiently.
- How large is the input?
- How does the algorithm scale as
ngrows?
- Comparisons? Swaps? Arithmetic steps?
- Just estimate trends, not exact counts.
- Does the order of input affect performance?
- Example: an already sorted array may help or hurt.
- Array, list, hash table, tree, heap, etc.
- Are they suitable for the operations performed?
- Constant → O(1)
- Logarithmic → O(log n)
- Linear → O(n)
- Linearithmic → O(n log n)
- Quadratic → O(n²), etc.
- Extra arrays, temporary buffers, recursion stack?
- Is it in-place (O(1) extra space) or uses O(n)?
- Single loop → O(n)
- Nested loops → O(n²)
- Divide & conquer → often O(n log n)
- Can you break early if the answer is found?
- Are calculations repeated unnecessarily?
- Can caching/memoization help?
- Use hash tables for O(1) lookups
- Binary search for sorted data
- Heap for priority operations
- Dynamic programming for overlapping subproblems
Besides swaps and comparisons, check:
- How many elements do you walk through?
- How many recursive calls?
- How deep is the call stack?
- Extra arrays, temp variables, recursion stack
- Costly?
- Shifting elements in arrays? Pointer updates in linked lists?
- O(1) access (arrays) vs O(n) access (linked lists)
- Pointers, dynamic resizing, tree balancing, hash collisions
- Input Size (n) – bigger input → more operations
- Input Order – sorted vs unsorted, best/avg/worst case
- Type of Operations – comparison (cheap), swap (more costly), recursion (stack overhead)
- Data Structure Choice – list search O(n) vs hash table O(1) avg
- Algorithm Strategy – brute-force O(n²), divide & conquer O(n log n)
- Memory Access Patterns – arrays faster than linked lists due to locality
- Extra data structures → arrays, stacks, trees
- Recursion → each call uses stack memory
- Temporary variables → buffers, helper arrays
- In-place vs out-of-place → O(1) vs O(n)
- Skip unnecessary operations
- Early stopping / pruning
- Fast search → hash table
- Fast sorted traversal → BST
- Fast insert/delete → linked list
- Fast random access → array
- Divide & conquer, dynamic programming, greedy, memoization
- Precompute, use prefix sums, caching
- Use arrays over linked lists where possible
- In-place operations reduce extra memory
- Check: traversals, recursion depth, memory usage, insert/delete cost, access pattern, data structure overhead
- Time Complexity depends on: input size/order, operation type, data structure, algorithm strategy, memory access
- Space Complexity depends on: extra structures, recursion, temp variables, in-place vs copies
- Optimize by: choosing better algorithms, better data structures, reducing work, caching, avoiding redundant operations
- Favor clarity first, then optimize
- Measure time vs space trade-offs
- Use built-in data structures (Python:
dict,set,deque,heapq) wisely - Think in terms of patterns: sliding window, two pointers, prefix sums, etc.
- Practice mental dry runs for small inputs to catch logic errors