Problem overview
The function reverseString receives a string, input, and returns a new string with the characters in the opposite order, without library helpers. For "hello" the result is "olleh". The statement allows O(n) time and O(n) extra space.
Approach: swap pairs from the ends
Reversing means that the first character trades places with the last, the second with the second-to-last, and so on until the middle. Two pointers do this: left starts at the first position, right at the last, and each step swaps the two elements and moves the pointers toward each other. When left < right is no longer true, every pair has been swapped. For an odd length, the middle element has no partner and stays in place.
There is one Go-specific obstacle: strings are immutable. You can't write input[0] = 'x'; the compiler rejects it. So the function first copies the string into a mutable byte slice, reverses the slice, and converts it back. That copy is the O(n) extra space the statement allows.
Tracing "hello"
After the copy, chars holds the bytes of h e l l o, with left = 0 and right = 4. The first pass swaps h and o, giving o e l l h, and the pointers become 1 and 3. The second pass swaps e and l, giving o l l e h, and the pointers become 2 and 2. Now left < right is false, so the loop ends. The middle l never moved, and the function returns "olleh".
Step by step
- Copy the string into a slice.
chars := []byte(input)allocates a new slice with the string's bytes, which can be changed freely. - Create
leftstarting at 0. - Create
rightstarting atlen(chars) - 1, the last index. - Loop while
left < rightwith a condition-onlyfor, Go's replacement forwhile. - Call
swapBytes(chars, left, right)inside the loop to exchange the two elements. - Declare the helper
swapBytes, taking the slice and two indices. Because a slice shares its backing array with the caller, changes made inside the helper are visible inreverseString. - Swap through a temporary. Save
chars[i]intemp, copychars[j]intochars[i], then puttempintochars[j]. Without the temporary, the first assignment would overwrite a value that is still needed. - Move the pointers with
left++andright--. - Convert back with
string(chars)and return it.
The solution in Go
func reverseString(input string) string {
chars := []byte(input)
left := 0
right := len(chars) - 1
for left < right {
swapBytes(chars, left, right)
left++
right--
}
return string(chars)
}
func swapBytes(chars []byte, i, j int) {
temp := chars[i]
chars[i] = chars[j]
chars[j] = temp
}
The Go details worth a closer look:
- Conversions copy.
[]byte(input)andstring(chars)each make a new copy. The originalinputis never modified: aftert := reverseString(s)withs = "hello",sstill printshelloandtprintsolleh. - Slices share memory. A slice is a small header pointing at an array. Passing
charstoswapBytescopies the header, not the bytes, so both functions see the same elements. That is why the helper needs no return value. Assigning one slice to another variable shares the data the same way: afterc := bandc[0] = 'z', the slicebshows the change too. - Grouped parameters.
i, j intdeclares two parameters of the same type. - Tuple assignment. Go can swap without a temporary:
chars[i], chars[j] = chars[j], chars[i]. The lesson's version spells out each move. - Bytes, not characters. This solution reverses bytes. For ASCII text that is the same as reversing characters. A character outside ASCII takes several bytes in UTF-8, and reversing the bytes breaks it:
reverseString("héllo")returns an invalid string. To handle such text, convert to[]runeinstead, which holds one element per character, and swap those; that givesolléh.
Reversing text that isn't ASCII
If the input can contain accented letters, reverse runes instead of bytes. A rune is one Unicode character, and the swap can use Go's tuple assignment:
r := []rune("héllo")
for i, j := 0, len(r)-1; i < j; i, j = i+1, j-1 {
r[i], r[j] = r[j], r[i]
}
fmt.Println(string(r)) // olléh
The loop is the same two-pointer idea; only the element type changed. This is a side note, since the lesson's tests use plain text.
Complexity
Time: O(n). The copy, the loop (at most n / 2 swaps) and the final conversion are each linear.
Space: O(n). The byte slice and the result string are new copies of the input. The swapping itself happens in place.
Edge cases and common mistakes
- Empty string.
rightis -1, the loop is skipped, and the function returns"". - One character.
"a"comes back unchanged. - Even and odd lengths.
"abcd"gives"dcba"after two swaps. The loop conditionleft < rightstops correctly in both cases. - Trying to modify the string. Assigning to an element of a string doesn't compile, so the slice copy is required.
- Using
left <= right. For odd lengths this swaps the middle element with itself. It is harmless but pointless. - Swapping without a temporary. Writing
chars[i] = chars[j]followed bychars[j] = chars[i]leaves both positions with the same value. - Forgetting to move the pointers. The loop swaps the same pair back and forth forever.
Testing
"hello"gives"olleh".""gives""."a"gives"a"."abcd"gives"dcba".