5.5 Software Engineering & Algorithms
Key Takeaways
- Big-O describes worst-case growth: binary search is O(log n), a linear scan is O(n), and a simple nested-loop sort is O(n^2).
- Arrays give O(1) indexed access but O(n) middle insertion; linked lists give O(1) insertion at a known node but O(n) search.
- Stacks are last-in-first-out (LIFO); queues are first-in-first-out (FIFO).
- Binary search requires a sorted array and halves the search space each step, reaching O(log n) comparisons.
- Waterfall moves linearly through SDLC phases; Agile delivers working software in short iterative sprints.
Data structures
A data structure organizes data so a program can access and modify it efficiently; the FE expects you to know each one's access trade-offs.
- Array: contiguous, fixed-size; O(1) access by index, but inserting or deleting in the middle is O(n) because elements shift.
- Linked list: nodes joined by pointers; O(1) insertion or deletion at a known node, but O(n) to search because you must traverse.
- Stack: LIFO (last-in, first-out); push and pop at one end. Used for function-call management (the call stack) and expression evaluation.
- Queue: FIFO (first-in, first-out); enqueue at the rear, dequeue at the front. Used for buffering and scheduling.
- Hash table: maps keys to values with average O(1) lookup via a hash function; collisions degrade it toward O(n).
- Tree: hierarchical; a balanced binary search tree supports O(log n) search, insert, and delete, while a graph models arbitrary relationships.
The FE tests you on choosing the right structure for an access pattern: use a stack to reverse order or to evaluate nested expressions and undo operations, a queue for first-come-first-served scheduling and buffering, a hash table when you need fast keyed lookup, and a tree when you need ordered data with fast search. Picking the wrong structure turns an O(1) or O(log n) operation into an O(n) one - the usual point of the question.
Big-O complexity
Big-O notation expresses how an algorithm's running time or memory grows as the input size n grows, keeping only the dominant term and dropping constants. It is the worst-case asymptotic bound - what matters when inputs get large. The ordering from best to worst is:
O(1) < O(log n) < O(n) < O(n log n) < O(n^2) < O(2^n) < O(n!)
O(1) does not depend on n, O(log n) halves the problem each step, O(n) touches each element once, O(n log n) is the bound of efficient comparison sorts, and O(n^2) appears in nested loops over the same data.
Worked trace: a doubly nested loop, for i in 1..n { for j in 1..n {...} }, runs the inner body n x n = n^2 times, so it is O(n^2); a single loop that halves a counter each pass (i = i/2) runs about log2(n) times, so it is O(log n).
Big-O of common operations
| Algorithm / Operation | Average Big-O | Notes |
|---|---|---|
| Array access by index | O(1) | Direct addressing |
| Linear search | O(n) | Unsorted data |
| Binary search | O(log n) | Requires sorted data |
| Bubble / insertion / selection sort | O(n^2) | Simple, nested loops |
| Merge sort / quicksort | O(n log n) | Efficient comparison sorts |
| Hash-table lookup | O(1) | Average; O(n) worst case |
| Balanced BST search | O(log n) | Self-balancing tree |
Quicksort degrades to O(n^2) in its worst case (already-sorted input with poor pivots), while merge sort guarantees O(n log n) and is stable - a frequent exam nuance.
What is the worst-case time complexity of performing binary search on a sorted array of n elements?
Control flow, pseudocode, and algorithms
Program logic is built from three control-flow structures: sequence (statements in order), selection (if/else, switch), and iteration (for, while loops). Any algorithm can be expressed with these three - the structured-programming theorem. Engineers express logic before coding with pseudocode (language-neutral English-like steps) and flowcharts, where a diamond is a decision, a rectangle is a process, and a parallelogram is input/output.
Two algorithms recur on the FE:
- Binary search: repeatedly compare the target to the middle element of a sorted array, discarding the half that cannot contain it - O(log n).
- Sorting: simple sorts (bubble, insertion, selection) are O(n^2) and easy to hand-trace; efficient sorts (merge, quick) are O(n log n).
Be ready to hand-trace a short loop and count how many times the body executes, and to read a flowchart and predict its output.
The software development lifecycle
The software development lifecycle (SDLC) is the sequence of phases a project moves through: requirements, design, implementation, testing, deployment, and maintenance. The FE asks you to distinguish the main process models:
- Waterfall: strictly sequential phases; each finishes before the next begins. Simple and well-documented, but inflexible to late changes.
- Agile: iterative and incremental; small cross-functional teams deliver working software in short sprints and welcome changing requirements (Scrum and Kanban are Agile frameworks).
- Spiral: risk-driven, repeating waterfall-like cycles with explicit risk analysis each loop.
- V-model: extends waterfall by pairing each development phase with a corresponding test phase.
Supporting practices include version control (such as Git) for tracking changes, and testing levels from unit (one module) to integration (modules together) to system (the whole product) to acceptance (does it meet user requirements).
A useful distinction is verification ("are we building the product right?" - inspections, unit/integration tests that confirm the software meets its specification) versus validation ("are we building the right product?" - acceptance testing that confirms it meets the user's actual need). The FE also contrasts black-box testing (behavior only, no code knowledge) with white-box testing (exercises internal code paths and branches). Fixing a defect grows more expensive the later it is found, which is the economic argument for early testing and iterative delivery.
Which SDLC model is best characterized by strictly sequential phases where each phase must be completed before the next begins?