Skip to content

Commit 25301d4

Browse files
committed
refactor: edit 1D ORS following comments
1 parent 4c0cf98 commit 25301d4

File tree

2 files changed

+45
-37
lines changed

2 files changed

+45
-37
lines changed

src/main/java/algorithms/orthogonalRangeSearching/oneDim/OrthogonalRangeSearching.java

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,17 +22,11 @@ public static RangeTreeNode<Integer> buildTree(int[] inputs, int start, int end)
2222

2323
if (start > end) {
2424
return null;
25-
} else if (end - start + 1 > 3) {
25+
} else if (start == end) {
26+
return new RangeTreeNode<>(inputs[start]);
27+
} else {
2628
return new RangeTreeNode<>(inputs[mid], buildTree(inputs, start, mid),
2729
buildTree(inputs, mid + 1, end));
28-
} else if (end - start + 1 == 3) {
29-
return new RangeTreeNode<>(inputs[mid], buildTree(inputs, start, mid),
30-
buildTree(inputs, end, end));
31-
} else if (end - start + 1 == 2) {
32-
return new RangeTreeNode<>(inputs[mid], buildTree(inputs, start, start),
33-
buildTree(inputs, end, end));
34-
} else {
35-
return new RangeTreeNode<>(inputs[mid]);
3630
}
3731
}
3832

@@ -52,7 +46,7 @@ public static RangeTreeNode<Integer> findSplit(RangeTreeNode<Integer> root, int
5246
return null;
5347
} else {
5448
if (high <= v.getVal()) {
55-
if (v.getLeft() == null && v.getRight() == null) { // reached the leaf
49+
if (isLeaf(v)) {
5650
break;
5751
}
5852
v = v.getLeft();
@@ -77,7 +71,7 @@ public static void allLeafTraversal(RangeTreeNode<Integer> v, List<Integer> resu
7771
if (v.getLeft() != null) {
7872
allLeafTraversal(v.getLeft(), result);
7973
}
80-
if (v.getLeft() == null && v.getRight() == null) { // leaf
74+
if (isLeaf(v)) {
8175
result.add(v.getVal());
8276
}
8377
if (v.getRight() != null) {
@@ -95,7 +89,7 @@ public static void allLeafTraversal(RangeTreeNode<Integer> v, List<Integer> resu
9589
*/
9690
public static void leftTraversal(RangeTreeNode<Integer> v, int low, List<Integer> result) {
9791
if (v != null) {
98-
if (v.getLeft() == null && v.getRight() == null) { // leaf
92+
if (isLeaf(v)) {
9993
result.add(v.getVal());
10094
} else {
10195
if (low <= v.getVal()) {
@@ -117,7 +111,7 @@ public static void leftTraversal(RangeTreeNode<Integer> v, int low, List<Integer
117111
*/
118112
public static void rightTraversal(RangeTreeNode<Integer> v, int high, List<Integer> result) {
119113
if (v != null) {
120-
if (v.getLeft() == null && v.getRight() == null && v.getVal() <= high) { // leaf, need extra check
114+
if (isLeaf(v) && v.getVal() <= high) { // leaf, need extra check
121115
result.add(v.getVal());
122116
} else {
123117
if (high > v.getVal()) {
@@ -142,8 +136,8 @@ public static Object[] search(RangeTreeNode<Integer> tree, int low, int high) {
142136
RangeTreeNode<Integer> splitNode = OrthogonalRangeSearching.findSplit(tree, low, high);
143137
ArrayList<Integer> result = new ArrayList<>();
144138
if (splitNode != null) {
145-
if (splitNode.getLeft() == null && splitNode.getRight() == null
146-
&& splitNode.getVal() >= low && splitNode.getVal() <= high) { // if split node is leaf
139+
if (isLeaf(splitNode) && splitNode.getVal() >= low
140+
&& splitNode.getVal() <= high) {
147141
result.add(splitNode.getVal());
148142
}
149143
leftTraversal(splitNode.getLeft(), low, result);
@@ -152,7 +146,11 @@ public static Object[] search(RangeTreeNode<Integer> tree, int low, int high) {
152146
return result.toArray();
153147
}
154148

155-
// Functions from here onwards are designed to support dynamic updates.
149+
private static boolean isLeaf(RangeTreeNode<Integer> node) {
150+
return node.getLeft() == null && node.getRight() == null;
151+
}
152+
153+
// FUNCTIONS FROM HERE ONWARDS ARE DESIGNED TO SUPPORT DYNAMIC UPDATES.
156154

157155
/**
158156
* Configures the height and parent nodes for the nodes in the Range Tree.
@@ -276,7 +274,7 @@ private static RangeTreeNode<Integer> rotateRight(RangeTreeNode<Integer> n) {
276274
* @return new root after rotation
277275
*/
278276
private static RangeTreeNode<Integer> rotateLeft(RangeTreeNode<Integer> n) {
279-
RangeTreeNode<Integer> newRoot = n.getRight();
277+
RangeTreeNode<Integer> newRoot = n.getRight(); // newRoot is the right subtree of the original root
280278
RangeTreeNode<Integer> newRightSub = newRoot.getLeft();
281279
newRoot.setLeft(n);
282280
n.setRight(newRightSub);
@@ -299,12 +297,14 @@ private static RangeTreeNode<Integer> rebalance(RangeTreeNode<Integer> n) {
299297
updateHeight(n);
300298
int balance = getBalance(n);
301299
if (balance < -1) { // right-heavy case
302-
if (height(n.getRight().getLeft()) > height(n.getRight().getRight())) {
300+
RangeTreeNode<Integer> rightChild = n.getRight();
301+
if (height(rightChild.getLeft()) > height(rightChild.getRight())) {
303302
n.setRight(rotateRight(n.getRight()));
304303
}
305304
n = rotateLeft(n);
306305
} else if (balance > 1) { // left-heavy case
307-
if (height(n.getLeft().getRight()) > height(n.getLeft().getLeft())) {
306+
RangeTreeNode<Integer> leftChild = n.getLeft();
307+
if (height(leftChild.getRight()) > height(leftChild.getLeft())) {
308308
n.setLeft(rotateLeft(n.getLeft()));
309309
}
310310
n = rotateRight(n);
@@ -320,26 +320,29 @@ private static RangeTreeNode<Integer> rebalance(RangeTreeNode<Integer> n) {
320320
* @return The root node of the updated Range Tree.
321321
*/
322322
public static RangeTreeNode<Integer> delete(RangeTreeNode<Integer> node, int val) {
323-
if (node.getLeft().getLeft() == null && node.getLeft().getRight() == null
324-
&& val == node.getLeft().getVal()) { // left node is the leaf node
325-
node.setVal(node.getRight().getVal());
323+
RangeTreeNode<Integer> leftChild = node.getLeft();
324+
RangeTreeNode<Integer> rightChild = node.getRight();
325+
326+
if (leftChild.getLeft() == null && leftChild.getRight() == null
327+
&& val == leftChild.getVal()) { // left node is the leaf node
328+
node.setVal(rightChild.getVal());
326329
node.setLeft(null);
327330
node.setRight(null);
328-
} else if (node.getRight().getLeft() == null && node.getRight().getRight() == null
329-
&& val == node.getRight().getVal()) { // right node is the leaf node
331+
} else if (rightChild.getLeft() == null && rightChild.getRight() == null
332+
&& val == rightChild.getVal()) { // right node is the leaf node
330333
node.setLeft(null);
331334
node.setRight(null);
332335
} else {
333336
if (val <= node.getVal()) {
334-
if (node.getLeft() != null) {
335-
node.setLeft(delete(node.getLeft(), val));
337+
if (leftChild != null) {
338+
node.setLeft(delete(leftChild, val));
336339
}
337340
if (val == node.getVal()) { // duplicate node
338-
node.setVal(getMostRight(node.getLeft()).getVal()); // update the duplicate key
341+
node.setVal(getMostRight(leftChild).getVal()); // update the duplicate key
339342
}
340343
} else {
341-
if (node.getRight() != null) {
342-
node.setRight(delete(node.getRight(), val));
344+
if (rightChild != null) {
345+
node.setRight(delete(rightChild, val));
343346
}
344347
}
345348
}

src/main/java/algorithms/orthogonalRangeSearching/oneDim/README.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
# 1D Orthogonal Range Searching
22

33
1D orthogonal range searching is a computational problem where you search for elements or data points within a
4-
specified 1D range (interval) in a collection of 1D data (e.g. Find me everyone between ages 22 and 27).
4+
specified 1D range (interval) in a collection of 1D data (e.g. Find me everyone between ages 22 and 27). Additionally,
5+
we also want to support efficient insertions of new data points into the maintained set.
56

6-
While we can simply sort (and binary search the low and high of the specified range) to return all data points within
7-
the specified range, each query will minimally take O(nlogn) due to the sorting step, assuming dynamic updating of
8-
the data.
7+
One strategy is would be to sort all the data points in O(nlogn) time, then insertion would take O(n). We can binary
8+
search the low and high of the specified range to return all data points within in O(logn) time. This would be a
9+
reasonable approach if the no. of queries >> no. of insertions.
910

10-
The goal of 1D Orthogonal Range Searching is to efficiently identify and retrieve all data points that fall within the
11-
given range.
11+
In cases where the no. of insertions >> no. of queries, we might want to further optimise the time complexity of
12+
insertions to O(logn) using a 1D range tree.
1213

1314
Strategy:
1415
1. Use a binary search tree
@@ -20,15 +21,19 @@ Strategy:
2021
Say we want to find all the nodes between 10 and 50 i.e. query(10, 50). We would want to:
2122
1. Find split node: highest node where search includes both left & right subtrees
2223
=> we want to make use of the range tree property to perform binary search to find our split node. See findSplit(root, low, high) in code.
23-
2. Left traversal & right traversal
24+
2. Left traversal & right traversal
25+
- Left traversal covers the range within [low, splitNode]
26+
- Right traversal covers the range within (splitNode, high]
2427

2528
![1DORSQuery](../../../../../../docs/assets/images/1DORSQuery.jpeg)
2629
Image Source: Lecture Slides
2730

2831
## Complexity Analysis
2932
**Time**:
3033

31-
Build Tree (cost incurred once only): O(nlogn) limited by sorting step
34+
Build Tree (cost incurred once only):
35+
- if given an unsorted array, O(nlogn) limited by sorting step
36+
- if given a sorted array, O(n)
3237

3338
Querying: O(k + logn)
3439
- Find Split Node: O(logn) (binary search)

0 commit comments

Comments
 (0)