Approaches to problem solving
Problem-solving approaches shape how you move from a broad need to an implementable
design. Top-down, bottom-up, and step-wise refinement each provide a different lens for
structuring work. Mastering when and how to apply them—and how to represent your design
in hierarchy charts—leads to clearer algorithms, cleaner programs, and faster iteration.
Top-down
Idea: Start with the problem as a whole, then repeatedly break it into smaller, more
specific subproblems until each subproblem is simple enough to implement directly.
o Why it helps: Keeps focus on the overall objective and user requirements;
reduces complexity by decomposition before touching code.
Process:
o Define goal: State the overall function at a high level (e.g., “Process student
scores”).
o Partition tasks: Split into major phases (e.g., input → validate → compute →
report).
o Refine each phase: Continue decomposing (e.g., compute → sum → count →
average).
o Stop when atomic: End when steps are clear, testable, and minimally dependent.
Strengths:
o Clarity of intent: Stakeholders can review early.
o Traceability: Every low-level task ties back to the top-level requirement.
o Risk management: Big unknowns surface early.
Limitations:
o Assumes availability: May overlook low-level constraints (e.g., missing library
support, data source quirks).
o Late discovery of detail issues: Implementation hurdles may appear after many
design layers.
Best use cases:
o Complex requirements: Systems with many features or strict outputs.
o Teaching/communication: Ideal for planning documents, design reviews, and
flowcharts.
Bottom-up
Idea: Identify, design, and implement useful low-level components first, then assemble
them into larger systems.
o Why it helps: Builds on proven building blocks; leverages reusable modules and
libraries.
Process:
o Catalogue primitives: List essential operations/utilities (e.g., parseNumber,
validateRange, computeAverage).
o Implement and test: Build robust functions with clear contracts (inputs/outputs,
errors).
o Compose: Combine components into higher-level behaviors (e.g., analytics
pipeline).
Strengths:
o Reusability: Components serve multiple projects.
o Reliability: Early unit tests solidify foundations.
o Velocity: Faster once a toolkit exists.
Limitations:
o Local optimization: Components may not align with top-level goals.
o Integration surprises: Interfaces can mismatch; glue code grows.
o Scope creep: Easy to build “cool parts” that aren’t needed.
Best use cases:
o Utility-heavy tasks: Data processing, math libraries, common services.
o Evolving systems: When requirements change and modularity is crucial.
Step-wise refinement
Idea: Iteratively transform a broad solution outline into a precise design by adding detail
in small, justified steps.
o Why it helps: Balances top-down clarity with bottom-up feasibility; each
refinement is reviewable and testable.
Process:
o Start with a specification: Inputs, outputs, constraints.
o Write a high-level algorithm: Pseudocode with major steps.
o Refine each step: Replace abstractions with concrete procedures (e.g., “validate
input” → “check type, range, format, duplicates”).
o Validate at each level: Maintain correctness (pre/postconditions, invariants), and
update tests.
Strengths:
o Controlled complexity: Never jump too many levels at once.
o Evidence-driven: Each refinement is justified by constraints and tests.
o Alignment: Keeps solution tied to specification while discovering details.
Limitations:
o Time-consuming: Multiple passes require discipline.
o Requires good checkpoints: Without tests/specs, refinement can drift.
Best use cases:
o Education and reviews: Demonstrates reasoning transparently.
o Safety-critical logic: Ensures traceable, verifiable design evolution.
Choosing the method
Problem clarity:
o Top-down: When outputs and constraints are well-understood.
o Bottom-up: When tooling/components dominate feasibility.
o Step-wise refinement: When you need iterative depth and verification.
Team and assets:
o Existing libraries: Favor bottom-up to reuse.
o New domains: Favor top-down to avoid premature implementation.
o Mixed: Use step-wise refinement to reconcile high-level goals with low-level
realities.
Risk profile:
o Early requirement validation: Top-down.
o Early technical validation: Bottom-up.
o Continuous verification: Step-wise refinement.
Tip: In practice, you’ll interleave methods—start top-down to structure the problem, implement
critical modules bottom-up to de-risk, and iterate via step-wise refinement to converge on a
correct, efficient design.
Representing solutions using hierarchy charts
A hierarchy chart (also called a structure chart) shows the system’s modules as boxes and their
decomposition from general to specific. It’s not a flowchart—there are no control arrows or
loops—just the “is composed of” relationships.
Elements and conventions
Modules: Rectangular boxes labeled with action-oriented names (verb + noun, e.g.,
“Compute Average”).
Levels: Higher levels represent broader responsibilities; lower levels show finer-grained
tasks.
Decomposition lines: Vertical arrangement indicates “contains” or “calls”; horizontal
groups indicate siblings.
No control flow: Use hierarchy charts to show structure, not sequence or conditions.
Example (top-down decomposition for “Process Grades”)
Interpretation:
o Process Grades is the top module.
o It decomposes into four submodules; each submodule further decomposes as
needed.
o This chart documents responsibilities and boundaries; pair it with
pseudocode/flowcharts for control details.
Building a hierarchy chart from step-wise refinement
Start with top module: The overall function name.
Add submodules as you refine: Each abstract step becomes a child module.
Stop when atomic: When a box’s behavior can be specified in a few precise steps or
implemented as a single function.
Cross-check: Ensure each module has a clear contract (inputs/outputs), minimal
coupling, and cohesion (does one thing well).
Worked mini-example across methods
Problem (brief): “Given a stream of numbers, compute sum, count, average; stop on −1;
report results.”
Top-down outline:
o Process Stream → Acquire → Validate → Aggregate → Report.
Bottom-up components:
o readNumber(), isTerminator(x), isValid(x), addToAggregate(state, x),
formatReport(state).
Step-wise refinement:
o Aggregate: Maintain invariants: sum ≥ 0, count ≥ 0; update on each valid input;
average = sum / count when count > 0; define edge cases (empty stream →
average “N/A”).
Hierarchy chart (structure only):
This shows how each method frames the same problem, and how the hierarchy chart captures
structure without control flow.