Start learning free

LESSON 252 OF 414 · TWO POINTERS

Reverse Words in Scala: Reverse Twice, In Place

Practice O(n) Read the full explanation

Given a string input, reverse the order of the words.

A word is defined as a sequence of non-space characters. The words in input 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

    SCALA · TWO POINTERS

    Reverse Words in Scala, explained

    Problem overview

    The method reverseWords(input: String) receives a sentence in which words are separated by exactly one space. It returns the same words in the opposite order, again separated by single spaces. For "sky is blue" the result is "blue is sky". Each word keeps its own spelling: only the order of the words changes.

    Approach: reverse everything, then reverse each word

    Building a list of words and joining it backwards would work, but this lesson practises a classic in-place trick that needs only two ingredients: a way to reverse part of an array, and two reversals.

    1. Reverse the whole string. The words end up in the opposite order, but every word is spelled backwards.
    2. Reverse each word on its own. That puts the letters of each word back in the right order and leaves the words where they are.

    On "sky is blue":

    sky is blue
    eulb si yks     (after reversing everything)
    blue is sky     (after reversing each word)
    

    Reversing the whole string moves the last word to the front, which is the order we want. Reversing a word that is already backwards spells it correctly again.

    A shorter route exists in Scala: split the string on spaces, reverse the resulting array of words and join them again. It builds an array of word strings along the way. The two-reversal version works directly on one array of characters, and it shows how the same reverse helper can serve two jobs: reversing the whole text and reversing a single word.

    Step by step

    1. Get a mutable copy. A Scala String is immutable, so input.toCharArray gives an Array[Char] named arr that can be changed in place.
    2. Reverse all of arr with a helper reverse(arr, 0, arr.length - 1), written later.
    3. Walk the words. A var i starts at 0 and marks the start of the current word. The outer while (i < arr.length) runs once per word.
    4. Find the end of the word. A second var j starts at i and moves right while j < arr.length and arr(j) is not a space. The bounds check comes first, because && stops evaluating as soon as its left side is false, so arr(j) is never read past the end.
    5. Reverse the word. When the inner loop stops, the word occupies i to j - 1. j is either the space after the word or the end of the array.
    6. Jump to the next word with i = j + 1, which skips the space.
    7. Build the result. new String(arr) turns the characters back into a string. As the last expression of the method, it is the return value.
    8. Write reverse. It takes the array and the two ends of the range. Scala parameters can't be reassigned, so it copies them into var i and var j, then loops while i < j: swap the two characters, move i right and j left.
    9. Write swap. It saves arr(i) in a val temp, copies arr(j) into arr(i), then puts temp into arr(j). Without the saved copy, the first assignment would destroy a value.

    The solution in Scala

    class Solution {
        def swap(arr: Array[Char], i: Int, j: Int): Unit = {
            val temp = arr(i)
            arr(i) = arr(j)
            arr(j) = temp
        }
    
        def reverse(arr: Array[Char], iStart: Int, jStart: Int): Unit = {
            var i = iStart
            var j = jStart
            while (i < j) {
                swap(arr, i, j)
                i += 1
                j -= 1
            }
        }
    
        def reverseWords(input: String): String = {
            val arr = input.toCharArray
            reverse(arr, 0, arr.length - 1)
            var i = 0
            while (i < arr.length) {
                var j = i
                while (j < arr.length && arr(j) != ' ') {
                    j += 1
                }
                reverse(arr, i, j - 1)
                i = j + 1
            }
            new String(arr)
        }
    }
    

    The Scala details worth knowing:

    • Arrays are mutable, strings are not. arr is a val, so the name always refers to the same array, but its elements can change. That is why swap and reverse return Unit and still have an effect on the caller's array.
    • arr(i) = x is an update. Scala indexes arrays with parentheses, and assigning to an indexed element calls the array's update operation.
    • No ++ or --. Counters move with += 1 and -= 1.
    • Parameters are read-only. That is why reverse copies iStart and jStart into variables before moving them.
    • Character comparison. ' ' is a Char literal with single quotes. Comparing it with arr(j) by != compares values.

    Here is the loop on the reversed text "eulb si yks". At i = 0 the inner loop stops at j = 4, the space, so reverse handles indices 0 to 3 and turns eulb into blue. Then i becomes 5, j stops at 7, and si becomes is. Finally i is 8, j runs to the end at 11, and yks becomes sky. The array now reads blue is sky.

    Complexity

    Time: O(n). The full reversal touches each character once. The word loop moves j over each character once, and the reversals of the words together touch each character at most once more.

    Space: O(n). The character array is a copy of the input. The reversing itself needs only a few integers and one temporary character.

    Edge cases and common mistakes

    • Empty string. The array has length 0, so reverse receives the range 0 to -1 and does nothing, the outer loop never runs, and the result is "".
    • A single word. "hello" is reversed to "olleh" and then reversed back to "hello".
    • The last word. The inner loop ends with j equal to arr.length, the reversal covers up to j - 1, and i = j + 1 ends the outer loop.
    • Extra spaces. The lesson promises single spaces. If the input had extra ones, the method would keep them in the mirrored positions: "a b" becomes "b a", and " a" becomes "a ".
    • Checking arr(j) before j < arr.length. On the last word this reads past the end of the array and throws an exception.
    • Reversing i to j instead of i to j - 1. The space at j would be swapped into the word.
    • Forgetting the first reversal. Reversing only the words leaves their order unchanged.

    Testing

    • "sky is blue" gives "blue is sky".
    • "hello" gives "hello".
    • "a b" gives "b a".
    • "one two three four" gives "four three two one".
    • "" gives "".
    Keep going with ScalaAbout 340 step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free