Sonith Manakkat

Algorithms, part 7 of 129 min read12 visuals

Greedy, divide and conquer, and backtracking

Three classic problem-solving styles, grab the best now, split the job, or try and back up, with everyday pictures, when each works, when it fails, and how to choose.

  • Greedy is fast but only correct when local choices add up to the best overall
  • Divide and conquer splits a problem, solves the pieces and combines them
  • Backtracking tries choices and undoes them, pruning dead ends early

Share on LinkedIn

Share
Reading level

Earlier parts showed individual algorithms. This part steps back to three styles of designing one, each with a different attitude to making choices. Greedy commits to whatever looks best right now. Divide and conquer splits the problem into smaller copies. Backtracking tries a choice, and if it leads nowhere, undoes it and tries another. Knowing which style fits a problem is half of solving it. Each section has a plain picture and an Expert view.

Three styles at a glance

  • GreedyAlways grab the nearest tasty thing on the table
  • Divide and conquerSplit a big job among helpers, then combine their work
  • BacktrackingIn a maze, try a path, back up at a dead end, try another
Three attitudes to a big problem.
Expert viewA note on how they relate

Dynamic programming (part 6) can be seen as a fourth style that sits between these: like divide and conquer it splits problems, but it stores overlapping answers. Backtracking is a systematic search of a tree of choices, closely related to depth-first search from part 5. Many problems can be attacked with more than one style, and the styles differ in what they guarantee: greedy gives a guarantee only when a special property holds, while backtracking can be made to find an answer or prove none exists, at a possibly large cost.

  1. 1Is a local best choice provably safe?Yes Greedy: fast and simpleNo keep going
  2. 2Do small pieces repeat and combine into the answer?Yes Dynamic programming: store and reuse answersNo keep going
  3. 3Can the problem split into independent pieces?Yes Divide and conquer: solve the pieces, then combineNo keep going
  4. 4OtherwiseBacktracking: try choices, undo dead ends and prune early

Takeaway. Check for a safe greedy rule first, and fall back to search only when nothing cheaper fits.

Choosing a style: ask the questions in order (a rule of thumb, not a proof).

Greedy: take the best choice now

  1. List the choicesWhat can I pick next?
  2. Pick the best-lookingBy some fixed rule
  3. Never undo itNo second thoughts
  4. RepeatUntil the job is done
Greedy in four steps.

Greedy is fast and simple, but it is only correct when local best choices add up to a global best. That depends on the problem.

Works

  • Standard coin sets: biggest coin first gives the fewest coins
  • Activity selection: always take the activity that finishes earliest

Fails

  • Coins 1, 3 and 4 for amount 6
  • Greedy: 4 + 1 + 1 is three coins, but 3 + 3 is two
Greedy working and failing.
Expert viewWhen can you trust greedy?

Two properties are usually proved: the greedy-choice property (some optimal solution begins with the greedy choice) and optimal substructure (after that choice, the rest is a smaller problem of the same type). The standard method is an exchange argument: take any optimal solution and show you can swap in the greedy choice without making it worse. Well-known correct greedy algorithms include activity selection by earliest finish, Huffman coding, and Dijkstra's algorithm for non-negative weights (part 5). Without such a proof, assume greedy may fail and test it on small cases against a slower exhaustive method.

Divide and conquer: split the job

DivideConquer thepiecesCombineWhole answer
Three steps in every divide and conquer algorithm.
52831647Start: split the row in halves again and again untilsingle bars.25831647Merge positions 1 to 2: two sorted halves become onesorted run.25381647Merge positions 3 to 4: two sorted halves become onesorted run.23581647Merge positions 1 to 4: two sorted halves become onesorted run.23581647Merge positions 5 to 6: two sorted halves become onesorted run.23581647Merge positions 7 to 8: two sorted halves become onesorted run.23581467Merge positions 5 to 8: two sorted halves become onesorted run.12345678Merge positions 1 to 8: two sorted halves become onesorted run.12345678Sorted: the last merge joined the two big halves.
Merge sort is divide and conquer: split in halves, solve each half, then merge.
Show the 9 steps as text
  1. Start: split the row in halves again and again until single bars.
  2. Merge positions 1 to 2: two sorted halves become one sorted run.
  3. Merge positions 3 to 4: two sorted halves become one sorted run.
  4. Merge positions 1 to 4: two sorted halves become one sorted run.
  5. Merge positions 5 to 6: two sorted halves become one sorted run.
  6. Merge positions 7 to 8: two sorted halves become one sorted run.
  7. Merge positions 5 to 8: two sorted halves become one sorted run.
  8. Merge positions 1 to 8: two sorted halves become one sorted run.
  9. Sorted: the last merge joined the two big halves.

Binary search (part 4) is another example, with a twist: it splits the sorted list in half, throws one half away, and continues on the other.

Expert viewThe recurrence, in words

The cost of such an algorithm is described by a recurrence. Merge sort sorts two halves, each half the size, and then does work proportional to n to merge. In words: the time for n items equals twice the time for n over 2 items, plus about n. That solves to O(n log n), because there are about log n levels of halving and each level does about n work in total. Binary search does one half-sized piece plus a constant amount of work: the time for n equals the time for n over 2, plus a constant, which solves to O(log n). The master theorem is a general rule that solves many recurrences of this shape. Divide and conquer fits best when the pieces are independent, which also makes them easy to run in parallel. If pieces overlap heavily, dynamic programming is usually the better tool.

Backtracking: try, undo, try again

  1. ChoosePick an option for the next step
  2. ExploreCarry on from there
  3. Dead end?Stop and undo the last choice
  4. Try the next optionUntil a solution is found or options run out
Backtracking in a maze or puzzle.
ChooseExploreDead endSolution1234
  1. Choose to Explore Pick an option for the next step and carry on from there
  2. Explore to Dead end The next step has no allowed option, so this path is stuck
  3. Dead end to Choose Undo the last choice and go back to choosing
  4. Explore to Solution Every step has a valid option: a solution

Takeaway. Undoing a failed choice and trying the next option is the whole trick.

Backtracking as moves between states: try a choice, check it, undo it if it fails.

The classic example is the N-queens puzzle: place N chess queens on an N by N board so that none attacks another (no two share a row, column or diagonal). Take the 4 by 4 board and place one queen per row, going down.

A dead end

  • Row 1 column 1, row 2 column 3, then row 3 has no safe square
  • Back up and try column 4 in row 2; that fails too, so row 1 must move

A solution

  • Row 1 column 2, row 2 column 4, row 3 column 1, row 4 column 3
  • No queen attacks another
A 4 by 4 attempt, in words.
Myth

Backtracking just tries every possibility

Fact

Pruning skips whole groups of choices as soon as they cannot work

A common mix-up.
Expert viewPruning and cost

Backtracking explores a tree of partial solutions, and pruning cuts a branch as soon as a constraint is broken. In the worst case the search can still be exponential, because pruning only helps as much as the constraints let it; the 4 by 4 board has just two solutions, but the search effort grows very quickly with N. Good practice is to check constraints as early as possible, choose the most constrained variable next (the idea behind many sudoku solvers), and keep fast data structures for "is this square attacked?" checks. Sudoku is the same pattern: fill a cell with a digit not already in its row, column or box; if a later cell has no legal digit, back up. Backtracking suits constraint puzzles, generating combinations and permutations, and finding any or all solutions.

Choosing between them

StyleIdeaGood whenWatch out for
GreedyTake the best-looking choice, never undoA proven greedy property holdsCan be wrong without a proof
Divide and conquerSplit, solve pieces, combinePieces are independent and combining is cheapOverlapping pieces repeat work
Dynamic programmingSplit, store overlapping answersOverlapping subproblems and optimal substructureTable memory
BacktrackingTry, undo, try againConstraint puzzles and "find all solutions"Possible exponential time

Try it by hand

  1. Draw a 4 by 4 gridUse paper and coins for queens
  2. Row 1Put a queen in column 1
  3. Row 2Find a safe column; mark squares a queen attacks
  4. Hit a dead endIf a row has no safe square, lift the last queen
  5. Move it onTry the next column in that row, and carry on
  6. Count solutionsTry every start; there are two solutions
Place 4 queens on a 4 by 4 board with trial and backtracking.

Greedy never looks back, divide and conquer shares the load, backtracking is allowed to say "oops".

Frequently asked questions

How do I know whether greedy works?

You need an argument or proof for the problem. A good habit is to compare greedy with an exhaustive method on small cases and look for a counter-example.

Is backtracking just brute force?

It is a smarter search: it abandons partial solutions early. In the worst case it can still be very slow.

Is merge sort the only divide and conquer algorithm?

No. Binary search, quick sort and fast Fourier transforms are other well-known examples.

Can I mix these styles?

Yes. Many practical solutions combine them, for example backtracking with greedy ordering of choices to find solutions sooner.

Back to all articles