469. Convex Polygon 🔒
Problem Description
You are given an array of points representing vertices of a polygon in the X-Y coordinate plane. Each point is given as points[i] = [xi, yi]
, and when you connect these points in the order they appear in the array, they form a polygon.
Your task is to determine whether this polygon is convex or not. Return true
if the polygon is convex, and false
otherwise.
A convex polygon is one where all interior angles are less than 180 degrees. In other words, if you walk along the boundary of the polygon, you always turn in the same direction (either always left or always right) at each vertex. No portion of the polygon "caves inward."
The problem guarantees that:
- The given points form a simple polygon (no self-intersections)
- Exactly two edges meet at each vertex
- Edges don't intersect each other except at vertices
The solution uses the cross product method to check convexity. For each set of three consecutive vertices, it calculates the cross product of the vectors formed by these points. The sign of the cross product indicates the direction of the turn:
- Positive cross product = left turn
- Negative cross product = right turn
- Zero cross product = collinear points (straight line)
The polygon is convex if and only if all turns are in the same direction (all cross products have the same sign, ignoring zeros). The code iterates through all vertices, computing (P[i+1] - P[i]) × (P[i+2] - P[i])
for each vertex P[i]
. If it ever finds two non-zero cross products with opposite signs, the polygon is not convex.
Intuition
To understand if a polygon is convex, imagine walking along its perimeter. If you're always turning in the same direction at each corner (either always turning left or always turning right), then the polygon is convex. If you sometimes turn left and sometimes turn right, the polygon is concave.
How can we mathematically determine the direction of a turn? This is where the cross product comes in. When we have three consecutive points A
, B
, and C
, we can form two vectors: vector AB
(from A to B) and vector AC
(from A to C). The cross product of these vectors tells us about the orientation:
- If
AB × AC > 0
: We're making a counter-clockwise (left) turn at point B - If
AB × AC < 0
: We're making a clockwise (right) turn at point B - If
AB × AC = 0
: The three points are collinear (on the same line)
The key insight is that for a convex polygon, all turns must be in the same direction. So we need to check every set of three consecutive vertices and ensure their cross products all have the same sign.
The cross product in 2D is calculated as: (x1 * y2 - x2 * y1)
, where (x1, y1)
and (x2, y2)
are our two vectors. This gives us a scalar value whose sign indicates the turn direction.
Why does this work? Because in a convex polygon, the interior angles are all less than 180°, meaning we never "flip" our turning direction. The moment we detect a sign change in our cross products (one positive and one negative), we know the polygon must have at least one reflex angle (greater than 180°), making it concave.
The algorithm cleverly uses modulo arithmetic (i + 1) % n
and (i + 2) % n
to wrap around the polygon, ensuring we check the turn at every vertex, including the turns involving the last and first points.
Solution Approach
The implementation follows a systematic approach to check the convexity of the polygon by examining the cross product at each vertex:
1. Initialize tracking variables:
n
stores the number of points in the polygonpre
keeps track of the previous non-zero cross product (initialized to 0)cur
stores the current cross product being calculated
2. Iterate through each vertex:
The algorithm loops through all n
vertices using index i
. For each vertex at position i
, we need to examine three consecutive points:
- Current point:
points[i]
- Next point:
points[(i + 1) % n]
- Point after next:
points[(i + 2) % n]
The modulo operator %
ensures we wrap around to the beginning when we reach the end of the array.
3. Calculate vectors from current point:
For each vertex i
, we compute two vectors:
- Vector 1: From
points[i]
topoints[(i + 1) % n]
x1 = points[(i + 1) % n][0] - points[i][0]
y1 = points[(i + 1) % n][1] - points[i][1]
- Vector 2: From
points[i]
topoints[(i + 2) % n]
x2 = points[(i + 2) % n][0] - points[i][0]
y2 = points[(i + 2) % n][1] - points[i][1]
4. Compute the cross product:
The 2D cross product is calculated as: cur = x1 * y2 - x2 * y1
This value tells us the turn direction at the middle point of our three consecutive vertices.
5. Check consistency of turn direction:
- If
cur != 0
(non-collinear points):- Compare
cur * pre
to check if both have the same sign - If
cur * pre < 0
, it means we have opposite turn directions (one positive, one negative), so the polygon is not convex - returnFalse
- Otherwise, update
pre = cur
to remember this non-zero cross product for future comparisons
- Compare
6. Return the result:
If we complete the loop without finding conflicting turn directions, the polygon is convex - return True
.
The elegance of this solution lies in using the sign of cross products to detect turn direction changes. By multiplying cur * pre
, we get a negative result only when the signs differ, immediately identifying a concave polygon without needing to track the actual turn direction explicitly.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with a simple square polygon with vertices at points = [[0,0], [2,0], [2,2], [0,2]]
.
Initial Setup:
n = 4
(number of vertices)pre = 0
(no previous cross product yet)
Iteration 1 (i=0): Examining turn at vertex [2,0]
- Three points: [0,0], [2,0], [2,2]
- Vector 1: [2,0] - [0,0] = (2, 0)
- Vector 2: [2,2] - [0,0] = (2, 2)
- Cross product:
cur = 2*2 - 0*2 = 4
- Since
cur ≠ 0
andpre = 0
, we setpre = 4
Iteration 2 (i=1): Examining turn at vertex [2,2]
- Three points: [2,0], [2,2], [0,2]
- Vector 1: [2,2] - [2,0] = (0, 2)
- Vector 2: [0,2] - [2,0] = (-2, 2)
- Cross product:
cur = 0*2 - 2*(-2) = 4
- Check:
cur * pre = 4 * 4 = 16 > 0
✓ (same sign) - Update
pre = 4
Iteration 3 (i=2): Examining turn at vertex [0,2]
- Three points: [2,2], [0,2], [0,0]
- Vector 1: [0,2] - [2,2] = (-2, 0)
- Vector 2: [0,0] - [2,2] = (-2, -2)
- Cross product:
cur = (-2)*(-2) - 0*(-2) = 4
- Check:
cur * pre = 4 * 4 = 16 > 0
✓ (same sign) - Update
pre = 4
Iteration 4 (i=3): Examining turn at vertex [0,0]
- Three points: [0,2], [0,0], [2,0] (wraps around)
- Vector 1: [0,0] - [0,2] = (0, -2)
- Vector 2: [2,0] - [0,2] = (2, -2)
- Cross product:
cur = 0*(-2) - (-2)*2 = 4
- Check:
cur * pre = 4 * 4 = 16 > 0
✓ (same sign)
All cross products are positive (value 4), indicating consistent left turns. The algorithm returns True
- the square is convex.
Counter-example with a concave polygon: points = [[0,0], [2,1], [1,0], [2,-1]]
When we reach the turn at vertex [1,0], we get:
- Cross product:
cur = -2
(right turn) - Previous was positive (left turn)
- Check:
cur * pre < 0
→ ReturnFalse
immediately
The algorithm detects the sign change and correctly identifies this as a non-convex polygon.
Solution Implementation
1class Solution:
2 def isConvex(self, points: List[List[int]]) -> bool:
3 """
4 Determines if a polygon defined by given points is convex.
5 A polygon is convex if all interior angles are less than 180 degrees,
6 which means all cross products should have the same sign.
7
8 Args:
9 points: List of [x, y] coordinates representing polygon vertices
10
11 Returns:
12 True if the polygon is convex, False otherwise
13 """
14 n = len(points)
15 previous_cross_product = 0
16
17 # Check each consecutive triplet of points
18 for i in range(n):
19 # Get three consecutive points (wrapping around using modulo)
20 point_a = points[i]
21 point_b = points[(i + 1) % n]
22 point_c = points[(i + 2) % n]
23
24 # Calculate vectors from point_a to point_b and point_a to point_c
25 vector_ab_x = point_b[0] - point_a[0]
26 vector_ab_y = point_b[1] - point_a[1]
27 vector_ac_x = point_c[0] - point_a[0]
28 vector_ac_y = point_c[1] - point_a[1]
29
30 # Calculate cross product of the two vectors
31 # Positive value indicates counter-clockwise turn, negative indicates clockwise
32 current_cross_product = vector_ab_x * vector_ac_y - vector_ac_x * vector_ab_y
33
34 # Skip if cross product is zero (points are collinear)
35 if current_cross_product != 0:
36 # Check if the sign of cross product has changed (indicating concavity)
37 if current_cross_product * previous_cross_product < 0:
38 return False
39 previous_cross_product = current_cross_product
40
41 return True
42
1class Solution {
2 public boolean isConvex(List<List<Integer>> points) {
3 int numPoints = points.size();
4 long previousCrossProduct = 0;
5 long currentCrossProduct = 0;
6
7 // Check each consecutive triplet of points
8 for (int i = 0; i < numPoints; ++i) {
9 // Get three consecutive points (wrapping around at the end)
10 List<Integer> point1 = points.get(i);
11 List<Integer> point2 = points.get((i + 1) % numPoints);
12 List<Integer> point3 = points.get((i + 2) % numPoints);
13
14 // Calculate vectors from point1 to point2 and point1 to point3
15 int vectorX1 = point2.get(0) - point1.get(0);
16 int vectorY1 = point2.get(1) - point1.get(1);
17 int vectorX2 = point3.get(0) - point1.get(0);
18 int vectorY2 = point3.get(1) - point1.get(1);
19
20 // Calculate cross product of the two vectors
21 // Cross product determines the orientation (clockwise or counterclockwise)
22 currentCrossProduct = (long)vectorX1 * vectorY2 - (long)vectorX2 * vectorY1;
23
24 // Skip if cross product is zero (collinear points)
25 if (currentCrossProduct != 0) {
26 // If signs of consecutive cross products differ, polygon is not convex
27 if (currentCrossProduct * previousCrossProduct < 0) {
28 return false;
29 }
30 previousCrossProduct = currentCrossProduct;
31 }
32 }
33
34 // All cross products have the same sign, polygon is convex
35 return true;
36 }
37}
38
1class Solution {
2public:
3 bool isConvex(vector<vector<int>>& points) {
4 int n = points.size();
5 long long previousCrossProduct = 0;
6 long long currentCrossProduct = 0;
7
8 // Iterate through all vertices of the polygon
9 for (int i = 0; i < n; ++i) {
10 // Calculate vectors from current point to next two points
11 // Vector 1: from points[i] to points[i+1]
12 int dx1 = points[(i + 1) % n][0] - points[i][0];
13 int dy1 = points[(i + 1) % n][1] - points[i][1];
14
15 // Vector 2: from points[i] to points[i+2]
16 int dx2 = points[(i + 2) % n][0] - points[i][0];
17 int dy2 = points[(i + 2) % n][1] - points[i][1];
18
19 // Calculate cross product of the two vectors
20 // Cross product determines the orientation (clockwise or counterclockwise)
21 currentCrossProduct = static_cast<long long>(dx1) * dy2 -
22 static_cast<long long>(dx2) * dy1;
23
24 // Skip if cross product is zero (collinear points)
25 if (currentCrossProduct != 0) {
26 // Check if orientation changes (sign of cross product changes)
27 // If signs are different, polygon is not convex
28 if (currentCrossProduct * previousCrossProduct < 0) {
29 return false;
30 }
31 previousCrossProduct = currentCrossProduct;
32 }
33 }
34
35 // All cross products have the same sign, polygon is convex
36 return true;
37 }
38};
39
1function isConvex(points: number[][]): boolean {
2 const n = points.length;
3 let previousCrossProduct = 0;
4 let currentCrossProduct = 0;
5
6 // Iterate through all vertices of the polygon
7 for (let i = 0; i < n; i++) {
8 // Calculate vectors from current point to next two points
9 // Vector 1: from points[i] to points[i+1]
10 const dx1 = points[(i + 1) % n][0] - points[i][0];
11 const dy1 = points[(i + 1) % n][1] - points[i][1];
12
13 // Vector 2: from points[i] to points[i+2]
14 const dx2 = points[(i + 2) % n][0] - points[i][0];
15 const dy2 = points[(i + 2) % n][1] - points[i][1];
16
17 // Calculate cross product of the two vectors
18 // Cross product determines the orientation (clockwise or counterclockwise)
19 currentCrossProduct = dx1 * dy2 - dx2 * dy1;
20
21 // Skip if cross product is zero (collinear points)
22 if (currentCrossProduct !== 0) {
23 // Check if orientation changes (sign of cross product changes)
24 // If signs are different, polygon is not convex
25 if (currentCrossProduct * previousCrossProduct < 0) {
26 return false;
27 }
28 previousCrossProduct = currentCrossProduct;
29 }
30 }
31
32 // All cross products have the same sign, polygon is convex
33 return true;
34}
35
Time and Space Complexity
Time Complexity: O(n)
The algorithm iterates through all n
vertices of the polygon exactly once using a single for loop. Within each iteration, it performs:
- Array indexing operations with modulo:
O(1)
- Basic arithmetic operations (subtraction and multiplication):
O(1)
- Comparison operations:
O(1)
Since all operations inside the loop take constant time and the loop runs n
times, the overall time complexity is O(n)
.
Space Complexity: O(1)
The algorithm uses only a fixed amount of extra space regardless of the input size:
- Variables
n
,pre
,cur
:O(1)
- Loop variable
i
:O(1)
- Temporary variables
x1
,y1
,x2
,y2
:O(1)
No additional data structures like arrays, lists, or recursive call stacks are used. The space usage remains constant irrespective of the number of points in the input polygon, resulting in O(1)
space complexity.
Common Pitfalls
1. Incorrectly Handling Collinear Points
One of the most common mistakes is mishandling collinear points (when three consecutive vertices lie on the same line, resulting in a cross product of zero). Developers often make these errors:
Pitfall Example:
# INCORRECT: Treating zero cross product as a sign change
def isConvex(self, points):
n = len(points)
pre = 0
for i in range(n):
# ... calculate cur ...
# Wrong: This fails when cur is 0
if pre != 0 and cur != 0 and pre * cur < 0:
return False
pre = cur # Problem: Setting pre to 0 when points are collinear!
return True
Why it's wrong: When cur = 0
(collinear points), setting pre = cur
makes pre = 0
. This loses track of the previous turn direction, potentially missing concavity detection later.
Correct Solution:
# Only update pre when we have a non-zero cross product if cur != 0: if cur * pre < 0: return False pre = cur # Only update with non-zero values
2. Not Wrapping Around the Polygon Correctly
Pitfall Example:
# INCORRECT: Forgetting to wrap around at the end
def isConvex(self, points):
n = len(points)
pre = 0
for i in range(n - 2): # Wrong: Stops too early!
x1 = points[i + 1][0] - points[i][0]
y1 = points[i + 1][1] - points[i][1]
x2 = points[i + 2][0] - points[i][0]
y2 = points[i + 2][1] - points[i][1]
# ...
Why it's wrong: This misses checking the last two vertices where we need to wrap around to the beginning (e.g., checking vertices n-2, n-1, 0 and n-1, 0, 1).
Correct Solution:
for i in range(n): # Check all n vertices
point_b = points[(i + 1) % n] # Use modulo to wrap
point_c = points[(i + 2) % n] # Use modulo to wrap
3. Incorrect Cross Product Calculation
Pitfall Example:
# INCORRECT: Wrong vector calculation
def isConvex(self, points):
for i in range(n):
# Wrong: Using vectors between consecutive points instead of from same origin
x1 = points[(i + 1) % n][0] - points[i][0]
y1 = points[(i + 1) % n][1] - points[i][1]
x2 = points[(i + 2) % n][0] - points[(i + 1) % n][0] # Wrong origin!
y2 = points[(i + 2) % n][1] - points[(i + 1) % n][1] # Wrong origin!
cur = x1 * y2 - x2 * y1
Why it's wrong: The cross product needs both vectors to originate from the same point to correctly determine turn direction. Using different origins gives incorrect results.
Correct Solution:
# Both vectors should originate from points[i] x1 = points[(i + 1) % n][0] - points[i][0] y1 = points[(i + 1) % n][1] - points[i][1] x2 = points[(i + 2) % n][0] - points[i][0] # Same origin as vector 1 y2 = points[(i + 2) % n][1] - points[i][1] # Same origin as vector 1
4. Edge Case: Polygons with All Collinear Points
Pitfall: Not considering degenerate cases where all points might be collinear (forming a line rather than a polygon).
Solution: The current implementation handles this correctly by returning True
if pre
never gets updated (remains 0), which is technically correct as a degenerate polygon with all collinear points can be considered convex.
How does quick sort divide the problem into subproblems?
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!