Problem overview
The method reverse of class Solution takes a string, input, and returns its characters in the opposite order, without using built-in helpers such as the array reverse method. For "hello" the answer is "olleh".
This version asks for O(n) time and O(n) extra space, so the work has to grow in step with the length of the string and not faster.
Approach: walk the string backwards and append
Instead of reading the input from the left and inserting each character at the front, read it from the right and add each character at the back. The first character you meet is the last one of the input, and it should come first in the output. Appending it to an empty result puts it there. The next character from the right goes after it, and so on.
read o (index 4) -> o
read l (index 3) -> ol
read l (index 2) -> oll
read e (index 1) -> olle
read h (index 0) -> olleh
Compared with the prepending version of this problem, the result is the same, but the work is different. Prepending forces a new string to be built from the whole old result on every pass, so in the simple cost model it is O(n²). Appending to the end of a string is the case JavaScript engines handle well, so the loop stays linear. Each step does a constant amount of work apart from that append, and there are n steps.
Step by step
- Create the result. Declare
reversedStringwithlet, typed asstringand set to"". - Loop over the indexes from the last to the first. Declare
iwithletand start it atinput.length - 1, the last valid index. Keep going whilei >= 0, and decrement withi--after each pass. - Append the character at index
i. AssignreversedString + input[i]back toreversedString. The old result stays on the left, and the new character goes on the right. - Return the result once the loop ends.
The solution in TypeScript
class Solution {
reverse(input: string): string {
let reversedString: string = "";
for (let i = input.length - 1; i >= 0; i--) {
reversedString = reversedString + input[i];
}
return reversedString;
}
}
The TypeScript details worth a closer look:
- The annotation is optional.
let reversedString: string = ""andlet reversedString = ""mean the same thing, because TypeScript infersstring. The annotation is erased when the code is compiled, so it doesn't affect the running program. - Counting down. The loop starts at the last index, tests
i >= 0and decrements. The test must be>=: withi > 0the loop would stop before index 0 and the first character of the input would be missing. input[i]is a one-character string. TypeScript has no separate character type, so adding it to a string just joins two strings.- No error for a bad index. Reading
input[-1]orinput[input.length]givesundefined, not an exception, andreversedString + undefinedwould add the text"undefined". Correct bounds matter. - Code units. Indexing works on 16-bit units. For letters and digits that is one unit per character. A character such as an emoji takes two units, and this loop reverses the two halves separately, which breaks the character. A
for...ofloop keeps such characters whole. - Equivalent shorter form.
reversedString += input[i]does the same as the longer assignment in the solution.
Complexity
Time: O(n). The loop runs once per character. Each pass reads one character and appends it to the end of the result, which the engine handles efficiently, so the total grows linearly with n.
Space: O(n). The result holds n characters. Strings are immutable, so the result can't be built in place, but only the result itself needs to be kept at the end.
Edge cases and common mistakes
- Empty string.
input.length - 1is-1, the testi >= 0fails at once, and the function returns"". - One character. The loop runs once, at index 0, and returns the same character.
- Starting at
input.length. The first read isinput[input.length], which isundefined, so the output would begin with the text"undefined". - Stopping at
i > 0. The first character of the input is skipped:"hello"would give"olle". - Increasing
i. Writingi++by habit moves the index away from 0, soi >= 0never becomes false and the loop never ends. - Prepending in this loop. If you write
input[i] + reversedString, you get the original string back, because going backwards and inserting at the front cancel out.
Testing
"hello"gives"olleh"."ab"gives"ba"."a"gives"a".""gives""."racecar"gives"racecar".