The quick hull algorithm uses a divide and conquer strategy to compute the convex hull of a shape. A shape's convex hull (also referred to as the convex closure) is the smallest set of points encapsulating it. The following image illustrates the convex hull.
You can see that the convex hull on the right is the smallest number of points that encapsulates the shape, which is five points. Let's see how it works below.
Given an array of points, we will follow these steps to find the convex hull. Note that our convex hull will be a set, so there are no duplicates.
Let's illustrate the steps with the diagram above.
Find the point with the minimum x-coordinate, min-x, and another point with the maximum x-coordinate, max-x.
The illustration shows two red points. Those are our min-x and max-x.
Join these two points with a line L to divide the shape into two parts.
We can see the line dividing the shape into two parts. Let's say these are the upper and lower parts.
For a part, we find the point P furthest from the line. Join the points min-x and max-x with P to form a triangle.
We solve the upper part as follows:
Let's take the upper part and find point P with the maximum distance from the line. Remember that this step further divides the problem into two sub-problems. Note that we can see the point (2,2) inside the triangle can never be part of the convex hull since it is encapsulated by min-x, max-x, and P.
Sub-problem 1: The line joining min-x and P.
Sub-problem 2: The line joining max-x and P.
We solve the lower part as follows:
Let's take the lower part and find point P2 with the maximum distance from the line. There are two sub-problems; the line joining min-x and P2 and the line joining P2 and max-x. For both, there are no further points so we add min-x, P2, and max-x to the hull.
Our final convex hull is min-x, max-x, P, P1, P.
Here is the implementation.
// Returns the side of point p with respect to the// line formed by p1 and p2.// This is the cross product of the p1.p2 and p1.p vectorsexport const getSide = (p1, p2, p) => {const result =(p[1] - p1[1]) * (p2[0] - p1[0]) - (p2[1] - p1[1]) * (p[0] - p1[0]);if (result > 0) return 1;return -1;};
Here is what the code does:
getSide.js
filegetDistanceFromLine.js
filequickHull.js
filequickHull()
.ind
value will still be -1
. This means we reach the stopping condition. We add the two points that form the line to the convex hull set.points[ind], p1
.points[ind], p2
.index.js
fileThe time complexity is similar to QuickSort due to the division into two sub-problems. Every call of quickHull()
takes O(n) due to the step where we find point P furthest from the line.
The best case is when the line divides the two balanced parts, resulting in the recurrence relation of T(n) = 2 T(n/2) + O(n)
.
This expands to become T(n) = O(n log(n))
.
The worst case is when the line divides into extremely unbalanced parts, resulting in a recurrence relation of T(n) = T(n-1) + O(n)
.
We expand this further into = T(n-2) + O(n) + O(n) = T(n-3) + O(3n) = ... = O(n^2)
.
The space complexity is O(n) because the resulting convex hull set is limited by the number of points given to the algorithm.