Chapter 1: Introduction to Data Structures and Algorithms
1.02Beginner9 topics16 min1 interactive animation

Algorithm fundamentals

An algorithm is a finite, unambiguous procedure with defined inputs and outputs. This lesson lists the properties every algorithm must have and teaches the two paper tools you use to understand one: the dry run and the tracing table.

Learning goals

  • List the properties that make a procedure an algorithm
  • State preconditions and postconditions for a method
  • Read and write pseudocode
  • Trace code by hand with a tracing table

Prerequisites

Loops and conditions
1.02.1

Algorithm properties

A procedure counts as an algorithm when it has: input (zero or more values), output (at least one result), finiteness (it stops), definiteness (every step is precise) and effectiveness (every step can be done by a machine in finite time).

“Make the best coffee” is not an algorithm: the steps are not definite. “Boil 200 ml of water, add 10 g of grounds, wait 4 minutes, pour” is.

PropertyQuestion it answers
InputWhat does it receive?
OutputWhat does it produce?
FinitenessDoes it always stop?
DefinitenessIs every step unambiguous?
DeterminismDoes the same input give the same steps and result?
EffectivenessCan each step actually be executed?
1.02.2

Finiteness

An algorithm must terminate on every valid input. Loops need a measure that moves toward the exit condition on every iteration, such as an index that grows toward n or a value that shrinks toward 0.

A common bug is updating the wrong variable, or updating it only on some paths, so the loop never ends.

Java
1// terminates: i grows toward n on every iteration
2for (int i = 0; i < n; i++) { work(); }
3
4// terminates: n halves each time and is an int, so it reaches 0
5while (n > 0) { n /= 2; }
6
7// does NOT terminate when x is 0 or negative: x never reaches 10 from below
8int x = 1;
9while (x != 10) { x -= 1; } // bug: should have been x += 1
1.02.3

Determinism

A deterministic algorithm performs the same steps and returns the same result every time it is given the same input. Most algorithms in this course are deterministic, which makes them easy to test.

Randomness, threads and reading the clock introduce non-determinism. Keep such sources behind a parameter (a seed, a clock object) so that tests can control them.

Java
1Random rnd = new Random(42); // fixed seed: same sequence every run
2int a = rnd.nextInt(100);
3int b = new Random(42).nextInt(100); // a == b
4// new Random() without a seed differs on every run, so tests become flaky
1.02.4

Definiteness

Each step must have one precise meaning. “Process the items quickly” is not definite; “for each item in index order, add its price to total” is. Definiteness also means the order of steps is fixed, and every branch has a stated condition.

Typical sources of ambiguity: unclear tie-breaking (“pick the largest” when two are equal), unspecified order of iteration, and undefined behaviour for edge inputs such as an empty array.

1.02.5

Preconditions and postconditions

A precondition is what must be true when a method is called; a postcondition is what is guaranteed when it returns. If the caller breaks a precondition, the method owes nothing. If the method breaks a postcondition, the method has a bug.

In Java, check cheap preconditions with exceptions at public boundaries, and use assert for internal conditions you believe are always true.

Java
1/** Precondition: lo <= hi, arr sorted. Postcondition: returns first index with arr[i] >= target, or arr.length. */
2static int lowerBound(int[] arr, int target) {
3 Objects.requireNonNull(arr); // public boundary: throw on misuse
4 int lo = 0, hi = arr.length;
5 while (lo < hi) {
6 int mid = lo + (hi - lo) / 2;
7 if (arr[mid] < target) lo = mid + 1; else hi = mid;
8 }
9 assert lo == arr.length || arr[lo] >= target; // postcondition (run with java -ea)
10 return lo;
11}
1.02.6

Pseudocode

Pseudocode is a language-neutral outline of an algorithm: precise enough to implement, loose about syntax. Use it to think before coding and to compare the idea with the Java.

Conventions used on this site: indentation shows nesting, ← is assignment, arrays are 0-based, and return ends the procedure.

Java
1LINEAR-SEARCH(A, target)
2 for i ← 0 to length(A) - 1
3 if A[i] = target
4 return i
5 return -1
6
7// Java
8static int linearSearch(int[] a, int target) {
9 for (int i = 0; i < a.length; i++) if (a[i] == target) return i;
10 return -1;
11}
1.02.7

Flowcharts

A flowchart draws an algorithm as boxes and arrows: rounded boxes for start and end, rectangles for actions, diamonds for decisions. They are useful for explaining branching logic to non-programmers, and clumsy for loops over data, which is why this course uses animations and tracing tables instead.

Text form of the flowchart for “is n even?”: START → read n → decision [n % 2 == 0] → yes: print “even”, no: print “odd” → END.

ShapeMeaning
Rounded boxStart or end
RectangleAction or assignment
DiamondYes/no decision (two exits)
ParallelogramInput or output
ArrowOrder of execution
1.02.8

Dry runs

A dry run executes code by hand with concrete values, without a computer. It finds logic errors early and builds the habit of thinking in program states. Pick a small input, write down each variable, and update them one statement at a time.

Step through the animation below with a different array each time. Each frame shows the variables a dry run would track, the condition just evaluated, and the highlighted line.

Dry run: sum the even values

Interactive · edit the input

Your input

Try

int[] arr · length 6

3
8
-2
7
10
5
012345
Being comparedEven: addedOdd: skipped
Setup

Every variable gets a column in the tracing table below. Step through and fill in one row per statement.

Step 1 / 18
Java
1public static int sumEvens(int[] arr) {
2 int sum = 0;
3 for (int i = 0; i < arr.length; i++) {
4 if (arr[i] % 2 == 0) {
5 sum += arr[i];
6 }
7 }
8 return sum;
9}

Live state

0 iterations

sum

0

n

6

No condition evaluated on this step
Execution trace (dry run table)Show 18 steps
StepLineStateWhat happens
1L2sum=0 n=6Every variable gets a column in the tracing table below. Step through and fill in one row per statement.
2L3i=0 sum=0i = 0 is below 6, so the body runs.
3L4i=0 arr[i]=3 sum=03 % 2 is 1, not 0, so skip it. In Java the remainder keeps the sign of the dividend, so odd negatives give -1, not 1.
4L3i=1 sum=0i = 1 is below 6, so the body runs.
5L4i=1 arr[i]=8 sum=08 % 2 is 0, so 8 is even.
6L5i=1 arr[i]=8 sum=8sum becomes 8.
7L3i=2 sum=8i = 2 is below 6, so the body runs.
8L4i=2 arr[i]=-2 sum=8-2 % 2 is 0, so -2 is even.
9L5i=2 arr[i]=-2 sum=6sum becomes 6.
10L3i=3 sum=6i = 3 is below 6, so the body runs.
11L4i=3 arr[i]=7 sum=67 % 2 is 1, not 0, so skip it. In Java the remainder keeps the sign of the dividend, so odd negatives give -1, not 1.
12L3i=4 sum=6i = 4 is below 6, so the body runs.
13L4i=4 arr[i]=10 sum=610 % 2 is 0, so 10 is even.
14L5i=4 arr[i]=10 sum=16sum becomes 16.
15L3i=5 sum=16i = 5 is below 6, so the body runs.
16L4i=5 arr[i]=5 sum=165 % 2 is 1, not 0, so skip it. In Java the remainder keeps the sign of the dividend, so odd negatives give -1, not 1.
17L3i=6 sum=16i = 6 is not below 6: the loop ends.
18L8sum=16 return=16return 16. The tracing table now lists every state change, which is exactly what you would write on paper.
1.02.9

Tracing tables

A tracing table has one column per variable (and one for the condition) and one row per step. It is the written form of what the animation's trace table shows. Open “Execution trace” under the player to see every step as a row.

Dry run of sumEvens on [3, 8, -2, 7]:

Stepiarr[i]arr[i] % 2 == 0sum
start---0
103false0
218true8
32-2true6
437false6
end4-i < 4 is false6

Practice ladder

Now try it yourself

Easy

  • Write pseudocode for finding the maximum of an array
  • Dry-run a loop that sums 1 to 5 in a tracing table

Medium

  • Find the bug that makes while (x != 10) x -= 1 never end, and fix it
  • Write pre- and postconditions for a method that removes duplicates from a sorted array

Advanced

  • Write a loop whose termination depends on a non-trivial measure (for example Euclid's gcd) and prove it ends

Key takeaways

  • An algorithm needs input, output, finiteness, definiteness and effectiveness.
  • Preconditions say what callers must provide; postconditions say what the method guarantees.
  • Dry runs and tracing tables turn code into a sequence of states you can check.