Homework Assignment 10
Suraj Atluri
Chapter 21
Exercise 21.5.7
Draw a quadtree for the following set of points, assuming
a 16×16 bounding box:
{(1,2),(4,10),(14,3),(6,6),(3,15),(2,2),(3,12),(9,4),(12,14)}.
Solution 21.5.7
Building a quadtree is to recursively divide 2D space into four
quadrants to a point at which each quadrant contains an
appropriate number of points, normally one or zero, as required
by an application.
Step to draw quadtree for specified set of points within a 16x16
bounding box
1. Root Node: It covers the entire space between (0,0) and
(16,16).
2. Divide the Root into
• Top-Left (NW): (0,0) to (8,8)
• Top-Right (NE): (8,0) to (16,8)
• Bottom-Left (SW): (0,8) to (8,16)
• Bottom-Right (SE): (8,8) to (16,16)
3. Merrill's Quadrant Points
NW: (1,2), (2,2), (6,6)}
NE: (14,3), (9,4)}
SW: (4,10), (3,12), (3,15)}
SE: {(12,14)}
4. Divide, as necessary
• When a quadrant contains more than one point and needs to be
subdivided:
NW Quadrant Subdivision
• Top-Left of NW: {(1,2), (
Top-Right of NW: Includes {(6,
NE Quadrant Subdivision
• No further subdivision is needed because it will be all leaf
nodes.
SW Quadrant Subdivision
Bottom-Left of SW: Has {(4,10)}
• Bottom-Right of SW: Contains {(3,12), (3,15)}
SE Quadrant
• No division is needed as it is just an individual point.
Quadtree Drawing:
• The whole 16x16 bounding box is the root.
• All nodes are quadrants, and these are subdivided as needed.
Exercise 21.5.13
Describe an efficient data structure for storing a set S of n items
with ordered keys, so as to support a rankRange(a,b) method,
which enumerates all the items with keys whose rank in S is in
the range [a,b], where a and b are integers in the
interval [0,n−1]. Describe methods for object insertions and
deletion , and characterize the running times for these and
the rankRange method.
Solution 21.5.13
We can implement Augmented Balanced Binary Search Tree as an
Order Statistic Tree which is an appropriate data structure to
produce a solution for the rank-based range query problem.
Particularly, an Augmented Red-Black Tree or an augmented AVL
Tree can be utilized to enable efficient performance of all the
required operations.
Data structures for both methods: Augmented Red-Black Tree
Each node is supplemented with an additional field to store the
size of the subtree rooted at the node. With this supplementation,
efficient node-location and node-enumeration based on rank can
be performed.
Augmentation Details
• Each node v of the tree stores an additional field:
o\tsize(v): The size of the subtree rooted at v, which includes v.
Adding this field allows us to fetch a node's rank and perform
node lookups by rank.
Operations
1. Insertion
Algorithm
1. Perform a regular insertion operation of a Red-Black Tree or an
AVL Tree, which takes O(logn) time.
2. Target all ancestor nodes along the insertion path to the root
node and alter their size attribute.
3. As there are O(logn) nodes along this path, and it takes an O(1)
update to the size field for each node, overall update time
complexity is O(logn).
•Complexidade do Tempo
2. Deletion
Algorithm
1. Perform the usual delete operation of an AVL tree or a Red-
Black tree, which takes O(logn) time.
2. When you delete a node, update all ancestor node size
attributes along the path to the root node.
3. Update operation's time complexity is O(logn), which is the
same as for insertion.
• Time Complexity: O(log
3. rankRange(a, b)
•The rankRange(a, b) operation should return all elements with
rank between a and b, and both a and b are between 0 and n−1.
Algorithm
1.FIND NODE BY RANK
In order to find a node with a certain rank, for example, a or b,
begin at the root and utilize size attributes to decide to move left,
move right, or to return the node at the current position.
Particularly, at each node v:
Let size(v.left) be the size of left subtree.
• If rank == size(v.left) + 1, v is the node of the desired rank.
• If rank <= size(v.left), go to left child.
Else, proceed to the right child, and modify rank (rank -=
size(v.left) + 1).
Finding a node by rank O(logn) time.
2.Range Enumeration of Nodes:
• After detecting nodes for ranks a and b, perform an in-order
traversal to enumerate all intervening nodes between them.
There are k nodes in the range. These nodes can be enumerated
in O(k) time.
Time Complexity: O(log n + k), where k is the number of nodes
within the specified range.
Summarized Time Complexities
• Insertion: O (log
•Deletion: O (log n)
• rankRange(a, b): O(log n+k), where k is the number of nodes
between a and b
Example Algorithm for rankRange(a, b)
Input
A pseudocode algorithm to perform operation rankRange(a, b) is
presented below:
pseudocode
function rankRange(root, a, b):
result ← empty list
nodeA ← findNodeByRank(root, a)
nodeB ← findNodeByRank(root, b)
enumerateRange(nodeA, nodeB, result)
return result
function findNodeByRank(node, rank):
loop while node is not null:
leftSize ← 0
if node.left is not null:
leftSize ← node.left.size
if rank == leftSize + 1:
return node
else if rank <= leftSize:
node ← node.left
else:
rank ← rank - (leftSize + 1)
node ← node.right
return null
Conclusion
Order Statistic Tree, being an augmented balanced BST (like a
Red-Black Tree), is a good data structure for this problem.
Augmenting each node with the size of its subtree can enable us
to maintain:
Insertions and deletions within O(log n) time.
• The operation rankRange(a, b), which takes O(log n+k) time,
where k is the number of nodes within range.
It offers efficient performance to store ordered lists and process
rank-based queries.
Exercise 21.5.27
In computer graphics and computer gaming environments, a
common heuristic is to approximate a complex two-dimensional
object by a smallest enclosing rectangle whose sides are parallel
to the coordinate axes, which is known as a bounding box. Using
this technique allows system designers to generalize range
searching over points to more complex objects. So, suppose you
are given a collection of n complex two-dimensional objects,
together with a set of their bounding boxes,
𝒮={R1,R2,…,Rn}.
Suppose further that for some reason you have a data
structure, D, that can store n four-dimensional points so as to
answer four-dimensional range-search queries in O(log 3 n+ s ) time,
where s is the number of points in the query range. Explain how
rectangles in 𝒮, given a query rectangle, R, would return every
you can use D to answer two-dimensional range queries for the
bounding box, Ri, in 𝒮, such that Ri is completely contained
inside R. Your query should run in O(log^ 3 n+s) time, where s is
the number of bounding boxes that are output as being
completely inside the query range, R.
Solution 21.5.27
We must use a data structure D, which supports efficient four-
dimensional range searching, to be able to solve a two-
dimensional range query problem for bounding boxes.
Let's look at what's needed and design a solution to meet it.
Problem Breakdown
Bounding Box Representation:
We have a set S={R1,R2,…,Rn}, where each Ri is a bounding
rectangle around a 2D object.
A bounding box is specified with two pairs of coordinates: (Xmin,
Ymin), and (Xmax, Ymax). Thus, there is an equation for each
bounding box as a point with four dimensions:
(Xmin, Ymin, Xmax, Ymax)
The Given Data Structure D
D contains n points of four-dimensional space.
Four-dimensional range-search queries are processed in O(log^3
n + s) time, where s is the size of the query range.
Objective
For a query region R in two-dimensional space, find all bounding
boxes Ri∈S for which Ri is fully contained within R.
The time complexity will be O(log^3 n + s), with s being the
number of bounding boxes fully contained within the query
rectangle.
Solution Strategy
To remedy this, we must convert the two-dimensional range query
to an equivalent four-dimensional range query that can be
answered efficiently by data structure D.
Step 1: Represent Each Bounding Box as a Four-Dimensional Point
Each bounding box Ri is defined by its four boundary values:
Ri=(Xmin, Ymin, Xmax, Ymax)
Step 2: Define the Query Rectangle
Let query rectangle R be defined as follows:
Bottom left corner: (X1,
Upper-right corner: (X2,Y2)
Step 3: Create the Four-Dimensional Question
We want to obtain all bounding boxes Ri which are entirely
contained within R. Namely:
X1 ≤ Xmin≤ Xmax≤ X2
Y1 ≤ Ymin≤ Ymax≤ Y2
We can represent this containment problem as a range query with
four dimensions:
X1≤ Xmin ≤X2
Y1<= Ymin <= Y2
X1≤ Xmax ≤X2
Y1≤ Ymax ≤Y2
Step 4: Use data structure D
We use data structure D to perform a range search for points
(Xmin, Ymin, Xmax, Ymax) which obey the conditions mentioned
above.
Since D supports range-search querying for four dimensions in
O(log^3 n + s) time, we can straightaway use it to find all those
bounding boxes fully within a query rectangle.
Pseudo Code
# Converts a bounding box into a 4D point representation
function convertTo4DPoint(boundingBox):
x_min ← boundingBox.x_min
y_min ← boundingBox.y_min
x_max ← boundingBox.x_max
y_max ← boundingBox.y_max
return (x_min, y_min, x_max, y_max)
# Executes a 2D range search on the 4D data structure D
function rangeQuery(D, queryRectangle):
x1 ← queryRectangle.x_min # Bottom-left x
y1 ← queryRectangle.y_min # Bottom-left y
x2 ← queryRectangle.x_max # Top-right x
y2 ← queryRectangle.y_max # Top-right y
# Construct the corresponding 4D query ranges
x_min_range ← [x1, x2]
y_min_range ← [y1, y2]
x_max_range ← [x1, x2]
y_max_range ← [y1, y2]
# Query the structure D for all matching 4D points
result ← D.rangeQuery(x_min_range, y_min_range, x_max_range,
y_max_range)
return result
# Main driver routine
function main():
S ← [R1, R2, ..., Rn] # Collection of bounding boxes
D ← new DataStructure() # Initialize the 4D range structure
for each boundingBox in S:
point4D ← convertTo4DPoint(boundingBox)
D.insert(point4D)
queryRectangle ← getInputQueryRectangle()
resultBoundingBoxes ← rangeQuery(D, queryRectangle)
print("Bounding boxes inside the query rectangle:")
for each boundingBox in resultBoundingBoxes:
print(boundingBox)
# Stub to obtain a query rectangle
function getInputQueryRectangle():
# Either user input or a fixed rectangle
return Rectangle(x_min, y_min, x_max, y_max)
# Start the process
main()
Time Complexity
The D layout allows the range-search query to be performed in
O(log^3 n + s), time, where s denotes points that fall within
query range.
Therefore, this solution meets the requirement for time
complexity.
Chapter 22
Exercise 22.6.7
Give a pseudocode description of the plane‐sweep algorithm for
finding a closest pair of points among a set of n points in the
plane.
Solution 22.6.7
Plane-sweep algorithm is an efficient algorithm to find out the
closest pair of points out of a set of n points within a plane based
on a sweep-line and divide-and-conquer methodology.
A key requirement is to maintain an efficient dynamic data
structure to enable good searching performance to find the
point's nearest neighbor as sweeping is performed left to right
across the plane. The algorithm typically requires O(n log n) time.
Data structures employed:
1.List or Array
2. Balanced BST (or) Self-Balancing Binary Search Tree
Pseudo Code
function closestPair(points):
# Step 1: Sort input points based on x-coordinate
points ← sortByX(points)
# Step 2: Recursively compute the closest pair
return closestPairRecursive(points)
function closestPairRecursive(points):
# Base condition: If number of points is 3 or less, apply brute force
if length(points) ≤ 3:
return bruteForceClosestPair(points)
# Step 3: Split the points into two groups
mid ← floor(length(points) / 2)
leftPoints ← points[0 to mid - 1]
rightPoints ← points[mid to end]
# Step 4: Recursively calculate closest distances in both halves
dLeft ← closestPairRecursive(leftPoints)
dRight ← closestPairRecursive(rightPoints)
# Step 5: Take the smaller of the two distances
delta ← min(dLeft, dRight)
# Step 6: Determine the dividing x-coordinate
midX ← points[mid].x
# Gather points near the division within delta range
strip ← []
for each point in points:
if abs(point.x - midX) < delta:
strip.append(point)
# Step 7: Sort strip points by their y-values
strip ← sortByY(strip)
# Step 8: Evaluate closest pair within the strip
dStrip ← findClosestInStrip(strip, delta)
# Step 9: Return the smallest among all computed distances
return min(delta, dStrip)
function bruteForceClosestPair(points):
minDist ← ∞
for i from 0 to length(points) - 1:
for j from i + 1 to length(points) - 1:
dist ← distance(points[i], points[j])
if dist < minDist:
minDist ← dist
return minDist
function findClosestInStrip(strip, delta):
minDist ← delta
for i from 0 to length(strip) - 1:
# Only check next 7 neighbors in y-order
for j from i + 1 to min(i + 7, length(strip) - 1):
if abs(strip[j].y - strip[i].y) < minDist:
dist ← distance(strip[i], strip[j])
if dist < minDist:
minDist ← dist
return minDist
function distance(point1, point2):
return sqrt((point1.x - point2.x)^2 + (point1.y - point2.y)^2)
Explanation of the Pseudocode
1. closestPair(points):
order of points by their x-coordinates
o Call recursive function closestPairRecursive() to obtain
closest pair.
2. closestPairRecursive(points):
Base Case: For 2 or 3 points, brute force can be used to
calculate the closest distance.
o Divide Step: Divide the points into two halves: leftPoints
and rightPoints
Recursive Search: Recursively find closest pair for both
halves (dLeft and dRight).
Merging Step: Calculate minimum distance (delta) between
both halves.
strip Calculation
Find all points that are within delta of the division line
Sort the points by y-coordinate.
find closest to strip
Traverse through strip points. There are only 7 points to be
searched for at each point next due to point distribution
geometry within the plane.
return minimum distance found (dStrip or delta)
3. bruteForceClosestPair(points):
Employs a double-loop to calculate minimum distance
between all pairs of points. It is used where points are
limited to O(n^2)
4.findClosestInStrip(strip, delta):
Verifies closest distance between points within the strip.
At most 7 points have to be taken into account for each point
in the strip for closest pair discovery.
Time Complexity
Initial Sorting
Sorting points based on x-coordinates is O(n logn).
Recursive Calls:
The closestPairRecursive() function divides the problem into
two halves at each step, similar to merge sort.
Each division needs O(log n) levels of recursion.
o.Merge Step:
O(n) time is needed for strip creation and closest pair finding
in the strip during the merge step.
Sorting the strip based on y-coordinates and finding the
closest pair in the strip will be O(n).
Total Time Complexity:
Overall time complexity is O(n log n), both because of
sorting and because of recursive computation.
The recurrence relation of the algorithm is:
T(n)=2T(n/2)+O(n)
Exercise 22.6.16
Let C be a collection of n horizontal and vertical line segments.
Describe an O(n log n)‐time algorithm for determining whether
the segments in C form a simple polygon.
Solution 22.6.16
We can approach this problem with a sweep line algorithm with an
event-driven approach and efficient data structures for quick
intersection checking. We want to make sure that there's no
intersection or endpoint between two segments at most.
Here is a solution to this problem with O(n log n) time complexity.
Data structures employed:
1. Balanced Binary Search Tree (BST) or Self-Balancing Tree:
Used for maintaining sweep line status for efficient intersection
discovery between active segments.
A well-balanced tree like an AVL Tree or a Red-Black Tree offers
efficient insertion, deletion, and finding neighbors, all within O(log
n) time.
2.Priority Queue (Min
It stores points as endpoints of segments.
allows us to process events stepwise, proceeding from the far left.
Algorithm Description:
1.Set Event Points:
Here is a list of endpoints of all segments of C:
Sort these endpoints by their x-coordinates, and if there is a tie,
by y-coordinate.
The time complexity for sorting is O(n log n)
2. Process Events Using Sweep Line:
Use a sweep line that sweeps from left to right and processes
events in order of sorting.
For each event point:
Beginning point of a segment:
• Put segment into sweep line status.
Search for potential intersections with neighboring segments of
the sweep line.
Ending point of a segment:
Remove the section of the sweep line status.
Verify if the neighbors of the deleted segment intersect with one
another.
3. Verify simple polygon properties:
Intersection Check: If at any point two segments intersect (apart
from common endpoints), then segments do not constitute a
simple polygon.
Degree Check: Monitor the degree of every endpoint. If at any
point there exists a point with degree > 2, segments do not make
a simple polygon.
Connectivity Check: Ensure all segments are connected and form
a closed loop.
Pseudo Code
function isSimplePolygon(segments):
# Step 1: Generate event points for each segment
events ← []
for each segment in segments:
if segment is either horizontal or vertical:
events.append((segment.start, "start", segment))
events.append((segment.end, "end", segment))
# Step 2: Sort the events by x, breaking ties with y
events ← sort(events, key = lambda e: (e[0].x, e[0].y))
# Step 3: Initialize sweep line structure and degree tracker
sweepLineStatus ← BalancedBST()
endpointDegrees ← {} # Keeps track of how many times each point is an
endpoint
# Step 4: Iterate over each event and apply sweep line logic
for each event in events:
point, eventType, segment ← event
if eventType == "start":
# Add current segment to sweep line
sweepLineStatus.insert(segment)
# Check for intersections with adjacent segments in the sweep line
leftNeighbor ← sweepLineStatus.leftNeighbor(segment)
rightNeighbor ← sweepLineStatus.rightNeighbor(segment)
if leftNeighbor and doIntersect(leftNeighbor, segment):
return False
if rightNeighbor and doIntersect(rightNeighbor, segment):
return False
# Update degree counts for segment endpoints
endpointDegrees[point] ← endpointDegrees.get(point, 0) + 1
endpointDegrees[segment.end] ←
endpointDegrees.get(segment.end, 0) + 1
else if eventType == "end":
# Before removing, check if neighbors now intersect
leftNeighbor ← sweepLineStatus.leftNeighbor(segment)
rightNeighbor ← sweepLineStatus.rightNeighbor(segment)
if leftNeighbor and rightNeighbor and doIntersect(leftNeighbor,
rightNeighbor):
return False
# Remove current segment from sweep line
sweepLineStatus.remove(segment)
# Step 5: Ensure all points are connected to exactly two segments
for each point, degree in endpointDegrees:
if degree ≠ 2:
return False
return True
function doIntersect(segment1, segment2):
# Determines whether segment1 and segment2 intersect
# Should support both axis-aligned horizontal and vertical segments
return (intersection conditions apply here)
Time Complexity Analysis
1. Event sorting: Event point sorting will be O(nlog n).
2. Processing Events: Insertions and deletions are both made and
removed from/to the balanced BST a constant number of times,
and an operation (insert, delete, find neighbors) takes O(log n)
time.
3. Neighbor Intersections: Neighbor intersections can be tested
within O(log n) time for an event.
So, the overall time complexity of the algorithm is O(n log n)
Exercise 22.6.30
In machine learning applications, we often have some kind of
condition defined over a set, S, of n points, which we would like to
characterize—that is, “learn”—using some simple rule. For
instance, these points could correspond to biological attributes
of n medical patients and the condition could be whether a given
patient tests positive for a given disease or not, and we would like
to learn the correlation between these attributes and this disease.
Think of the points with positive tests as painted “red,” and the
tests with negative tests as painted “blue.” Suppose that we have
a simple two‐factor set of attributes; hence, we can view each
patient as a two‐dimensional point in the plane, which is colored
as either red or blue. An ideal characterization, from a machine
learning perspective, is if we can separate the points of S by a
line, L, such that all the points on one side of L are red and all the
points on the other side of L are blue. So suppose you are given a
set, S, of n red and blue points in the plane. Describe an efficient
algorithm for determining whether there is a line, L, that
separates the red and blue points in S. What is the running time
of your algorithm?
Solution 22.6.30
we can approach this problem as a linear separability problem
in computational geometry,
Data Structures Used
Input Points: A list of points, 𝑆
Convex Hull Representation: A list of vertices representing the
boundary of the convex hull in counterclockwise order for
Edges: A list of line segments connecting consecutive vertices in
the convex hull.
Intersection Test: A geometric line segment intersection algorithm
using vector cross products.
Separate Points by Color:
Divide the set SSS into two subsets:
o R: Points labeled "red."
o B: Points labeled "blue."
Compute Convex Hulls:
Use a convex hull algorithm (e.g., Graham's scan or
Andrew's monotone chain) to compute the convex hulls for R
and B.
Convex hull algorithms require sorting the points and
identifying the boundary vertices.
Check for Intersection:
For each edge of the convex hull of R, check if it intersects
with any edge of the convex hull of B using a line-segment
intersection algorithm.
Decision:
If any intersection is found, the points are not linearly
separable.
If no intersection is found, the points are linearly separable,
and a separating line exists.
Algorithm Psuedo code
function isLinearlySeparable(points):
# Divide the input points into two color groups
R, B ← separatePoints(points)
# Compute the convex hulls for each group
convexHullR ← computeConvexHull(R)
convexHullB ← computeConvexHull(B)
# Check for intersection between the two hulls
if hullsIntersect(convexHullR, convexHullB):
return False # Not separable if hulls intersect
return True # Separable if hulls are disjoint
function separatePoints(points):
R ← [] # Red points
B ← [] # Blue points
for each point in points:
if point.color == "red":
R.append(point)
else:
B.append(point)
return R, B
function computeConvexHull(points):
# Sort the list of points first by x, then by y for tie-breaking
sort points by x-coordinate (with y-coordinate as secondary key)
lowerHull ← []
for each point in points:
while size of lowerHull ≥ 2 and last two points with current point form a
non-left turn:
remove the last point from lowerHull
lowerHull.append(point)
upperHull ← []
for each point in reverse(points):
while size of upperHull ≥ 2 and last two points with current point form a
non-left turn:
remove the last point from upperHull
upperHull.append(point)
# Avoid repeating the point where upper and lower hulls meet
remove the last point from upperHull
return lowerHull + upperHull # Combine both parts into full convex hull
function hullsIntersect(hull1, hull2):
for each edge1 in hull1:
for each edge2 in hull2:
if edgesIntersect(edge1, edge2):
return True
return False
function edgesIntersect(edge1, edge2):
u, v ← edge1
p, q ← edge2
orient1 ← orientation(u, v, p)
orient2 ← orientation(u, v, q)
orient3 ← orientation(p, q, u)
orient4 ← orientation(p, q, v)
if orient1 ≠ orient2 and orient3 ≠ orient4:
return True # Proper intersection occurs
return False # No intersection
function orientation(p, q, r):
value ← (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y)
if value == 0:
return 0 # Points are collinear
else if value > 0:
return 1 # Clockwise orientation
return 2 # Counter-clockwise orientation
Time Complexity
Separating Points: O(n)
Convex hull construction: O(n log n) for all hulls.
Intersection Testing: O(h1·h2) with h1 and h2 being the
number of vertexes of both convex hulls.
Total Time Complexity: O(n log n)