Problem overview
The function printX(n: Int) prints an n by n picture of the letter X. Each line has n characters: x where the two diagonals cross that line, and - everywhere else. The function prints the lines with println and returns nothing.
For n = 5 the output is:
x---x
-x-x-
--x--
-x-x-
x---x
The first line has its x characters at positions 0 and 4, the second at 1 and 3, and the middle line at position 2 twice, which shows as a single x. For an even n such as 4 the diagonals cross between two columns, so the two middle lines both read -xx-.
Approach: two positions that walk toward each other
Think of the picture one line at a time. On line 0 the x characters sit at the two ends, positions 0 and n - 1. On every later line the left one moves one step right and the right one moves one step left. So two variables, firstX and secondX, are enough to describe every line.
Printing a line then means walking over the positions 0 to n - 1, writing x at a position equal to firstX or secondX and - at any other. Splitting the work into a function that prints and a helper that builds one line keeps each part short.
Step by step
- Declare the two positions with
var.firstXstarts at 0 andsecondXatn - 1. They change on every line, sovalwould not work. - Loop once per line with
for (i in 0 until n). The loop counts lines and its index is not used inside. - Build and print the line. The loop calls a helper,
buildString(n, firstX, secondX), stores the result in avalnamedstrand prints it. Writing the call first, and the helper afterwards, lets you think about one problem at a time. Order of members inside a class does not matter in Kotlin. - Move the positions. After printing,
firstX++andsecondX--prepare the next line. - Write the helper's header. It takes three
Intparameters and returns aString. In Kotlin the return type goes after the parameter list, following a colon. - Start from an empty string held in a
var, because each+=replaces it with a longer string. - Loop over the columns with another
0 until n. - Append
"x"wheni == firstX || i == secondX. - Append
"-"in theelsebranch. - Return the string after the loop, once every position is filled.
The solution in Kotlin
class Solution {
fun printX(n: Int) {
var firstX = 0
var secondX = n - 1
for (i in 0 until n) {
val str = buildString(n, firstX, secondX)
println(str)
firstX++
secondX--
}
}
fun buildString(n: Int, firstX: Int, secondX: Int): String {
var str = ""
for (i in 0 until n) {
if (i == firstX || i == secondX) {
str += "x"
} else {
str += "-"
}
}
return str
}
}
Kotlin details worth knowing:
0 until nexcludesn. It produces 0, 1, ...,n - 1, exactly the positions of a line.0..nwould includenand print one character too many.- Two variables called
str. The one inprintXis a read-onlyval; the one inbuildStringis avar. They live in different functions and do not interact. - Parameters are read-only.
firstXandsecondXinsidebuildStringcannot be reassigned. That is why the changing copies live inprintXasvar. +=on aStringcreates a new string. Strings never change in place, so the helper copies the line so far at each step. For one line that costs O(n²) work in total, which is fine for the sizes here.||short-circuits. Ifi == firstXis true, the second comparison is skipped.
Tracing n = 5
The loop runs five times. The pairs (firstX, secondX) passed to the helper are (0, 4), (1, 3), (2, 2), (3, 1) and (4, 0). In the third pair both positions are 2, so the helper appends a single x at position 2 and the line is --x--. In the fourth and fifth pairs the positions have crossed, and since the test is symmetric, the lines -x-x- and x---x come out the same as the second and first.
Complexity
The function prints n lines of n characters each, so it does at least O(n²) work, which is the size of its own output. Building each line with += adds a factor of up to n for the copying, giving O(n³) in the worst case for this implementation. The only extra memory is the current line.
Edge cases and common mistakes
n = 1. Both positions are 0, so the single line isx.- Even
n. Forn = 4the output isx--x,-xx-,-xx-,x--x. The positions never coincide, they pass each other, and the second half of the picture mirrors the first. Forn = 2both lines readxx. - Forgetting to move the positions. Without
firstX++andsecondX--every line would look like the first one. - Moving them before printing. The first line would be missing its edge
xcharacters. - Using
0..n. The extra iteration adds a character to each line and an extra line to the output. - Declaring the positions with
val.firstX++then fails to compile because avalcannot be reassigned. - Returning instead of printing.
printXreturns nothing; its result is what it writes withprintln.
Testing
n = 1printsx.n = 2printsxxtwice.n = 4printsx--x,-xx-,-xx-,x--x, one per line.n = 5prints the picture from the overview.