iq.lab
Python sleeps until you run code
01 Python, one step at a time

Lesson 2 of 5

Walking a list

A for loop visits every item in a list, one at a time. Keep one or two values in a notebook as you go, and running totals, counts and maximums all turn out to be the same small program.

About 55 minutes
By the end you can
  • Trace a for loop over a list and say the value of every name after each pass

  • Write a loop that keeps a running total, a count or a running maximum

  • Pick a safe starting value for a running maximum

  • Loop over positions with range and enumerate when you need the index

  • Use a while loop when the walk does not move one item at a time

You have a week of daily order counts: [12, 30, 7, 25]. Which day was busiest? In SQL you would write SELECT MAX(orders), and in Python you could call max(orders). Both are fine at work.

In an interview, the question is almost never the plain maximum. It is the busiest day, or the biggest jump between two days, or the longest streak of busy days. No built-in function answers those. What answers them is a short loop that walks the list and keeps a few notes, and that loop is the most important program in this course. Every pattern later on, from hash maps to dynamic programming, is this same loop with a smarter notebook.

So we start here, slowly, and we watch every step.

One item at a time

A for loop takes the items of a list one at a time, in order, and runs the indented lines once for each. Here is the smallest version. The trace below shows the code on one side and every name and value on the other. Press Next to move one line at a time.

Step throughVisiting every item
Step 1 of 12
Line 1 is about to run.
orders = [12, 30, 7, 25]
for x in orders:
print(x)
print("done")
Names
no names yet
Output
(nothing printed yet)

Three things to notice, because they cause most loop confusion:

  • x is a name, not a box in the list. Each pass, Python points x at the next item. Writing x = 0 inside the loop would change what x names, not the list.
  • The body is whatever is indented under the for line. Line 4 is not indented, so it runs once, after the loop.
  • The loop decides when to stop. You do not count; it runs out of items.

Before you run the next one, predict it.

PredictTwice each item

Read the loop and type exactly what it prints, one value per line.

nums = [3, 1, 4]
for x in nums:
    print(x * 2)
print("end")

A notebook that remembers

Printing each item is not useful by itself. The useful loops remember something as they go. Total orders for the week: we need one number that grows as we walk.

Step throughA running total
Step 1 of 13
Line 1 is about to run.
orders = [12, 30, 7, 25]
total = 0
for x in orders:
total = total + x
print(total)
Names
no names yet
Output
(nothing printed yet)

Read line 4 the way Python does: compute the right side first, then point the name on the left at the answer. total = total + x is not an equation (it would be false); it is an instruction: "the new total is the old total plus x". Python has a shorthand for exactly this, total += x, and you will see both.

The idea that makes loops easy to get right is the promise. At the end of every pass, something is true about the notebook. Here:

Check it against the trace: after two passes, total is 42, and the items visited so far are 12 and 30. When the loop ends, the items visited so far are all of them, so the promise turns into the answer. You will use this argument for every algorithm in this course.

Work it outThe notebook mid-walk

In the running-total trace above (orders = [12, 30, 7, 25], total starting at 0), what is total right after the third pass of the loop body has run? Type a whole number.

total

Type a number: 0.25, -2, 3/4 and sqrt(2) all work. Enter checks.

Counting is the same loop with a condition. How many days had more than 20 orders? The notebook is a count, and it only grows when the item passes the test:

count = 0
for x in orders:
    if x > 20:
        count += 1

The promise: after each pass, count is how many visited items were above 20. With [12, 30, 7, 25] that ends at 2.

Now put a counting loop together yourself. The lines are all here, in the wrong order.

Put in orderCount the even numbers

Put the lines in order to build count_evens(nums), which returns how many numbers in nums are even. Indentation is already part of each line. Two of the lines do not belong.

A number is even when dividing it by 2 leaves no remainder: x % 2 == 0.

Your program

Add lines from the list below, in order.

    Unused lines (some do not belong)
    • if x % 2 == 0:
    • count = 0
    • count += 1
    • return count
    • count = 0
    • for x in nums:
    • def count_evens(nums):
    • return count

    The running maximum

    Back to the question we started with: the busiest day. The notebook is one number, best, the largest count seen so far. Each pass asks one question: is this item bigger than the best so far? If yes, it becomes the new best.

    Before you step through, guess: how many times does best change on [3, 8, 2, 9, 4]?

    Step throughFinding the largest number
    Step 1 of 18
    Line 1 is about to run.
    def largest(nums):
    best = nums[0]
    for x in nums:
    if x > best:
    best = x
    return best
    Names
    no names yet
    Output
    (nothing printed yet)

    Here is the whole idea in the four questions this course asks of every algorithm:

    Why start at nums[0]? Because the promise must be true before the loop starts, and the only number we can vouch for at that moment is the first. It is tempting to start at 0. That works for order counts, which are never negative, and fails quietly for anything else:

    Quick checkWhere should best start?

    This version starts the notebook at 0:

    def largest(nums):
        best = 0
        for x in nums:
            if x > best:
                best = x
        return best
    

    What does largest([-5, -2, -9]) return?

    Choose one answer, then check.

    One more question an interviewer wants you to ask out loud: what if the list is empty? nums[0] raises an error on []. There is no right answer in the abstract; you ask ("can the list be empty, and what should I return then?") and handle it with one if at the top. Asking is part of the score.

    Now write the mirror image yourself.

    Code itThe smallest number

    Write smallest(nums), which returns the smallest number in a non-empty list. Use a loop, not the built-in min: the point is the walk. Then press Watch it run to see your notebook change.

    ⌘+Enter runsPython sleeps until you run code
    Write your code where the starter says raise NotImplementedError, then press Run tests. Each check says what it expects.

    When you need the position

    Often the question is not the largest value but where it is: which day was busiest? for x in nums hands you the items but not their positions. Two tools give you positions.

    range(n) produces the whole numbers 0, 1, ..., n − 1. So range(len(nums)) produces every valid position of nums, and nums[i] reads the item there. Notice that range stops before n: range(4) is 0, 1, 2, 3, which is exactly the positions of a four-item list.

    Step throughThe busiest day, by position
    Step 1 of 14
    Line 1 is about to run.
    orders = [12, 30, 7, 25]
    best_day = 0
    for i in range(len(orders)):
    if orders[i] > orders[best_day]:
    best_day = i
    print(best_day)
    Names
    no names yet
    Output
    (nothing printed yet)

    Positions start at 0. "The second day" is position 1. Keep that translation in your head; off-by-one mistakes come from forgetting it.

    enumerate gives you both at once. for i, x in enumerate(orders) gives i = 0, x = 12, then i = 1, x = 30, and so on. Use it when you need the position and the item together; it reads better than orders[i] everywhere. (The i, x on the left is unpacking two values into two names; lesson 4 shows how it works.)

    Positions also let you look at neighbors. The biggest jump from one day to the next compares orders[i] with orders[i - 1], so the walk must start at position 1, not 0: range(1, len(orders)). range(a, b) starts at a and stops before b.

    PredictPositions and neighbors

    Type exactly what this prints. range(1, 4) starts at 1 and stops before 4.

    orders = [12, 30, 7, 25]
    for i in range(1, 4):
        print(i, orders[i] - orders[i - 1])
    
    Code itThe biggest jump

    Write biggest_jump(orders), which returns the largest increase from one day to the next: the largest value of orders[i] - orders[i - 1]. The list has at least two days. If orders only ever fall, the answer is the least-bad change, which is negative.

    ⌘+Enter runsPython sleeps until you run code
    Write your code where the starter says raise NotImplementedError, then press Run tests. Each check says what it expects.

    Here is a full problem that uses a running total and builds a new list as it walks. Try it before you open the hints.

    easyLoops and basicsPrefix sums target 15 min

    Running sum

    Given a list of numbers nums, return a new list of the same length where the item at position i is the sum of nums[0] through nums[i].

    Example 1
    Inputnums = [1, 2, 3, 4]Output[1, 3, 6, 10]

    1, then 1 + 2, then 1 + 2 + 3, then 1 + 2 + 3 + 4.

    Example 2
    Inputnums = [3, -1, 2]Output[3, 2, 4]
    Constraints
    • 0 ≤ len(nums) ≤ 10^5

    • -10^6 ≤ nums[i] ≤ 10^6

    ⌘+Enter runs 0:00Python sleeps until you run code
    Run examples checks the examples. Submit runs every test, including edge cases and a large input.

    While loops: walking at your own pace

    A for loop moves exactly one item per pass. A while loop moves whenever and however you tell it to. It checks a condition before every pass and stops the first time the condition is false.

    Use it when the walk is not "every item once": stopping as soon as you find something, or moving two pointers at different speeds (module 4). Here we find the first day with more than 20 orders and stop there:

    Step throughStop at the first busy day
    Step 1 of 9
    Line 1 is about to run.
    orders = [12, 7, 30, 25]
    i = 0
    while i < len(orders) and orders[i] <= 20:
    i += 1
    print(i)
    Names
    no names yet
    Output
    (nothing printed yet)

    The order inside the condition matters. i < len(orders) is checked first, and and stops as soon as one side is false. If no day were busy, i would reach 4, the first check would fail, and Python would never try orders[4], which does not exist.

    The last problem needs two notebook values at once: the length of the current streak, and the best streak so far. It is a classic warm-up, and warm-ups matter: the research on these rounds says a fast, clean first problem banks time for the harder follow-up.

    easyLoops and basics target 15 min

    Longest run of ones

    A list bits holds only 0s and 1s. Return the length of the longest unbroken run of 1s.

    Example 1
    Inputbits = [1, 1, 0, 1, 1, 1]Output3

    The last three items are a run of three 1s.

    Example 2
    Inputbits = [1, 0, 1, 1, 0, 1]Output2
    Constraints
    • 1 ≤ len(bits) ≤ 10^5

    • Every item is 0 or 1.

    ⌘+Enter runs 0:00Python sleeps until you run code
    Run examples checks the examples. Submit runs every test, including edge cases and a large input.

    Last, practice saying it. The words you write here are the words you will say in the room.

    Say itSay it like you would in the room

    Explain, as you would to an interviewer before typing, how you would count the days with more than 20 orders. Cover the walk, the notebook, the promise and the cost in a few plain sentences.

    A few sentences first: 0 of 60 characters.

    Saved in this browser as you type.

    Next: Decisions and functions