</> MAANG.io
coding interview · 301

Advanced

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/95 solved 0% complete

Angular and Two-Pointer

What is this?

Both problems here are easy once you stop looking at the data you were given and look at a different axis. Points scattered on a plane become a one-dimensional list of angles. A quadratic applied to a sorted array is not sorted — but it is monotonic outward from its vertex, which means the extremes sit at the two ends and can be collected inward.

Neither needs a clever data structure. Both need you to notice which ordering already exists.

flowchart TD A["the given axis is awkward"] --> B["Maximum Number of Visible Points"] A --> C["Sort Transformed Array"] B --> B1["convert points to ANGLES from the viewer"] B1 --> B2["sort, then append every angle + 360"] B2 --> B3["an ordinary window now spans the wrap"] C --> C1["a parabola is monotonic outward from its vertex"] C1 --> C2["extremes are at the two ends"] C2 --> C3["two pointers fill the result outside-in"]

💡 Fun fact: the + 360 duplication is the angular version of a trick that appears throughout the curriculum — linearise a circular problem by writing the data twice. It is the same move that makes circular next-greater-element work by iterating 2n times with % n, and the same reason a doubled string contains every rotation of itself. Whenever wraparound is the only complication, duplicating the sequence usually removes it entirely.

🔓 The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: find the axis on which the data is already ordered — the angle from the viewer, or the distance from a parabola's vertex — and a standard linear technique applies without any new machinery.


1. Points become angles

import math
angles = []
same = 0
for x, y in points:
    if [x, y] == location:
        same += 1                      # a point AT the viewer is always visible
        continue
    angles.append(math.degrees(math.atan2(y - location[1], x - location[0])))

angles.sort()
angles += [a + 360 for a in angles]    # linearise the wraparound

best = left = 0
for right in range(len(angles)):
    while angles[right] - angles[left] > angle:
        left += 1
    best = max(best, right - left + 1)
return best + same

Three details decide correctness. atan2 handles all four quadrants and the vertical case, where a naive atan(y/x) divides by zero. Points located exactly at the viewer have no defined angle and are visible regardless — count them separately and add at the end. And the duplication must happen after sorting, so the appended copies stay in order.

Floating point deserves a word: comparing angles[right] - angles[left] > angle on values produced by atan2 can misjudge a point exactly on the boundary. Where the problem's tolerance matters, compare with a small epsilon.

2. A parabola is monotonic from its ends

Applying f(x) = ax² + bx + c to a sorted array does not give a sorted result — but the shape is predictable. When a > 0 the parabola opens upward, so the largest values are at the two ends of the input and the smallest is somewhere in the middle. When a < 0 it is the reverse.

So fill the output from the outside in:

res = [0] * len(nums)
i, j = 0, len(nums) - 1
write = len(nums) - 1 if a >= 0 else 0
step = -1 if a >= 0 else 1
while i <= j:
    fi, fj = f(nums[i]), f(nums[j])
    if (a >= 0) == (fi > fj):          # take whichever end wins for this orientation
        res[write] = fi; i += 1
    else:
        res[write] = fj; j -= 1
    write += step
return res

a == 0 makes it linear, and folding it in with a >= 0 handles it correctly — the values are then monotonic in the same direction as the input, and the comparison still picks the right end.


3. A 30-second worked example (the wrap)

Angles at 350° and 10°, with a field of view of 30°. On the raw sorted list [10, 350] the gap reads as 340° and the window sees one point.

sorted:          [10, 350]
+360 duplicate:  [10, 350, 370, 710]

window with angle = 30:
   left=350, right=370  →  370 - 350 = 20 <= 30  ✓  two points visible

The two points are 20° apart across the zero mark, and the duplication is what lets a left-to-right window see it. Without it, no window over [10, 350] ever contains both.


4. Where you'll actually meet this

  • Games and simulation. Field-of-view and line-of-sight queries — how many entities fall inside a cone from the player.
  • Robotics. LIDAR and sensor sweeps, where readings are naturally angular and the zero crossing is a real source of bugs.
  • Radar and surveillance. Counting contacts within a bearing window.
  • Computational geometry. Angular sweeps underpin convex hull and visibility algorithms.
  • Signal processing. Applying a quadratic response curve to sorted measurements and needing the result ordered.

5. Problems in this chapter

▶ Maximum Number of Visible Points

Most points visible within a fixed angular field of view. Convert to angles with atan2, sort, append every angle + 360, then run an ordinary window; count points at the viewer separately.
Pattern: angular window with wraparound duplication. Target: O(n log n) time, O(n) space.

▶ Sort Transformed Array

Apply ax² + bx + c to a sorted array and return the sorted result. Two pointers fill outside-in, because a parabola is monotonic outward from its vertex.
Pattern: two-pointer fill by orientation. Target: O(n) time — better than the O(n log n) transform-then-sort.


6. Common pitfalls 🚫

  • Using atan instead of atan2. It loses the quadrant and divides by zero on vertical alignments.
  • Forgetting points at the viewer. They have no angle, are always visible, and must be added after the window.
  • Duplicating before sorting. The appended copies must preserve order or the window breaks.
  • Ignoring floating-point tolerance on points exactly at the field-of-view boundary.
  • Sorting after the transform. Correct, and O(n log n) for a problem that is O(n).
  • Handling a == 0 as a special case. Folding it in with a >= 0 works; a separate branch is easy to get backwards.
  • Writing the output forwards when a > 0. The largest values come first, so the write pointer must start at the end and move backwards.

7. Key takeaways

  1. Change the axis. Points become angles; a parabola becomes distance from its vertex.
  2. Duplicate to linearise a circle. + 360 here, 2n iterations elsewhere — the same idea.
  3. atan2, always, for anything angular.
  4. Handle the degenerate point — one located exactly at the viewer — outside the main loop.
  5. A parabola's extremes are at the input's ends, which is what makes an outside-in fill correct.
  6. Two pointers beat a sort whenever the output ordering can be derived rather than computed.
  7. Why interviewers like it: both problems look geometric and are solved by ordinary linear techniques once the right ordering is spotted. That spotting is the whole test.

Order: Maximum Number of Visible Points → Sort Transformed Array.

Sort Transformed Array

Core idea: You're given an array nums that is already sorted ascending, and a quadratic f(x) = ax² + bx + c. The naive move is to map every element through f and sort() the results in O(n log n). But a parabola is monotonic on each side of its vertex — it falls toward the vertex and rises away from it (or the reverse when it opens downward). So as you walk inward from the two ends of a sorted input, f changes monotonically on each side, which means the extreme outputs always come from the extreme inputs. Put one pointer at each end. If a >= 0 the parabola opens up, so the largest outputs sit at the ends — compare f(left) and f(right), take the larger, and fill the result from the back. If a < 0 it opens down, so the smallest outputs sit at the ends — take the smaller and fill from the front. Each step consumes one end and advances; n steps total → O(n). The whole trick is the a >= 0 versus a < 0 fill direction.


The problem, rephrased

You manage a list of sensor offsets that are already sorted from most-negative to most-positive. A calibration pass runs every reading through the same fixed formula f(x) = ax² + bx + c — squaring amplifies extremes, the linear term shifts, the constant biases. After calibration you still need the readings sorted, because everything downstream (binary search, percentile cuts, merge joins) assumes order. Re-sorting from scratch is O(n log n); you want to exploit the fact that the input was already sorted and the formula is a plain parabola.

Formally: given an integer array nums sorted in non-decreasing order and integers a, b, c, return the array f(nums[i]) = a·nums[i]² + b·nums[i] + c sorted in non-decreasing order, in O(n) time.

This is LeetCode 360 — Sort Transformed Array.

Input Means Output
nums = [-4,-2,2,4], a=1, b=3, c=5 Opens up (a>0); extremes are the largest [3, 9, 15, 33]
nums = [-4,-2,2,4], a=-1, b=3, c=5 Opens down (a<0); extremes are the smallest [-23, -5, 1, 7]
nums = [-3,-1,2,4], a=0, b=2, c=1 Degenerate: f is linear, just monotone [-5, -1, 5, 9]

The sign of a is the whole story: it decides which end of the sorted output the input extremes land on, and therefore which direction you fill.


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.