Flowcharts in C — practice with the band’s streak, a palindrome number and the week’s average (flowchart first, code second)

My activity band congratulated me one Tuesday on a «3-day streak» and I wanted to check it with my own program. My first version went through the weeks separately, with one loop for each, and reset the streak to 0 at the start of the second week. Result: I got a streak of 2. The band was right: Sunday, Monday and Tuesday are consecutive days, even if they fall in different weeks. The bug was not in the code but in the flowchart I never drew: had I drawn it, I would have seen that the «streak = 0» at the change of week did not come from any diamond.

In the flowcharts theory we saw the symbols, the two-diamond template and where i = i + 1 goes. Here we use it in three medium-level programs, always in the same order: flowchart first, C second. We use the band data from the theory, with its weak days and its ties, because that is exactly where easy flowcharts break: a streak that crosses the change of week, a number read as text and an average that forces you to go through the array twice.

Before you start

It helps to be clear about:

  • The five symbols and the diamond rules (two exits, Yes and No, and the branches meet again).
  • How a for is taken apart in a flowchart: initialisation before, condition in the diamond, i = i + 1 at the end.
  • The two-diamond template (loop on top, if below).
  • That && only looks at the second condition if the first is true.

If any of these sounds shaky, go over the theory before carrying on.

Programs 2 and 3 read from the keyboard with scanf. Compile them with gcc -Wall program.c -o program and run them with ./program; type the data when asked and press Enter. In the article you will see the output together with what was typed.

Program 1: the longest streak

The band stores the steps of two weeks (14 days). We want the longest streak of consecutive days reaching the 10,000-step goal and the day it starts on. What makes it tricky: a streak can cross the change of week, there may be two streaks of the same length, and you have to remember where the best one started once it has already ended.

The idea

We go through the 14 days in a single loop (so Sunday and the following Monday are consecutive). We keep three variables: streak (how many days in a row up to today), best (the longest streak seen) and best_start (where it started). For each day:

  • If it reaches the goal, the streak goes up and, if it beats the best, we save it.
  • If it does not, the streak goes back to 0.

It is an if / else with another if inside the Yes branch. This is how it looks drawn:

Flowchart of the longest streakLoop over the 14 days. If steps[i] >= GOAL, the streak goes up and, if it beats the best one, best and best_start are saved; if not, the streak goes back to 0. The branches meet, the day and streak are shown, i = i + 1 and loop back. After the loop, the best streak and where it starts are shown.STARTstreak = 0, best = 0best_start = 0, i = 0i < N?Yessteps[i] >= GOAL?Yesstreak = streak + 1streak > best?Yesbest = streakbest_start = i – streak + 1NoNostreak = 0Show week, day,steps and streaki = i + 1loop backNoShow bestand best_startEND
An if inside another: the «streak > best» diamond is only asked on days that reach the goal.

Before programming, think about where streak > best? is asked. It only makes sense on days that add to the streak: that is why it goes inside the Yes, not after the meeting point.

The code

#include <stdio.h>

#define N 14       // two weeks of data
#define GOAL 10000

int main(void) {
    int steps[N] = {6420, 11250, 8900, 12030, 4100, 9870, 10500,
                    10200, 11800, 7300, 10050, 12400, 13100, 9100};
    const char *days[7] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
    int streak = 0;       // days in a row reaching the goal (up to today)
    int best = 0;         // the longest streak seen so far
    int best_start = 0;   // position of the first day of that streak
    int i;

    for (i = 0; i < N; i++) {
        if (steps[i] >= GOAL) {
            streak = streak + 1;             // the streak goes on
            if (streak > best) {             // > and not >=: in a tie the first streak wins
                best = streak;
                best_start = i - streak + 1;   // today's position less the length of the streak
            }
        } else {
            streak = 0;                      // a weak day breaks the streak
        }
        // i % 7 gives the day of the week (0 = Monday) and i / 7 + 1, the week
        printf("Week %d, %s: %d steps -> streak %d\n", i / 7 + 1, days[i % 7], steps[i], streak);
    }

    printf("Longest streak: %d days, starts on %s of week %d\n",
           best, days[best_start % 7], best_start / 7 + 1);
    return 0;
}

The hard part is the heart of the loop. Let’s look at it on its own:

if (steps[i] >= GOAL) {
    streak = streak + 1;             // the streak goes on
    if (streak > best) {             // > and not >=: in a tie the first streak wins
        best = streak;
        best_start = i - streak + 1;   // today's position less the length of the streak
    }
} else {
    streak = 0;                      // a weak day breaks the streak
}
  • streak = streak + 1 comes first: today counts for the streak before comparing it with the best.
  • if (streak > best) uses > and not >=. With >=, a streak that ties with the best would replace it, and the program would say it starts with the latest one. With >, in a tie the first one wins.
  • best_start = i - streak + 1: if today is day i and we have streak days in a row, the first one was streak − 1 days earlier. For example, at i = 8 with a streak of 3: 8 − 3 + 1 = 6.
  • The else sets the streak to 0. It is the only way the streak breaks: a weak day, never a change of week.

The printf line does two integer calculations: i % 7 gives the day of the week (8 % 7 = 1, Tuesday) and i / 7 + 1 the week (8 / 7 + 1 = 2).

Output

Week 1, Monday: 6420 steps -> streak 0
Week 1, Tuesday: 11250 steps -> streak 1
Week 1, Wednesday: 8900 steps -> streak 0
Week 1, Thursday: 12030 steps -> streak 1
Week 1, Friday: 4100 steps -> streak 0
Week 1, Saturday: 9870 steps -> streak 0
Week 1, Sunday: 10500 steps -> streak 1
Week 2, Monday: 10200 steps -> streak 2
Week 2, Tuesday: 11800 steps -> streak 3
Week 2, Wednesday: 7300 steps -> streak 0
Week 2, Thursday: 10050 steps -> streak 1
Week 2, Friday: 12400 steps -> streak 2
Week 2, Saturday: 13100 steps -> streak 3
Week 2, Sunday: 9100 steps -> streak 0
Longest streak: 3 days, starts on Sunday of week 1
The two weeks and the streak day by dayFourteen cells with the steps of the two weeks; in green those that reach 10000. Below, each day's streak: 0 1 0 1 0 0 1 2 3 0 1 2 3 0. The streak of 3 that starts on Sunday of week 1 is marked as the longest; the other streak of 3, Thursday to Saturday of week 2, ties and does not replace it.Week 1Week 2M64200T112501W89000T120301F41000S98700S105001M102002T118003W73000T100501F124002S131003S91000streaklongest streak: 3Sunday W1 → Tuesday W2also 3: a tie3 > 3 is false: no change
The streak crosses the change of week: Sunday, Monday and Tuesday count as consecutive.

Let’s check it by hand with a table of the days that decide the result:

iDaysteps>= 10000?streakstreak > best?bestbest_start
1Tuesday W111250Yes11 > 0 Yes11
6Sunday W110500Yes11 > 1 No11
7Monday W210200Yes22 > 1 Yes27 − 2 + 1 = 6
8Tuesday W211800Yes33 > 2 Yes38 − 3 + 1 = 6
9Wednesday W27300No0(not asked)36
12Saturday W213100Yes33 > 3 No36

Position 6 is Sunday of week 1 (6 % 7 = 6, 6 / 7 + 1 = 1), exactly what it prints. And the Thursday-to-Saturday streak of week 2 also reaches 3, but it does not replace the first one because 3 > 3 is false.

If you wanted the most recent streak to win a tie, changing > to >= would be enough. It is a decision, not a mistake, but it must be written the same way in the diamond and in the code.

Questions you might ask

Why one loop of 14 and not two of 7? Because with two loops you would have to decide what happens to the streak when moving from one to the other, and the natural thing is to reset it (my mistake in the story). With a single array of 14 days, Sunday and Monday are simply i = 6 and i = 7.

Why store best_start instead of working it out at the end? Because at the end the current streak is no longer the best one (on the last day it is 0). You have to note where it started at the moment it becomes the best.

What if no day reached the goal? best would stay at 0 and best_start at 0, and the program would say «0 days, starts on Monday of week 1». The right thing would be one more diamond at the end: best == 0? → «There is no streak».

Where does each day’s printf go in the flowchart? In a parallelogram after the meeting point, because it runs whether the streak goes up or breaks. If you drew it inside one branch, only some days would be shown.

Is the else with streak = 0 needed? Yes. Without it, the streak would never break and would end up counting every day with 10,000 or more, even if they were not consecutive (8, with this data).

Resetting the streak somewhere other than the diamond’s No (when the week changes, at the start of each lap…). The streak only breaks because of a weak day; any other streak = 0 is a logic error.

Try it yourself

  • Change > to >= in streak > best and check that the longest streak now starts on Thursday of week 2.
  • Add the best == 0? diamond at the end of the flowchart and the code, and test it by setting the goal to 20,000.

Program 2: is your step count a palindrome?

One day I walked 11,211 steps and the number caught my eye: it reads the same forwards and backwards. The program reads the steps from the keyboard as text and says whether they are a palindrome. What makes it tricky: you have to compare the first digit with the last, the second with the second-to-last… and stop as soon as a pair does not match.

The idea

Two indexes: i starts at the first digit and j at the last. While they do not cross and their digits match, i moves forward and j moves back. When the loop stops there are two possibilities, and a second diamond tells them apart:

  • i >= j: they crossed (or met in the center) without finding any different pair → it is a palindrome.
  • i < j: they stopped because number[i] != number[j] → it is not.

It is the same scheme as the search in the theory (loop with && + diamond that checks why it stopped):

Flowchart of the palindrome checkSTART; i = 0 and j = length minus 1; diamond: i less than j and number[i] equal to number[j]; if yes, i goes up, j goes down and loop back; if not, diamond i >= j: if yes, it is a palindrome; if not, it is not and the two different digits are shown.STARTi = 0j = strlen(number) – 1i < j andnumber[i] == number[j]?Yesi = i + 1j = j – 1loop backNoi >= j?YesNoShow «is a palindrome»Show «is not a palindrome»and the two digitsEND
Before the loop (not drawn) comes the parallelogram «Read number»; the second diamond tells «they crossed» apart from «they found two different digits».

Before programming, think about why we read the number as text and not as an int. With an int you would have to extract the digits with % 10 and / 10; with a string, each digit is already in its own cell.

The code

#include <stdio.h>
#include <string.h>

int main(void) {
    char number[20];   // the steps are read as text to look at them digit by digit
    int i, j;

    printf("Today's steps: ");
    scanf("%19s", number);          // %19s: leaves room for the '\0'

    i = 0;                          // one index from the left
    j = strlen(number) - 1;         // and another from the right (the last character, not the '\0')

    // They move towards the center while they do not cross AND the digits match
    while (i < j && number[i] == number[j]) {
        i = i + 1;
        j = j - 1;
    }

    // If they crossed (or met in the center), every pair matched
    if (i >= j) {
        printf("%s is a palindrome\n", number);
    } else {
        printf("%s is not a palindrome: digit %d ('%c') does not match digit %d ('%c')\n",
               number, i, number[i], j, number[j]);
    }
    return 0;
}

The loop is short, but each piece has its reason:

i = 0;                          // one index from the left
j = strlen(number) - 1;         // and another from the right (the last character, not the '\0')

// They move towards the center while they do not cross AND the digits match
while (i < j && number[i] == number[j]) {
    i = i + 1;
    j = j - 1;
}
  • j = strlen(number) - 1: strlen counts the characters without the '\0'. For “11211” it gives 5, and the last character is at position 4. Without the - 1, j would point at the '\0'.
  • i < j goes before the comparison: once the indexes cross, there is no point comparing further.
  • Both indexes change inside the loop. If you forget j = j - 1, you would always compare with the last digit.

Output

Real output, typing 11211:

Today's steps: 11211
11211 is a palindrome
The two palindrome indexes with 11211Three rows with the digits 1 1 2 1 1. In the first, i at 0 and j at 4: equal. In the second, i at 1 and j at 3: equal. In the third, i and j at 2: i < j is false and the loop stops; since i >= j, it is a palindrome.1011221314ijlap 1: '1' == '1' → go on1011221314ijlap 2: '1' == '1' → go on1011221314iji = 2, j = 2: i < j is false → stop→ «11211 is a palindrome»
i comes in from the left and j from the right; with 5 digits, the middle one does not need comparing.

By hand, with strlen = 5:

Lapijnumber[i]number[j]i < j and equal?
104‘1’‘1’Yes → i = 1, j = 3
213‘1’‘1’Yes → i = 2, j = 2
—22‘2’‘2’No: i < j is false

On leaving, i >= j (2 >= 2), so it is a palindrome. The middle digit is never compared with another one: it does not need to be. If you type 12341, the loop stops on lap 2 with '2' against '4' and the program says it is not a palindrome (you will see it in Python Tutor, further down).

Questions you might ask

Why does the final diamond ask i >= j and not whether the digits are equal? Because once the indexes cross, number[i] and number[j] can be anything (with 4 digits, i ends at 2 and j at 1). The only safe question is whether they crossed.

Does it work with an even number of digits? Yes. With “1221”: lap 1 compares 0 and 3, lap 2 compares 1 and 2, and then i = 2, j = 1: they crossed, palindrome.

Why %19s in the scanf? Because the array has 20 cells and one is for the '\0'. If someone types 30 characters, %19s reads 19 and does not write outside the array.

How is strlen drawn in the flowchart? Like any other calculation: in the initialisation rectangle, j = strlen(number) - 1. Functions need no special symbol.

j = strlen(number) without the - 1. The first comparison would be '1' against the '\0', the loop would stop on lap 1 and the program would say no number is a palindrome.

Try it yourself

  • Run it with 1221, 12341 and 7 (a single digit: what does the loop do if i and j start equal?).
  • Change the program so it also says how many pairs of digits it has compared.

Program 3: the week’s average and the days above it

This time the user types the steps, one per day. The program works out the average and then says which days were above it. What makes it tricky: to know whether a day is above the average you need the average… and the average is only known once every day has been read.

The idea

Two loops in a row, not nested:

  1. Loop 1: read the 7 days and add them up at the same time.
  2. After it, average = total / N and it is shown.
  3. Loop 2: go through the array again and, with an if, show and count those above the average.

Between the two loops, i goes back to 0. In the flowchart it looks like two «blocks» of diamond + way back, one under the other:

Two loops in a row: read, then compareFirst loop: read steps[i] and add it, seven times. After it, average = total / N and it is shown. Second loop from i = 0: if steps[i] is greater than the average, the day is shown and above goes up. After it, above is shown.STARTtotal = 0, above = 0, i = 0i < N?Loop 1: readYesRead steps[i]total = total + steps[i]i = i + 1Noaverage = total / NShow averagei = 0i < N?Loop 2: compareYessteps[i] > average?YesShow day and stepsabove = above + 1Noi = i + 1NoShow aboveEND
Between the two loops, i goes back to 0: otherwise the second diamond would say No straight away.

Before programming, think about why it cannot be done in a single loop. When you read Monday (6420) you do not yet know whether it will end up above or below: the average depends on the days still to come.

The code

#include <stdio.h>

#define N 7

int main(void) {
    const char *days[N] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
    int steps[N];
    int total = 0;
    int above = 0;   // days above the average
    double average;
    int i;

    // Loop 1: read the seven days (parallelogram inside the loop)
    for (i = 0; i < N; i++) {
        printf("Steps on %s: ", days[i]);
        scanf("%d", &steps[i]);
        total = total + steps[i];      // added up while it is read
    }

    average = (double) total / N;      // the average is only known when loop 1 ends
    printf("Average: %.1f steps\n", average);

    // Loop 2: with the average already worked out, go through the array again
    for (i = 0; i < N; i++) {
        if (steps[i] > average) {
            printf("  %s: %d (above)\n", days[i], steps[i]);
            above = above + 1;
        }
    }
    printf("Days above the average: %d of %d\n", above, N);
    return 0;
}

The two loops, up close:

// Loop 1: read the seven days (parallelogram inside the loop)
for (i = 0; i < N; i++) {
    printf("Steps on %s: ", days[i]);
    scanf("%d", &steps[i]);
    total = total + steps[i];      // added up while it is read
}

average = (double) total / N;      // the average is only known when loop 1 ends
printf("Average: %.1f steps\n", average);

// Loop 2: with the average already worked out, go through the array again
for (i = 0; i < N; i++) {
    if (steps[i] > average) {
        printf("  %s: %d (above)\n", days[i], steps[i]);
        above = above + 1;
    }
}
  • In loop 1, the scanf («Read steps[i]» parallelogram) and the addition happen in the same lap: that way there is no need for a third loop to add up.
  • (double) total / N: without the (double), C would do an integer division.
  • In loop 2, steps[i] > average compares an int with a double; C converts the integer to double before comparing, so it works.

Output

Real output, typing the week’s steps:

Steps on Monday: 6420
Steps on Tuesday: 11250
Steps on Wednesday: 8900
Steps on Thursday: 12030
Steps on Friday: 4100
Steps on Saturday: 9870
Steps on Sunday: 10500
Average: 9010.0 steps
  Tuesday: 11250 (above)
  Thursday: 12030 (above)
  Saturday: 9870 (above)
  Sunday: 10500 (above)
Days above the average: 4 of 7
The week's steps against the averageSeven bars with the steps from Monday to Sunday and a dashed line at the average, 9010. Above it are Tuesday 11250, Thursday 12030, Saturday 9870 and Sunday 10500.6420Mon11250Tue8900Wed12030Thu4100Fri9870Sat10500Sunaverage9010.0
To know which bars cross the line you need the line first: that is why two loops are needed.

The trace of loop 1 shows how the total grows:

LapDaysteps[i] readtotal after
1Monday64206420
2Tuesday1125017670
3Wednesday890026570
4Thursday1203038600
5Friday410042700
6Saturday987052570
7Sunday1050063070

63070 / 7 = 9010.0. In loop 2, the days above it are Tuesday (11250), Thursday (12030), Saturday (9870) and Sunday (10500): 4 out of 7. Saturday does not reach the 10,000 goal, but it does beat the average.

Questions you might ask

Why does the «Steps on Monday» printf go inside the loop? Because it is the message that goes with each reading. In the flowchart, the «Read steps[i]» parallelogram includes it: there is no need to draw the prompt separately.

What if we did not set i = 0 before loop 2? In C with for it cannot happen, because the header does it. But in the flowchart (and with while) it is a rectangle you have to draw: otherwise i would be 7 and the loop 2 diamond would say No straight away.

Why > and not >= the average? The task says «above». A day with exactly the average is not above it. With this data nothing changes (no day has 9010), but the diamond must say what the task says.

Can the average be stored as an int? For the diamond it would make no difference: if a whole number of steps beats the truncated average, it also beats the real one. But the average that is shown would lose its decimals: with 63071 steps you would see 9010 instead of 9010.1. With double, what is shown and what is compared is the exact value.

Trying to do it in a single loop by comparing with the «average so far». The average changes with each day read, so Monday would be compared with itself and the results would depend on the order of the days.

Try it yourself

  • Add an else to loop 2 that counts the days below, and check that above + below = 7.
  • Change the program so it also says which day was closest to the average (hint: the difference in absolute value, with one more diamond).

Common mistakes in this practice

  • Resetting a counter or a streak outside its diamond. If the flowchart has no No leading to streak = 0, the code must not have it either.
  • Using >= when the task asks for «more than». The diamond’s sign is part of the task: «above», «longer», «beats» are >.
  • Forgetting the second diamond after a while with &&. The loop can stop for two reasons and the program needs to know which.
  • strlen without the - 1 when starting an index at the end of a string.
  • Dividing integers for an average without (double).
  • Merging two loops into one when the second needs a value that only exists once the first has finished.

Watch it step by step in Python Tutor

Python Tutor draws the variables at each step of the run. Choose C and paste this version of the palindrome with a fixed number (it does not read from the keyboard) that is not a palindrome, to see where it stops:

#include <stdio.h>
#include <string.h>

int main(void) {
    char number[] = "12341";   // fixed data: Python Tutor does not read from the keyboard
    int i = 0;
    int j = strlen(number) - 1;

    while (i < j && number[i] == number[j]) {
        i = i + 1;
        j = j - 1;
    }

    if (i >= j) {
        printf("%s is a palindrome\n", number);
    } else {
        printf("%s is not a palindrome (%c vs %c)\n", number, number[i], number[j]);
    }
    return 0;
}

Real output:

12341 is not a palindrome (2 vs 4)

What to watch step by step:

  1. At the start, number appears as an array of 6 characters: '1' '2' '3' '4' '1' and the '\0'. j is 4 because strlen gives 5.
  2. On the first lap, cells 0 and 4 are compared ('1' and '1'): the loop enters and i and j change at the same time.
  3. On the second while question, i = 1 and j = 3: '2' against '4'. Notice that the arrow does not enter the body: it is the diamond’s No.
  4. Since i < j (1 < 3), the final if goes through the else: it is the flowchart’s second diamond saying No.
  5. Change "12341" to "12321" and run it again: the loop does two laps and stops with i = j = 2.

Summary

  • Draw the flowchart before programming: a streak = 0 that does not come from any diamond is a mistake the flowchart gives away.
  • An if inside another is drawn as a diamond inside the Yes branch; the outer meeting point collects both branches.
  • A while with && needs a diamond afterwards to know why it stopped.
  • Two indexes moving towards the center (i up, j down) solve palindromes, reversals and end-to-end comparisons.
  • If a calculation needs a value that only exists at the end (the average), you need two loops in a row, with i = 0 between them.
  • The sign in each diamond (>, >=, <) is decided by the task, and it must be the same in the flowchart and in the code.

Checklist: have you practised it?

  • Have you drawn the flowchart of each program before looking at the code?
  • Can you explain why the longest streak starts on Sunday and not on Thursday?
  • Do you know what happens in the palindrome check if you remove the - 1 from strlen?
  • Can you explain why the average needs two loops?
  • Have you run the three programs and checked the outputs?
  • Have you tried the «Try it yourself» tasks without looking at the theory?

If you answered yes to almost everything, you are ready for the flowcharts exercises, where I no longer give you the code. See you there!

Next step: exercisesFlowcharts in C — exercises with counters, passwords and two weeks of steps (and a program that hung)

Did this article help you? Here is how you can support SergioLearns →