What types of algorithms exist by structure?
Short answer
- Linear (sequential) - steps execute strictly one after another.
- Branching - one of the execution branches is chosen based on a condition.
- Iterative (looping) - a block of actions repeats while a condition holds or over a given range.
- Recursive - the algorithm calls itself until a base case is reached (more often viewed as an organizational technique, but singled out separately).
- Combined - combine the listed control structures within a single solution.
In detail
Structured programming distinguishes three basic control structures: sequence, branching, and loops. Recursion is a key technique that is often considered separately. Real-world algorithms usually combine several structures.
1) Linear (sequential)
Execute step by step, without branching or repetition. Applicable when the order of actions is fixed and does not depend on conditions.
- Advantages: simplicity, predictability, easy verifiability.
- When to use: formatting data, computing aggregates, preparing values before validation.
- Typical mistakes: extra steps, duplicated code, missing handling of edge cases.
function normalizeName(name) {
return name.trim().toLowerCase().replace(/\s+/g, ' ');
}
function avg(nums) {
if (!nums.length) return 0;
let sum = 0;
for (const n of nums) sum += n;
return sum / nums.length;
}
const raw = " Alice Bob ";
const normalized = normalizeName(raw);
const mean = avg([10, 20, 30]);
console.log({ normalized, mean: mean.toFixed(2) });2) Branching
Choosing one of the alternatives based on a condition. Main constructs: if/else, switch/case, the ternary operator.
- if / else - a universal choice based on conditions.
- switch - convenient for discrete values.
- The ternary ? : - for simple conditional assignments.
function grade(score) {
if (score < 0 || score > 100) return "invalid"; // guard case
if (score >= 90) return "A";
if (score >= 75) return "B";
if (score >= 60) return "C";
if (score >= 40) return "D";
return "F";
}
console.log([95, 76, 12, 101].map(grade));- Watch for completeness of conditions (handle edge cases).
- Order checks from more strict to less strict (or the other way, but consistently).
- Avoid if-else "staircases" when a lookup table/map can replace them.
3) Iterative (looping)
Executing a block of code multiple times. Kinds: for, while, do...while, as well as iterable forms (for...of) and higher-order methods (forEach, map) - they logically implement repetition too.
- for - a known number of iterations or iterating over a collection.
- while - while a condition is true; do...while - at least one iteration.
- break/continue - controlling the flow inside a loop.
function sumUntilLimit(arr, limit) {
let sum = 0;
for (const n of arr) {
if (sum + n > limit) break; // early exit
sum += n;
}
return sum;
}
console.log(sumUntilLimit([5, 3, 8, 2], 10)); // 8- Define an invariant - what remains true on every iteration.
- Watch how counters/conditions change to avoid infinite loops.
- Use early exits (break/return) to avoid unnecessary iterations.
Recursive algorithms
Recursion is a way of solving problems by breaking them into subproblems of the same type with a base case. It is especially convenient for trees, graphs, and divide-and-conquer splits.
const tree = {
value: 1,
children: [
{ value: 2, children: [] },
{ value: 3, children: [ { value: 4, children: [] } ] }
]
};
function dfs(node, visit) {
if (!node) return; // base case
visit(node.value);
for (const child of node.children || []) {
dfs(child, visit); // recursive step
}
}
dfs(tree, v => console.log(v)); // 1, 2, 3, 4- Be sure to define a base case and progress toward it, otherwise you get a stack overflow.
- Tail recursion can be optimized by a compiler/engine, but not everywhere (in JS - there is no guarantee).
- An iterative version using an explicit stack/queue often saves the call stack and gives better control over memory.
function dfsIter(root, visit) {
if (!root) return;
const stack = [root];
while (stack.length) {
const node = stack.pop();
visit(node.value);
const children = node.children || [];
for (let i = children.length - 1; i >= 0; i--) {
stack.push(children[i]);
}
}
}
dfsIter(tree, v => console.log(v)); // 1, 2, 3, 4Combined algorithms
In practice, algorithms usually combine sequential steps, branching, and loops. Example: form validation - a linear pass over the fields (a loop) with rule checks (branching) and preprocessing (a sequence).
function validateForm(fields) {
const errors = [];
for (const f of fields) { // loop
const value = String(f.value ?? "").trim(); // linear preprocessing
if (f.required && value === "") { // branching
errors.push(`${f.name}: required`);
continue;
}
if (f.type === "email") {
const ok = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
if (!ok) errors.push(`${f.name}: invalid email`);
} else if (f.type === "age") {
const age = Number(value);
if (!Number.isInteger(age) || age < 0 || age > 120) {
errors.push(`${f.name}: invalid age`);
}
}
}
return errors;
}
console.log(
validateForm([
{ name: "email", type: "email", required: true, value: " user@example.com " },
{ name: "age", type: "age", required: false, value: "200" }
])
);Quick summary table
| Type | Key idea | JS constructs | Where used |
|---|---|---|---|
| Linear | A sequence of steps | Sequential expressions | Formatting, aggregation |
| Branching | Choosing a branch by condition | if/else, switch, ?: | Validation, routing |
| Iterative | Repeating until a condition/over a range | for, while, do...while, for...of | Iterating collections, search, aggregation |
| Recursive | Self-application to subproblems | Functions that call themselves | Trees, graphs, divide and conquer |
| Combined | Mixing structures | A combination of the above | Real-world applications and services |
What you might be asked in an interview
- Give examples of a linear, a branching, and a looping algorithm, and their time/memory complexity.
- When is recursion preferable to iteration, and vice versa? How to rewrite recursive code iteratively.
- Draw a flowchart of an algorithm with branching and loops for a given problem.
- Where would you add early exits (guard clauses), and why does that simplify the code.
Tip: before writing code, sketch pseudocode, note the base cases and invariants - this reduces the number of mistakes.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.