Skip to main content

A C++ Interview Question That Tests Reasoning, Not Tricks

Based on a LinkedIn post originally published on 7 January 2026

During a technical interview, I was asked to solve a deceptively simple C++ problem:

Given a sorted array of integers in ascending order, produce a new array containing the square of each value. The output must also be sorted in ascending order and contain only unique values.

At first glance, the solution appears obvious:

  1. Iterate through the input.

  2. Square every value.

  3. Sort the resulting array.

  4. Remove duplicates.

That approach would produce the correct result. However, it would also ignore the most useful information provided by the problem: the input is already sorted.

The interesting part of the exercise is recognising that this is not really a sorting problem. It is a two-pointer problem.

Why squaring destroys the original order

Consider this input:

-7, -4, -1, 0, 3, 5

It is already sorted in ascending order.

Squaring the values in their existing sequence produces:

49, 16, 1, 0, 9, 25

The result is no longer sorted.

This happens because the magnitude of a negative number increases as we move towards the beginning of the input array. After squaring, a large negative value may become larger than every positive value.

The largest remaining square must therefore come from one of the two ends of the input:

  • the negative value with the greatest absolute magnitude on the left; or

  • the largest positive value on the right.

That observation leads naturally to a two-pointer solution.

The two-pointer approach

We place one pointer at the beginning of the input and another at the end.

At every step:

  1. Square the value at the left pointer.

  2. Square the value at the right pointer.

  3. Compare the two squares.

  4. Add the larger square to an intermediate result.

  5. Move the pointer associated with the selected value.

  6. If both squares are equal, move both pointers.

Because the largest value is selected first, the intermediate result is produced in descending order.

Once every input value has been considered, the result can be reversed to obtain ascending order.

Duplicates can be removed while the descending result is being constructed. Because equal squared values appear next to one another during the merge, no separate set or additional deduplication pass is required.

A C++17 implementation

#include <cstddef>
#include <vector>

auto sortedUniqueSquares(const std::vector<int>& input)
    -> std::vector<long long>
{
    if (input.empty()) {
        return {};
    }

    std::vector<long long> descending;
    descending.reserve(input.size());

    std::ptrdiff_t left{0};
    std::ptrdiff_t right{
        static_cast<std::ptrdiff_t>(input.size()) - 1
    };

    while (left <= right) {
        const auto leftValue{
            static_cast<long long>(input[left])
        };
        const auto rightValue{
            static_cast<long long>(input[right])
        };

        const auto leftSquare{leftValue * leftValue};
        const auto rightSquare{rightValue * rightValue};

        long long selected{};

        if (leftSquare > rightSquare) {
            selected = leftSquare;
            ++left;
        } else if (rightSquare > leftSquare) {
            selected = rightSquare;
            --right;
        } else {
            selected = leftSquare;
            ++left;
            --right;
        }

        if (descending.empty() ||
            descending.back() != selected) {
            descending.push_back(selected);
        }
    }

    return {
        descending.rbegin(),
        descending.rend()
    };
}

Walking through an example

Suppose the input is:

-7, -4, -4, -1, 0, 3, 5

The initial comparison is between -7 and 5:

(-7)² = 49
5²    = 25

The algorithm selects 49 and advances the left pointer.

The next comparison is between -4 and 5:

(-4)² = 16
5²    = 25

It selects 25 and moves the right pointer.

The process continues, producing an intermediate descending sequence:

49, 25, 16, 9, 1, 0

The repeated -4 does not create another 16 because duplicates are discarded as the intermediate result is constructed.

Reversing the sequence produces the required output:

0, 1, 9, 16, 25, 49

Complexity

Let n be the number of input values.

The two pointers move towards each other, and every input element is considered at most once.

The time complexity is therefore:

O(n)

The output and intermediate storage require:

O(n)

A solution that squares every value and then applies a general-purpose sort would require:

O(n log n)

The difference may not matter for a tiny interview example, but recognising and using existing invariants is an important engineering habit.

Edge cases worth discussing

A good implementation should account for more than the simplest example.

Empty input

An empty input should return an empty result immediately.

One value

A single value should produce a single squared value.

Duplicate values

Repeated input values may produce repeated squares and must be removed.

For example:

-3, -3, 3, 3

must produce:

9

Different values with identical squares

A negative and positive version of the same value also produce identical squares:

-4, 4

The result must contain only one 16.

Zero

Zero sits at the point where the ordering behaviour changes, but it requires no special branch in the two-pointer algorithm.

Integer overflow

Squaring a large int directly as an int may overflow before the result is assigned to a wider type.

For that reason, each input value is converted to long long before multiplication:

const auto value{
    static_cast<long long>(input[index])
};

const auto square{value * value};

The order of these operations matters. Casting only after multiplication would be too late.

Pointer underflow

Container sizes and indexes are frequently represented using unsigned types. Decrementing an unsigned index below zero causes it to wrap to a very large value.

The implementation uses std::ptrdiff_t for the two moving indexes so that the right pointer can safely move below zero when the final element has been processed.

What the question really tests

The code itself is not especially large. The value of the exercise lies in the reasoning behind it.

It tests whether a candidate can:

  • recognise that squaring changes the ordering around zero;

  • use the fact that the input is already sorted;

  • identify a two-pointer or merge-style solution;

  • compare algorithmic complexity;

  • handle duplicates without unnecessary data structures;

  • consider overflow and index safety;

  • and communicate the reasoning clearly.

This is why I appreciated the question.

It did not depend on obscure syntax or a memorised trick. It tested whether the candidate could identify the important properties of the data and design an efficient, readable solution around them.

The connection to real engineering

Production engineering problems are rarely solved by applying the most obvious generic operation.

Good solutions often begin by identifying what is already known:

  • Is the data ordered?

  • Is a range bounded?

  • Are values unique?

  • Is processing sequential?

  • Can earlier work be reused?

  • Which invariants remain true as the system changes?

Understanding those properties can eliminate unnecessary work and lead to simpler designs.

The broader lesson is not merely that two pointers are faster than sorting in this particular exercise. It is that engineers should make deliberate use of the structure already present in a problem.

Clear reasoning under constraints is more valuable than cleverness—and much closer to the work required in real systems.

AI Assistance Disclosure

This article is based on my original ideas, experience, analysis and conclusions. Artificial intelligence tools were subsequently used as editorial and research assistants to review grammar and wording, improve structure and presentation, organise some arguments into clearer logical sections, and help review references to legal, regulatory and technical concepts.

Where relevant, factual and regulatory references were checked against the sources cited in the article. AI assistance does not replace professional legal, regulatory, financial or technical advice, and the final selection, interpretation, opinions and conclusions presented here remain my own.

Comments

Popular posts from this blog

Movies - The Bubble (2022)

  Back to Evolution (2001) .

IT - Fixing Windows Error 1327: Account Restrictions Are Preventing This User from Signing In

Fixing Windows Error 1327: Account Restrictions Are Preventing This User from Signing In Introduction Error 1327, “Account restrictions are preventing this user from signing in,” is a perplexing and disruptive issue that occurs on some Windows 10 and Windows 11 machines. The message typically appears at login or while connecting to remote resources, like shared folders, network drives, or remote desktops. Table of Contents Symptoms of Error 1327 Common Causes Step-by-Step Troubleshooting Advanced Fixes Automation via PowerShell Prevention Tips Further Reading Symptoms of Error 1327 Users experiencing this error may encounter one or more of the following: Login screen fails after credentials are entered. Error message appears when accessing mapped drives or network resources. Remote Desktop Connection (RDP) is rejected with the 1327 message. Group Policy logon restrictions silently block access. Co...

ATA Drive Capacity Limitations

ATA interface versions up through ATA-5 suffered from a drive capacity limitation of about 137GB (billion bytes). Depending on the BIOS used, you can further reduce this limitation to 8.4GB, or even as low as 528MB (million bytes). This is due to limitations in both the BIOS and the ATA interface, which when combined create even further limitations. To understand these limits, you have to look at the BIOS (software) and ATA (hardware) interfaces together. NOTE In addition to the BIOS/ATA limitations discussed in this section, various operating system limitations exist. These are described later in this chapter. The limitations when dealing with ATA drives are those of the ATA interface as well as the BIOS interface used to talk to the drive. A summary of the limitations is shown in Table 7.12. Table 7.12. ATA/IDE Capacity Limitations for Various Sector Addressing Methods Sector Addressing Method Total Sectors Calculation Maximum Total Sectors Maximum Capacity (Byte...