Start learning free

LESSON 225 OF 338 · TWO POINTERS

Reverse Words in Go: Reverse Everything, Then Each Word

Practice O(n) Read the full explanation

Given an input string s, reverse the order of the words.

A word is defined as a sequence of non-space characters. The words in s will be separated by one space for simplicity.

Return a string of the words in reverse order concatenated by a single space.

Example:

Input:  input = "sky is blue"

Output:  "blue is sky"
STEP 1 OF 18
    Ctrl + Enter to check

    GO · TWO POINTERS

    Reverse Words in Go, explained

    Problem overview

    The function reverseWords receives a string, input, made of words separated by single spaces, and returns the words in reverse order, again separated by single spaces. For "sky is blue" the answer is "blue is sky". A word is a run of non-space characters.

    Approach: two reversals

    A direct approach splits the string into words, reverses the list and joins it back. It works, but it builds a list of substrings. This lesson uses a neat in-place trick instead, working on a mutable copy of the text:

    1. Reverse the whole string. "sky is blue" becomes "eulb si yks". The words are now in the right order, but each one is written backward.
    2. Reverse each word on its own. "eulb" becomes "blue", "si" becomes "is" and "yks" becomes "sky".

    The spaces never move relative to the words' boundaries: the first reversal puts them in exactly the places where the final answer needs them, so the second pass only has to find each word and flip it. Both passes use the same helper, reverseRange, which reverses a stretch chars[i..j] with two pointers.

    On the reversed text "eulb si yks", the first pass of the loop starts with i = 0, scans j to the space at index 4, flips indices 0 to 3 and sets i to 5. The next pass handles "si", the last one handles "yks", where j stops at the end of the slice instead of at a space.

    Step by step

    1. Copy the string into a byte slice: chars := []byte(input). Go strings are immutable, and the slice can be changed.
    2. Reverse everything with reverseRange(chars, 0, len(chars)-1), a helper that is written afterwards.
    3. Create the pointer i at 0. It marks where the current word starts.
    4. Loop while i < len(chars). Each pass of this loop handles one word.
    5. Create j equal to i. It will scan to the end of the word.
    6. Advance j to the next space with an inner loop that runs while j < len(chars) and chars[j] != ' '. The length check comes first, so the code never reads past the end.
    7. Increment j inside that inner loop.
    8. Reverse the word, which occupies indices i to j-1, because j is the space after it or the end of the slice.
    9. Jump to the next word with i = j + 1, skipping the space.
    10. Convert back with string(chars) and return.
    11. Write reverseRange: a loop that runs while i < j, swaps the two elements with swapBytes, then does i++ and j--.
    12. Write swapBytes with a temporary variable: save chars[i], copy chars[j] into chars[i], then put the saved value into chars[j].

    The solution in Go

    func reverseWords(input string) string {
        chars := []byte(input)
        reverseRange(chars, 0, len(chars)-1)
        i := 0
        for i < len(chars) {
            j := i
            for j < len(chars) && chars[j] != ' ' {
                j++
            }
            reverseRange(chars, i, j-1)
            i = j + 1
        }
        return string(chars)
    }
    
    func reverseRange(chars []byte, i, j int) {
        for i < j {
            swapBytes(chars, i, j)
            i++
            j--
        }
    }
    
    func swapBytes(chars []byte, i, j int) {
        temp := chars[i]
        chars[i] = chars[j]
        chars[j] = temp
    }
    

    The Go details worth a closer look:

    • Slices are shared, parameters are copies. reverseRange receives a copy of the slice header, but it points at the same bytes, so its swaps change the caller's data. The integers i and j are copied by value, which is why reverseRange can count them up and down freely without affecting the caller's variables.
    • Single quotes make a byte. ' ' is a character literal, and compared with a chars[j] byte it works. A string literal " " would not compile in that comparison.
    • Short-circuit &&. When j < len(chars) is false, the right side isn't evaluated. Swapping the two conditions would panic with an index-out-of-range error at the end of the last word.
    • Empty ranges are fine. For an empty string, reverseRange(chars, 0, -1) has i < j false and does nothing, and the outer loop is skipped. For consecutive spaces, reverseRange(chars, i, j-1) gets j-1 < i and does nothing too.
    • Bytes again. The slice holds bytes, so words with non-ASCII letters would be damaged by the byte reversal. The lesson's text is plain ASCII.

    Complexity

    Time: O(n). The full reversal touches each byte once. In the loop, every byte is passed over by j once, and each word is reversed once, so the work for all words together is also linear.

    Space: O(n). The byte slice and the result string are copies of the input. The reversals themselves happen in place with a few integers.

    Edge cases and common mistakes

    • A single word. "hello" is reversed twice and comes back as "hello".
    • Empty string. It returns "".
    • Two words. "a b" gives "b a".
    • Extra spaces. The problem promises single spaces. If the input had more, the code would keep them, mirrored: "a b" comes back as "b a", and " a b" as "b a ", so leading and trailing spaces would not be removed.
    • Reversing only the whole string. That leaves each word backward: "eulb si yks".
    • Reversing only the words. That leaves the word order unchanged.
    • Using j instead of j-1. The space would be pulled into the word, and the word would swap places with it.
    • Forgetting i = j + 1. The outer loop would handle the same word forever.

    Testing

    • "sky is blue" gives "blue is sky".
    • "the quick brown fox" gives "fox brown quick the".
    • "a b" gives "b a".
    • "hello" gives "hello".
    • "" gives "".
    Keep going with GoAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free