forked from TheAlgorithms/Java
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMosAlgorithm.java
More file actions
260 lines (217 loc) · 8.42 KB
/
MosAlgorithm.java
File metadata and controls
260 lines (217 loc) · 8.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
package com.thealgorithms.others;
import java.util.Arrays;
import java.util.Comparator;
/**
* Mo's Algorithm (Square Root Decomposition) for offline range queries
*
* Mo's Algorithm is used to answer range queries efficiently when:
* 1. Queries can be processed offline (all queries known beforehand)
* 2. We can efficiently add/remove elements from current range
* 3. The problem has optimal substructure for range operations
*
* Time Complexity: O((N + Q) * sqrt(N)) where N = array size, Q = number of queries
* Space Complexity: O(N + Q)
*
* @see <a href="https://www.geeksforgeeks.org/dsa/mos-algorithm-query-square-root-decomposition-set-1-introduction/">Mo's Algorithm</a>
* @author BEASTSHRIRAM
*/
public final class MosAlgorithm {
/**
* Query structure to store range queries
*/
public static class Query {
public final int left;
public final int right;
public final int index; // Original index of query
public int result; // Result of the query
public Query(int left, int right, int index) {
this.left = left;
this.right = right;
this.index = index;
this.result = 0;
}
}
private MosAlgorithm() {
// Utility class
}
/**
* Solves range sum queries using Mo's Algorithm
*
* @param arr the input array
* @param queries array of queries to process
* @return array of results corresponding to each query
*/
public static int[] solveRangeSumQueries(int[] arr, Query[] queries) {
if (arr == null || queries == null || arr.length == 0) {
return new int[0];
}
int n = arr.length;
int blockSize = (int) Math.sqrt(n);
// Sort queries using Mo's ordering
Arrays.sort(queries, new MoComparator(blockSize));
// Initialize variables for current range
int currentLeft = 0;
int currentRight = -1;
int currentSum = 0;
// Process each query
for (Query query : queries) {
// Expand or shrink the current range to match query range
// Expand right boundary
while (currentRight < query.right) {
currentRight++;
currentSum += arr[currentRight];
}
// Shrink right boundary
while (currentRight > query.right) {
currentSum -= arr[currentRight];
currentRight--;
}
// Expand left boundary
while (currentLeft < query.left) {
currentSum -= arr[currentLeft];
currentLeft++;
}
// Shrink left boundary
while (currentLeft > query.left) {
currentLeft--;
currentSum += arr[currentLeft];
}
// Store the result
query.result = currentSum;
}
// Extract results in original query order
int[] results = new int[queries.length];
for (Query query : queries) {
results[query.index] = query.result;
}
return results;
}
/**
* Solves range frequency queries using Mo's Algorithm
* Example: Count occurrences of a specific value in range [L, R]
*
* @param arr the input array
* @param queries array of queries to process
* @param targetValue the value to count in each range
* @return array of results corresponding to each query
*/
public static int[] solveRangeFrequencyQueries(int[] arr, Query[] queries, int targetValue) {
if (arr == null || queries == null || arr.length == 0) {
return new int[0];
}
int n = arr.length;
int blockSize = (int) Math.sqrt(n);
// Sort queries using Mo's ordering
Arrays.sort(queries, new MoComparator(blockSize));
// Initialize variables for current range
int currentLeft = 0;
int currentRight = -1;
int currentCount = 0;
// Process each query
for (Query query : queries) {
// Expand right boundary
while (currentRight < query.right) {
currentRight++;
if (arr[currentRight] == targetValue) {
currentCount++;
}
}
// Shrink right boundary
while (currentRight > query.right) {
if (arr[currentRight] == targetValue) {
currentCount--;
}
currentRight--;
}
// Expand left boundary
while (currentLeft < query.left) {
if (arr[currentLeft] == targetValue) {
currentCount--;
}
currentLeft++;
}
// Shrink left boundary
while (currentLeft > query.left) {
currentLeft--;
if (arr[currentLeft] == targetValue) {
currentCount++;
}
}
// Store the result
query.result = currentCount;
}
// Extract results in original query order
int[] results = new int[queries.length];
for (Query query : queries) {
results[query.index] = query.result;
}
return results;
}
/**
* Comparator for Mo's Algorithm query ordering
* Queries are sorted by block of left endpoint, then by right endpoint
*/
private static class MoComparator implements Comparator<Query> {
private final int blockSize;
MoComparator(int blockSize) {
this.blockSize = blockSize;
}
@Override
public int compare(Query a, Query b) {
int blockA = a.left / blockSize;
int blockB = b.left / blockSize;
if (blockA != blockB) {
return Integer.compare(blockA, blockB);
}
// For odd blocks, sort right in ascending order
// For even blocks, sort right in descending order (optimization)
if ((blockA & 1) == 1) {
return Integer.compare(a.right, b.right);
} else {
return Integer.compare(b.right, a.right);
}
}
}
/**
* Demo method showing usage of Mo's Algorithm
*
* @param args command line arguments
*/
public static void main(String[] args) {
// Example: Range sum queries
int[] arr = {1, 3, 5, 2, 7, 6, 3, 1, 4, 8};
Query[] queries = {
new Query(0, 2, 0), // Sum of elements from index 0 to 2: 1+3+5 = 9
new Query(1, 4, 1), // Sum of elements from index 1 to 4: 3+5+2+7 = 17
new Query(2, 6, 2), // Sum of elements from index 2 to 6: 5+2+7+6+3 = 23
new Query(3, 8, 3) // Sum of elements from index 3 to 8: 2+7+6+3+1+4 = 23
};
System.out.println("Array: " + Arrays.toString(arr));
System.out.println("Range Sum Queries:");
// Store original queries for display
Query[] originalQueries = new Query[queries.length];
for (int i = 0; i < queries.length; i++) {
originalQueries[i] = new Query(queries[i].left, queries[i].right, queries[i].index);
}
int[] results = solveRangeSumQueries(arr, queries);
for (int i = 0; i < originalQueries.length; i++) {
System.out.printf("Query %d: Sum of range [%d, %d] = %d%n", i, originalQueries[i].left, originalQueries[i].right, results[i]);
}
// Example: Range frequency queries
System.out.println("\nRange Frequency Queries (count of value 3):");
Query[] freqQueries = {
new Query(0, 5, 0), // Count of 3 in range [0, 5]: 1 occurrence
new Query(2, 8, 1), // Count of 3 in range [2, 8]: 2 occurrences
new Query(6, 9, 2) // Count of 3 in range [6, 9]: 1 occurrence
};
// Store original frequency queries for display
Query[] originalFreqQueries = new Query[freqQueries.length];
for (int i = 0; i < freqQueries.length; i++) {
originalFreqQueries[i] = new Query(freqQueries[i].left, freqQueries[i].right, freqQueries[i].index);
}
int[] freqResults = solveRangeFrequencyQueries(arr, freqQueries, 3);
for (int i = 0; i < originalFreqQueries.length; i++) {
System.out.printf("Query %d: Count of 3 in range [%d, %d] = %d%n", i, originalFreqQueries[i].left, originalFreqQueries[i].right, freqResults[i]);
}
}
}