Linear recursion

Reverse String
Reverse String in C++

reverse_string.h

#ifndef REVERSE_STRING_H
#define REVERSE_STRING_H

#include <string>

namespace reverse_string
{
    std::string reverse_string(std::string_view str);
}

#endif // REVERSE_STRING_H

reverse_string.cpp

#include "reverse_string.h"

namespace reverse_string
{

// (1) the function takes a std::string_view as its input parameter
// (2) the function takes an "output parameter"
void recursive_helper(std::string_view input, std::string& output)
{
    // (3) handle the base case
    if (input.empty())
        return;

    // (4) append the last character of the input to the output
    output.append(input.back());
    // and "remove" the last character from the input
    input.remove_suffix(1);

    // (5) handle the rest of the input recursively
    recursive_helper(input, output);
}

std::string reverse_string(std::string_view original)
{
    std::string result;
    helper(original, result);
    return result;
}

} // namespace reverse_string;

The types of the parameters

Both functions take a std::string_view as their first argument. That type was added to C++17, it is an immutable "view" of a string, it is cheap to construct and to copy (see cppreference.com.) We can use it here because the functions do not really modify the parameter, the recursive_helper just narrows the view (input.remove_suffix(1)).

The helper function has an "output parameter" (see the Wikipedia). In C++ we use a (non-const) reference for that (std::string&, note the ampersand!).

The base case

Whenever we use recursion we need two things: A "base case" and the recursion itself (see the Wikipedia.) Here the base case is a string that is empty. In that case nothing needs to be done.

Processing the current character

std::string_view has the member function back that returns the last character (see cppreference.com).

std::string has the member function append that appends something to the string (see cppreference.com). There are equivalent member functions like push_back (see cppreference.com) or the operator += (see cppreference.com). In this case they do exactly the same.

std::string_view has the member function remove_suffix that conceptually removes some characteas from the end of the std::string_view. That does not modify the underlying string, it just narrows the view.

These three functions combined allow us to get the last character of the input, append it to the output, and remove it from the input.

The recursion

After processing the current character the helper function calls itself recursively. Note that the input is now one character shorter, so the recursion is guaranteed to terminate.

Analysis

The general category of the algorithm is "Linear Recursion": If our original string has n characters the helper function gets called n + 1 times. That makes it easy to reason about the function and its linear runtime complexity.

But this linear recursion is dangerous in C++. If the string is too long we risk a stack overflow (see the Wikipedia). Only do that if you know for sure the string will be relatively short, or if the compiler can and will perform "Tail Call Optimization" (see the Wikipedia.)

Conclusion

This is a valid approach but in practice the recursion is slower than processing the string iteratively. Therefore it is only presented as an interesting idea and to warn about linear recursion in C++.

27th Nov 2024 · Found it useful?