LeetCode 611 - Valid Triangle Number
The problem asks us to determine how many triplets from a given integer array nums can form a valid triangle. In geometric terms, a triangle is valid if the sum of any two sides is greater than the third side.
Difficulty: 🟡 Medium
Topics: Array, Two Pointers, Binary Search, Greedy, Sorting
Solution
Problem Understanding
The problem asks us to determine how many triplets from a given integer array nums can form a valid triangle. In geometric terms, a triangle is valid if the sum of any two sides is greater than the third side. Because we are only concerned with side lengths from an array, the key condition is that for three numbers a, b, and c (where a <= b <= c), the triangle inequality reduces to a + b > c.
The input nums is an array of integers representing potential side lengths, and the output is a single integer: the count of distinct triplets that satisfy the triangle inequality.
The constraints tell us that the array has at most 1000 elements, and the numbers are non-negative integers up to 1000. This suggests that a naive triple-loop solution (O(n^3)) may be too slow for the worst case, but approaches that reduce the number of comparisons will work efficiently.
Important edge cases include arrays with zeros, arrays where all numbers are equal, arrays with fewer than three numbers, and arrays where no combination can form a triangle.
Approaches
The brute-force approach is straightforward: iterate over all possible triplets (i, j, k) and check whether the three numbers satisfy the triangle inequality. This guarantees correctness because it exhaustively checks every combination, but the time complexity is O(n^3), which will be slow for n = 1000.
The optimal approach leverages sorting. After sorting nums, we can fix the largest side nums[k] and use two pointers i and j to find valid pairs (nums[i], nums[j]) such that nums[i] + nums[j] > nums[k]. The key insight is that once nums[i] + nums[j] > nums[k], all pairs between i and j satisfy the condition due to the sorted order. This reduces the time complexity to O(n^2) and is efficient enough for the constraints.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^3) | O(1) | Iterate over all triplets, check triangle inequality |
| Optimal | O(n^2) | O(1) | Sort array and use two pointers to count valid pairs efficiently |
Algorithm Walkthrough
- Sort the array
numsin ascending order. This ensures that for any triplet(nums[i], nums[j], nums[k])withi < j < k,nums[i] <= nums[j] <= nums[k]. - Initialize a variable
countto 0. This will hold the total number of valid triangles. - Iterate
kfromlen(nums) - 1down to 2, treatingnums[k]as the largest side of the triangle. - For each
k, initialize two pointers:i = 0andj = k - 1. - While
i < j, check ifnums[i] + nums[j] > nums[k].
- If true, all pairs
(i, i+1, ..., j-1)combined withjandkform valid triangles. Incrementcountbyj - iand decrementjby 1 to consider the next smaller second side. - If false, increment
iby 1 to increase the sum and try again.
- Continue until
i >= j, then move to the nextk. - Return
count.
Why it works: Sorting ensures that as the sum of the two smaller sides increases, all intermediate pairs between i and j satisfy the triangle inequality. This invariant allows us to count multiple valid triangles in one step without checking each explicitly.
Python Solution
from typing import List
class Solution:
def triangleNumber(self, nums: List[int]) -> int:
nums.sort()
count = 0
n = len(nums)
for k in range(n - 1, 1, -1):
i, j = 0, k - 1
while i < j:
if nums[i] + nums[j] > nums[k]:
count += j - i
j -= 1
else:
i += 1
return count
The Python implementation first sorts the array and initializes count to accumulate valid triangles. It iterates over potential largest sides and uses a two-pointer approach to efficiently find the number of valid triangles for each fixed largest side. Incrementing count by j - i avoids explicitly checking all smaller combinations, exploiting the sorted order to guarantee correctness.
Go Solution
import "sort"
func triangleNumber(nums []int) int {
sort.Ints(nums)
count := 0
n := len(nums)
for k := n - 1; k >= 2; k-- {
i, j := 0, k - 1
for i < j {
if nums[i] + nums[j] > nums[k] {
count += j - i
j--
} else {
i++
}
}
}
return count
}
In Go, sorting is done via sort.Ints. The two-pointer technique works the same as in Python. Go handles slices naturally, so no additional data structures are needed. Integer overflow is not an issue because nums[i] and nums[j] are bounded by 1000.
Worked Examples
Example 1: nums = [2,2,3,4]
- Sorted: [2,2,3,4]
- k = 3 (nums[k] = 4), i=0, j=2
- nums[i]+nums[j]=2+3=5>4 → count += 2 → j=1
- nums[i]+nums[j]=2+2=4 not > 4 → i=1
- i=j → move to next k
- k=2 (nums[k]=3), i=0, j=1
- nums[i]+nums[j]=2+2=4>3 → count += 1 → j=0
- i>=j → done
- Total count = 3
Example 2: nums = [4,2,3,4]
- Sorted: [2,3,4,4]
- k=3 (nums[k]=4), i=0, j=2
- nums[i]+nums[j]=2+4=6>4 → count += 2 → j=1
- nums[i]+nums[j]=2+3=5>4 → count +=1 → j=0
- k=2 (nums[k]=4), i=0, j=1
- nums[i]+nums[j]=2+3=5>4 → count +=1 → j=0
- Total count = 4
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n^2) | Sorting takes O(n log n), and the two-pointer scan for each largest side takes O(n), resulting in O(n^2) overall. |
| Space | O(1) | No extra data structures beyond variables; sorting in-place is O(1) space. |
The two-pointer approach avoids the O(n^3) brute-force iterations while still counting all valid triangles by leveraging the sorted order.
Test Cases
# Provided examples
assert Solution().triangleNumber([2,2,3,4]) == 3 # Example 1
assert Solution().triangleNumber([4,2,3,4]) == 4 # Example 2
# Edge and boundary cases
assert Solution().triangleNumber([]) == 0 # empty array
assert Solution().triangleNumber([0,0,0]) == 0 # all zeros
assert Solution().triangleNumber([1,1,1]) == 1 # minimum non-zero triangle
assert Solution().triangleNumber([3,4,5,6,10]) == 6 # multiple triangles
assert Solution().triangleNumber([1]*1000) == 166167000 # large array with same numbers
| Test | Why |
|---|---|
| [] | Tests empty input handling |
| [0,0,0] | Tests zeros cannot form a triangle |
| [1,1,1] | Tests minimal valid triangle |
| [3,4,5,6,10] | Checks general multiple triangles |
| [1]*1000 | Stress test with large input size, all equal elements |
Edge Cases
One important edge case is when the array contains zeros. A zero cannot form a triangle with any other side, so the algorithm correctly ignores such cases because the sum of any two positive numbers must be greater than zero, but if a side is zero, the triangle inequality fails.
Another edge case is arrays with fewer than three elements. Since a triangle requires three sides, the algorithm will naturally return zero because the outer loop starts from index n-1 down to 2. If n < 3, the loop does not execute.
A third edge case is arrays where all numbers are equal. Here, the triangle inequality holds for any triplet. The algorithm efficiently counts all combinations without repeated explicit checks, and the sorted array ensures that the two-pointer counting method correctly captures all valid triangles.