Facebook Pixel

3899. Angles of a Triangle

Medium
LeetCode ↗

Problem Description

You are given a positive integer array sides of length 3.

Your task is to determine whether these three values can serve as the side lengths of a triangle that has a positive area. In other words, the three numbers must form a real triangle (not a degenerate one where all points lie on a straight line).

The rule for checking this is the triangle inequality: for the three sides to form a valid triangle with positive area, the sum of any two sides must be strictly greater than the third side. If we sort the sides as a <= b <= c, it is enough to check the condition a + b > c, since this is the only inequality that could fail when the values are sorted.

  • If such a triangle exists, you must return an array of three floating-point numbers representing the triangle's three internal angles, measured in degrees. These angles must be sorted in non-decreasing order (from smallest to largest).
  • If such a triangle does not exist, you must return an empty array.

Because the answer involves floating-point computation, any returned value that is within 10^-5 of the correct answer will be accepted.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

How We Pick the Algorithm

Why Math / Bit Manipulation?

This problem maps to Math / Bit Manipulation through a short path in the full flowchart.

Math orbittricks?yesNumberproperties?noMath / BitManipulation

Mathematical reasoning or direct calculation produces the answer.

Open in Flowchart

Intuition

The problem naturally splits into two parts: first deciding whether a valid triangle exists, and second computing its three internal angles.

For the existence check, the key observation is the triangle inequality. The condition we need is that the sum of any two sides is strictly greater than the third. If we sort the sides so that a <= b <= c, then the largest side is c, and the only inequality that can possibly fail is a + b > c (the other two inequalities a + c > b and b + c > a are automatically satisfied once the sides are sorted). So a single check a + b <= c tells us when the triangle is invalid, and we return an empty array in that case. Sorting also conveniently helps us produce the angles in non-decreasing order later, because a larger side is always opposite a larger angle.

For computing the angles, we already know all three side lengths, so the natural tool is the law of cosines. For any triangle with sides a, b, c and the angle A opposite to side a, the law of cosines gives:

cosA=b2+c2a22bc\cos A = \frac{b^2 + c^2 - a^2}{2bc}

By applying this twice, we can directly solve for two of the angles A and B using acos, then convert the results from radians to degrees. Since the angle opposite a smaller side is smaller, and we sorted a <= b <= c, the angles come out in the order A <= B <= C naturally.

Finally, instead of computing the third angle C with another law of cosines call, we use the simple fact that the internal angles of any triangle add up to 180 degrees. So we get C = 180 - A - B, which is both faster and avoids extra rounding error.

Solution Approach

Solution 1: Sorting + Math

We first sort the array sides in non-decreasing order, and denote the three side lengths as a, b, and c, where a <= b <= c.

According to the triangle inequality, if a + b <= c, then these three sides cannot form a triangle with positive area, so we return an empty array directly.

Otherwise, the three sides can form a valid triangle. By the law of cosines, we have:

cosA=b2+c2a22bc\cos A = \frac{b^2 + c^2 - a^2}{2bc}
cosB=a2+c2b22ac\cos B = \frac{a^2 + c^2 - b^2}{2ac}

Therefore, we can compute angles A and B separately. We use acos to get each angle in radians, then convert it to degrees using degrees. Finally, using the fact that the sum of the internal angles of a triangle is 180 degrees, we get:

C=180ABC = 180^\circ - A - B

Since the array is sorted as a <= b <= c, and a larger side is opposite a larger angle, the resulting angles satisfy A <= B <= C. So we can directly return [A, B, C] as the answer in non-decreasing order.

The time complexity is O(1) (sorting three elements is constant work), and the space complexity is O(1), ignoring the space for the output array.

Example Walkthrough

Let's trace through the solution approach with a concrete input: sides = [4, 5, 3].

Step 1: Sort the sides

We sort the array in non-decreasing order:

[4,5,3][3,4,5][4, 5, 3] \rightarrow [3, 4, 5]

So we assign a = 3, b = 4, c = 5, which satisfies a <= b <= c.

Step 2: Check the triangle inequality

We only need to verify a + b > c:

a+b=3+4=7>5=ca + b = 3 + 4 = 7 > 5 = c \quad ✓

The condition holds, so a valid triangle with positive area exists. We proceed to compute the angles. (If instead we had something like [1, 2, 3], then 1 + 2 = 3 is not strictly greater than 3, so we would return [].)

Step 3: Compute angle A (opposite side a = 3)

Using the law of cosines:

cosA=b2+c2a22bc=42+5232245=16+25940=3240=0.8\cos A = \frac{b^2 + c^2 - a^2}{2bc} = \frac{4^2 + 5^2 - 3^2}{2 \cdot 4 \cdot 5} = \frac{16 + 25 - 9}{40} = \frac{32}{40} = 0.8
A=arccos(0.8)0.6435 rad36.87°A = \arccos(0.8) \approx 0.6435 \text{ rad} \approx 36.87°

Step 4: Compute angle B (opposite side b = 4)

cosB=a2+c2b22ac=32+5242235=9+251630=1830=0.6\cos B = \frac{a^2 + c^2 - b^2}{2ac} = \frac{3^2 + 5^2 - 4^2}{2 \cdot 3 \cdot 5} = \frac{9 + 25 - 16}{30} = \frac{18}{30} = 0.6
B=arccos(0.6)0.9273 rad53.13°B = \arccos(0.6) \approx 0.9273 \text{ rad} \approx 53.13°

Step 5: Compute angle C using the angle-sum rule

Instead of another law of cosines call, we use the fact that the internal angles sum to 180°:

C=180°AB=180°36.87°53.13°=90°C = 180° - A - B = 180° - 36.87° - 53.13° = 90°

This makes sense: [3, 4, 5] is a classic right triangle, so the largest angle (opposite the largest side 5) is exactly 90°.

Step 6: Return the result

Because we sorted a <= b <= c and a larger side is always opposite a larger angle, the angles already come out in non-decreasing order:

[36.87°,  53.13°,  90.0°][36.87°, \; 53.13°, \; 90.0°]

This is our final answer, with A <= B <= C as required.

Solution Implementation

1import math
2from typing import List
3
4
5class Solution:
6    def internalAngles(self, sides: List[int]) -> List[float]:
7        # Sort the sides so that side_a <= side_b <= side_c
8        sides.sort()
9        side_a, side_b, side_c = sides
10
11        # Triangle inequality check: the sum of the two shorter sides
12        # must be strictly greater than the longest side.
13        # Otherwise, the three lengths cannot form a valid triangle.
14        if side_a + side_b <= side_c:
15            return []
16
17        # Law of Cosines to find angle A (opposite to side_a):
18        #   a^2 = b^2 + c^2 - 2*b*c*cos(A)
19        #   => cos(A) = (b^2 + c^2 - a^2) / (2*b*c)
20        angle_a = math.degrees(
21            math.acos((side_b ** 2 + side_c ** 2 - side_a ** 2) / (2 * side_b * side_c))
22        )
23
24        # Law of Cosines to find angle B (opposite to side_b):
25        #   cos(B) = (a^2 + c^2 - b^2) / (2*a*c)
26        angle_b = math.degrees(
27            math.acos((side_a ** 2 + side_c ** 2 - side_b ** 2) / (2 * side_a * side_c))
28        )
29
30        # The interior angles of a triangle sum to 180 degrees,
31        # so angle C is simply the remainder.
32        angle_c = 180 - angle_a - angle_b
33
34        return [angle_a, angle_b, angle_c]
35
1import java.util.Arrays;
2
3class Solution {
4    /**
5     * Computes the three internal angles (in degrees) of a triangle
6     * given the lengths of its three sides.
7     *
8     * @param sides an array containing exactly three side lengths
9     * @return a double array with the three internal angles in degrees,
10     *         or an empty array if the sides cannot form a valid triangle
11     */
12    public double[] internalAngles(int[] sides) {
13        // Sort the sides in ascending order so that sideC is the longest.
14        Arrays.sort(sides);
15        int sideA = sides[0];
16        int sideB = sides[1];
17        int sideC = sides[2];
18
19        // Triangle inequality check: the sum of the two shorter sides
20        // must be strictly greater than the longest side.
21        if (sideA + sideB <= sideC) {
22            return new double[0];
23        }
24
25        // Apply the Law of Cosines to find the angle opposite each side.
26        // angleA is opposite sideA, angleB is opposite sideB.
27        double angleA = Math.toDegrees(
28                Math.acos((sideB * sideB + sideC * sideC - sideA * sideA)
29                        / (2.0 * sideB * sideC)));
30        double angleB = Math.toDegrees(
31                Math.acos((sideA * sideA + sideC * sideC - sideB * sideB)
32                        / (2.0 * sideA * sideC)));
33
34        // The three angles of a triangle sum to 180 degrees,
35        // so the remaining angle can be derived by subtraction.
36        double angleC = 180.0 - angleA - angleB;
37
38        return new double[] {angleA, angleB, angleC};
39    }
40}
41
1class Solution {
2public:
3    // Computes the three internal angles (in degrees) of a triangle given its side lengths.
4    // Returns an empty vector if the sides cannot form a valid triangle.
5    vector<double> internalAngles(vector<int>& sides) {
6        // Sort the sides in ascending order so that side_a <= side_b <= side_c.
7        sort(sides.begin(), sides.end());
8        int side_a = sides[0];
9        int side_b = sides[1];
10        int side_c = sides[2];
11
12        // Triangle inequality check: the sum of the two shorter sides must
13        // exceed the longest side. Otherwise, no valid triangle exists.
14        if (side_a + side_b <= side_c) {
15            return {};
16        }
17
18        // Pi is obtained via acos(-1.0); used to convert radians to degrees.
19        const double pi = acos(-1.0);
20        const double radians_to_degrees = 180.0 / pi;
21
22        // Apply the Law of Cosines to find each angle (opposite to its same-named side).
23        // angle_a is opposite side_a, angle_b is opposite side_b.
24        double angle_a = acos((1.0 * side_b * side_b + 1.0 * side_c * side_c - 1.0 * side_a * side_a) /
25                              (2.0 * side_b * side_c)) * radians_to_degrees;
26        double angle_b = acos((1.0 * side_a * side_a + 1.0 * side_c * side_c - 1.0 * side_b * side_b) /
27                              (2.0 * side_a * side_c)) * radians_to_degrees;
28
29        // The angles of a triangle sum to 180 degrees, so derive the third directly.
30        double angle_c = 180.0 - angle_a - angle_b;
31
32        // Return the three angles in the order corresponding to the sorted sides.
33        return {angle_a, angle_b, angle_c};
34    }
35};
36
1/**
2 * Computes the three internal angles (in degrees) of a triangle given its side lengths.
3 *
4 * The function uses the Law of Cosines to derive each angle:
5 *   cos(A) = (b² + c² - a²) / (2bc)
6 *
7 * @param sides - An array of three numbers representing the triangle's side lengths.
8 * @returns An array of three angles in degrees, or an empty array if the sides
9 *          cannot form a valid triangle.
10 */
11function internalAngles(sides: number[]): number[] {
12    // Sort the sides in ascending order so that `sideC` is the longest.
13    sides.sort((a, b) => a - b);
14
15    // Destructure the sorted sides into clearly named variables.
16    const [sideA, sideB, sideC]: number[] = sides;
17
18    // Triangle inequality check: the sum of the two shorter sides must
19    // exceed the longest side. Otherwise, a valid triangle cannot exist.
20    if (sideA + sideB <= sideC) {
21        return [];
22    }
23
24    // Conversion factor from radians to degrees.
25    const radianToDegree: number = 180 / Math.PI;
26
27    // Law of Cosines: angle opposite to sideA.
28    const angleA: number =
29        Math.acos((sideB * sideB + sideC * sideC - sideA * sideA) / (2 * sideB * sideC)) *
30        radianToDegree;
31
32    // Law of Cosines: angle opposite to sideB.
33    const angleB: number =
34        Math.acos((sideA * sideA + sideC * sideC - sideB * sideB) / (2 * sideA * sideC)) *
35        radianToDegree;
36
37    // The angles of a triangle sum to 180 degrees, so derive the third angle.
38    const angleC: number = 180 - angleA - angleB;
39
40    // Return the three internal angles in degrees.
41    return [angleA, angleB, angleC];
42}
43

Time and Space Complexity

  • Time Complexity: O(1)

    The input sides always contains exactly 3 elements, so sides.sort() operates on a fixed-size list and takes constant time. The subsequent arithmetic operations—computing angles A, B, and C using acos and degrees—are all constant-time operations independent of any growing input size. Therefore, the overall time complexity is O(1).

  • Space Complexity: O(1)

    The algorithm uses only a fixed number of variables (a, b, c, A, B, C), and the returned list always holds exactly 3 elements. No additional space scales with the input. Hence, the space complexity is O(1).

Common Pitfalls

Pitfall 1: Forgetting that acos may produce a slightly out-of-range argument due to floating-point error

The law of cosines computes cos(A) = (b^2 + c^2 - a^2) / (2*b*c). Mathematically this value always lies within [-1, 1], but because of floating-point rounding, it can occasionally end up as something like 1.0000000002 or -1.0000000001. When such a value is passed to math.acos, Python raises a ValueError: math domain error.

This is especially likely when the triangle is nearly degenerate (e.g., a + b is only marginally greater than c), where the cosine value approaches exactly 1 or -1.

Solution: Clamp the cosine value to the valid [-1, 1] range before calling acos.

import math
from typing import List


class Solution:
    def internalAngles(self, sides: List[int]) -> List[float]:
        sides.sort()
        side_a, side_b, side_c = sides

        if side_a + side_b <= side_c:
            return []

        def safe_acos(x: float) -> float:
            # Clamp into [-1, 1] to avoid math domain errors from rounding.
            return math.acos(max(-1.0, min(1.0, x)))

        angle_a = math.degrees(
            safe_acos((side_b ** 2 + side_c ** 2 - side_a ** 2) / (2 * side_b * side_c))
        )
        angle_b = math.degrees(
            safe_acos((side_a ** 2 + side_c ** 2 - side_b ** 2) / (2 * side_a * side_c))
        )
        angle_c = 180 - angle_a - angle_b

        return [angle_a, angle_b, angle_c]

Pitfall 2: Integer overflow / squaring before the inequality check (language dependent)

In Python this is not an issue because integers have arbitrary precision. However, when porting the solution to languages such as C++, Java, or Go, computing side_b ** 2 + side_c ** 2 with 32-bit integers can overflow when the side lengths are large. Additionally, doing the triangle inequality check as a + b <= c with fixed-width integers can overflow a + b.

Solution: Use a wide enough integer type (e.g., long long in C++ / long in Java) for the squared terms, or perform the inequality check in a way that avoids addition overflow, such as a > c - b. In Python, no special handling is required.

Pitfall 3: Misordering the returned angles

The approach relies on the geometric fact that a larger side is opposite a larger angle. After sorting a <= b <= c, the angles automatically satisfy A <= B <= C. A common mistake is to compute the angles from an unsorted array (or to compute C first and naively place it last without sorting the sides), which breaks the required non-decreasing order of the output.

Solution: Always sort sides first, then compute A (opposite a), B (opposite b), and C = 180 - A - B. This guarantees the output [A, B, C] is already in non-decreasing order without an extra sort.

Pitfall 4: Using > instead of >= (or vice versa) for the degenerate case

The problem requires a triangle with positive area, so a degenerate triangle where a + b == c (all points collinear) must be rejected. Using a + b < c instead of a + b <= c would incorrectly accept these flat "triangles," which have zero area.

Solution: Use the strict comparison if side_a + side_b <= side_c: return [] to reject both impossible and degenerate cases.

Ready to land your dream job?

Unlock your dream job with a 5-minute quiz for a personalized study roadmap!

Get My Roadmap
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Get a Personalized Study Roadmap:

Which of the following is a good use case for backtracking?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More