Problem overview
The task is to write the method PrintX(int n), which prints an n by n grid of characters. Every position holds - except the two diagonals, which hold x, so together the x characters form the letter X.
For n = 5 the output is:
x---x
-x-x-
--x--
-x-x-
x---x
Each line has exactly n characters. On line 0 the x characters are at positions 0 and 4. On line 1 they are at positions 1 and 3. On line 2 both diagonals meet at position 2, so that line has a single x. After the middle, the x characters move apart again.
Approach: one line at a time
You could think of the grid as a two-dimensional array and fill it cell by cell, but there is a simpler view. The output is just a list of lines, and each line is fully described by two numbers: the positions of its two x characters.
On the first line those positions are 0 and n - 1, the two ends. On every following line the left x moves one position right and the right x moves one position left. The diagonals cross in the middle and keep going, so the numbers simply carry on changing by one per line. Two counters, firstX and secondX, are all the state the program needs.
Splitting the work into two methods keeps each one short. PrintX handles the vertical direction: it loops over the lines and moves the counters. A helper, BuildString, handles the horizontal direction: given the two positions, it builds one line of text. The helper has no side effects, so you can reason about it separately.
Step by step
- Declare the two counters in one statement:
firstXstarts at0andsecondXatn - 1, the two ends of the first line. - Loop once per line. A
forloop withifrom0whilei < nruns the bodyntimes, because the grid hasnlines. - Call the helper inside the loop and keep its result in a
stringnamedstr. At this point the helper doesn't exist yet. Writing the call first lets you decide what the helper must accept and return before you write it. Then print the string withConsole.WriteLine, which adds the line break. - Move the counters after printing:
firstXgoes up by one andsecondXgoes down by one, ready for the next line. - Write the helper's signature to match the call:
public string BuildString(int n, int firstX, int secondX). A method in the same class can be called by its plain name, with no object in front. - Start with an empty string named
strthat will collect the characters. - Loop over character positions with another
forloop from0whilei < n. - Choose the character. If
iequalsfirstXorsecondX, append"x"; otherwise append"-". The||operator is true when at least one side is true. - Return the string after the loop ends, once every position has a character.
The solution in C#
class Solution {
public string BuildString(int n, int firstX, int secondX) {
string str = "";
for (int i = 0; i < n; i++) {
if (i == firstX || i == secondX) {
str += "x";
} else {
str += "-";
}
}
return str;
}
public void PrintX(int n) {
int firstX = 0, secondX = n - 1;
for (int i = 0; i < n; i++) {
string str = BuildString(n, firstX, secondX);
Console.WriteLine(str);
firstX++;
secondX--;
}
}
}
A few C# details are worth knowing:
- Several variables in one declaration.
int firstX = 0, secondX = n - 1;declares twointvariables, each with its own initial value. The second one may even use a parameter, as it does here. - Strings are immutable.
str += "x"doesn't change the existing string. It creates a new, longer string and assigns it tostr. That is fine for short lines, and the complexity section shows its cost. - Double quotes for strings.
"x"is astringof one character. The single-quoted'x'is achar. For+=on a string, either one works, but this solution uses strings throughout. ||short-circuits. Ifi == firstXis already true, C# doesn't evaluate the second comparison.WriteLineversusWrite.Console.WriteLineends the output with a line break, which is what separates the lines of the picture.Console.Writewould run them together.
Complexity
The output has n * n characters, so no solution can beat O(n²) time. This one is slightly slower. Each += copies the string built so far, so building one line of length n copies about 1 + 2 + ... + n characters, which is O(n²) work. Doing that for n lines gives O(n³) in total. For the sizes in this lesson that is invisible.
The extra space is O(n): only one line exists at a time, and old intermediate strings become garbage that .NET collects.
If you wanted an O(n²) version, you would build each line with new string('-', n) and a char[], or with StringBuilder, which appends in place. For this lesson, the string version is clearer.
Edge cases and common mistakes
- Even
n. The diagonals never share a cell, so there is no single-x line. Forn = 4the output isx--x,-xx-,-xx-,x--x. The counters pass each other after the middle, and the code handles that without a special case. n = 1. Both counters start at0, so the only line isx.n = 2. Both lines arexx.- Odd
n. On the middle line the counters are equal, so the same position matches twice and one x appears. - Moving the counters before printing. Then the first line would already be shifted. Update them after the line is printed.
- Using
<=in a loop.i <= nproducesn + 1lines or characters. Positions run from0ton - 1. - Using
&&instead of||. A position can't be both counters at once except in the middle, so the grid would contain almost no x characters. - Returning inside the loop. A
returnplaced before the loop closes ends the helper after one character.
Testing
PrintX(1)printsx.PrintX(2)printsxxtwice.PrintX(3)printsx-x,-x-,x-x.PrintX(5)prints the grid from the example.BuildString(5, 1, 3)returns-x-x-.PrintX(0)prints nothing, because the loop never runs.