GeeksforGeeks App
Open App
Browser
Continue

# Explain an alternative Sorting approach for MO’s Algorithm

MO’s Algorithm is an algorithm designed to efficiently answer range queries in an array in linear time. It is a divide-and-conquer approach that involves pre-processing the array, partitioning it into blocks, and then solving the queries in each of the blocks.

### Alternate Approach for Sorting:

An alternate approach that has been proposed is known as the double-ended priority queue (DEQ) approach. This approach uses a double-ended priority queue (DEQ) to store the elements of the array. The elements are then sorted using the DEQ, which allows for faster retrieval of elements.

### Double-Ended Priority Queue (DEQ):

A double-ended priority queue (DEQ) is a data structure that stores items in an order of priority and allows access from both the front and back of the queue.

The DEQ allows for both the insertion and deletion of elements in O(log n) time, where n is the number of elements in the DEQ. This makes it an efficient data structure for maintaining the order of elements in a range as it allows for fast insertion and deletion of elements.

Consider an array that consists of N elements and there are Q queries where each query consists of two indices l and r denoting the range of the query.

Steps involved in the implementation of code:

• The block size is calculated as the square root of N which will be used to partition the queries.
• Then, the queries are sorted according to their left indices and whether the block size is even or odd.
• A sliding window is created that stores the range of indices from l to r
• For each query, the elements in the window are updated by adding or removing elements from the range l to r.
• The variable cnt[a[x]] is used to count the number of times an element x appears in the window.
• The variable ans[0] is used to keep track of the number of distinct elements in the window.
• The answer to each query is stored in the array ans[].

Below is the implementation of Double ended Priority Queue (DEQ):

## C++

 `// C++ code to implement the approaach` `#include ``using` `namespace` `std;` `// N is the maximum number of elements``// in the array``const` `int` `N = 1e5 + 5;``int` `n, q, a[N], ans[N], cnt[N], block_size;` `// Query structure for storing query details``struct` `query {``    ``int` `l, r, id;``} qry[N];` `// Function to compare two queries``bool` `cmp(query a, query b)``{``    ``// If the query is in different blocks,``    ``// sort according to lth index``    ``if` `(a.l / block_size != b.l / block_size)``        ``return` `a.l < b.l;` `    ``// If the queries are in the same block,``    ``// sort according to the parity of the``    ``// block number``    ``return` `(a.l / block_size & 1) ? a.r < b.r : a.r > b.r;``}` `// Function to add an element to the``// current range``void` `add(``int` `x)``{``    ``cnt[a[x]]++;` `    ``// Increase the answer if the element``    ``// is added for the first time in``    ``// this range``    ``if` `(cnt[a[x]] == 1)``        ``ans[0]++;``}` `// Function to remove an element from``// the current range``void` `remove``(``int` `x)``{``    ``cnt[a[x]]--;` `    ``// Decrease the answer if the element``    ``// is completely removed from``    ``// this range``    ``if` `(cnt[a[x]] == 0)``        ``ans[0]--;``}` `// Driver code``int` `main()``{``    ``n = 12;``    ``q = 3;``    ``block_size = ``sqrt``(n);` `    ``// Example array``    ``int` `array[] = { 1, 2, 2, 2, 3, 4, 5, 5, 5, 6, 6, 6 };` `    ``// Store the elements in the array``    ``for` `(``int` `i = 0; i < n; i++)``        ``a[i + 1] = array[i];` `    ``// Example range queries``    ``int` `l1 = 1, r1 = 4, l2 = 2, r2 = 6, l3 = 3, r3 = 8;` `    ``// Store the query details``    ``qry[0].l = l1;``    ``qry[0].r = r1;``    ``qry[0].id = 0;``    ``qry[1].l = l2;``    ``qry[1].r = r2;``    ``qry[1].id = 1;``    ``qry[2].l = l3;``    ``qry[2].r = r3;``    ``qry[2].id = 2;` `    ``// Sort the queries according to their``    ``// lth and rth indices``    ``sort(qry, qry + q, cmp);` `    ``// l and r denote the current range``    ``int` `l = 1, r = 0;` `    ``// Iterate through all the queries``    ``for` `(``int` `i = 0; i < q; i++) {` `        ``// Expand the range to the left``        ``while` `(l > qry[i].l)``            ``add(--l);` `        ``// Expand the range to the right``        ``while` `(r < qry[i].r)``            ``add(++r);` `        ``// Shrink the range from the left``        ``while` `(l < qry[i].l)``            ``remove``(l++);` `        ``// Shrink the range from the right``        ``while` `(r > qry[i].r)``            ``remove``(r--);` `        ``// Store the answer for the``        ``// current query``        ``ans[qry[i].id] = ans[0];``    ``}` `    ``// Print answers to the queries``    ``for` `(``int` `i = 0; i < q; i++)``        ``cout << ans[i] << endl;` `    ``return` `0;``}`

## Java

 `// Java code to implement the approaach``import` `java.util.*;` `public` `class` `Main {``    ``static` `class` `Query {``        ``int` `l, r, id;` `        ``// Query structure for storing query details``        ``Query(``int` `l, ``int` `r, ``int` `id) {``            ``this``.l = l;``            ``this``.r = r;``            ``this``.id = id;``        ``}``    ``}` `    ``static` `int` `n, q, block_size;``    ``static` `int``[] a, ans, cnt;` `    ``// Function to compare two queries``    ``static` `class` `QueryComparator ``implements` `Comparator {``        ``@Override``        ``public` `int` `compare(Query a, Query b) {``            ``// If the query is in different blocks,``            ``// sort according to lth index``            ``if` `(a.l / block_size != b.l / block_size) {``                ``return` `Integer.compare(a.l, b.l);``            ``}``            ``// If the queries are in the same block,``            ``// sort according to the parity of the``            ``// block number``            ``return` `((a.l / block_size) & ``1``) == ``0` `? Integer.compare(a.r, b.r) : Integer.compare(b.r, a.r);``        ``}``    ``}` `    ``// Function to add an element to the``    ``// current range``    ``static` `void` `add(``int` `x) {``        ``cnt[a[x]]++;``        ` `        ``// Increase the answer if the element``        ``// is added for the first time in``        ``// this range``        ``if` `(cnt[a[x]] == ``1``) {``            ``ans[``0``]++;``        ``}``    ``}` `    ``// Function to remove an element from``    ``// the current range``    ``static` `void` `remove(``int` `x) {``        ``cnt[a[x]]--;``        ` `        ``// Decrease the answer if the element``        ``// is completely removed from``        ``// this range``        ``if` `(cnt[a[x]] == ``0``) {``            ``ans[``0``]--;``        ``}``    ``}` `    ``// Driver code``    ``public` `static` `void` `main(String[] args) {``        ``n = ``12``;``        ``q = ``3``;``        ``block_size = (``int``) Math.sqrt(n);` `        ``// Example array``        ``int``[] array = {``1``, ``2``, ``2``, ``2``, ``3``, ``4``, ``5``, ``5``, ``5``, ``6``, ``6``, ``6``};` `        ``// Store the elements in the array``        ``a = ``new` `int``[n + ``1``];``        ``for` `(``int` `i = ``0``; i < n; i++) {``            ``a[i + ``1``] = array[i];``        ``}` `        ``// Example range queries``        ``int` `l1 = ``1``, r1 = ``4``, l2 = ``2``, r2 = ``6``, l3 = ``3``, r3 = ``8``;` `        ``// Store the query details``        ``Query[] qry = ``new` `Query[q];``        ``qry[``0``] = ``new` `Query(l1, r1, ``0``);``        ``qry[``1``] = ``new` `Query(l2, r2, ``1``);``        ``qry[``2``] = ``new` `Query(l3, r3, ``2``);` `        ``// Sort the queries according to their``        ``// lth and rth indices``        ``Arrays.sort(qry, ``new` `QueryComparator());` `        ``// l and r denote the current range``        ``int` `l = ``1``, r = ``0``;``        ``cnt = ``new` `int``[n];``        ``ans = ``new` `int``[q + ``1``];` `        ``// Iterate through all the queries``        ``for` `(``int` `i = ``0``; i < q; i++) {``            ``// Expand the range to the left``            ``while` `(l > qry[i].l)``                ``add(--l);` `            ``// Expand the range to the right``            ``while` `(r < qry[i].r)``                ``add(++r);` `            ``// Shrink the range from the left``            ``while` `(l < qry[i].l)``                ``remove(l++);` `            ``// Shrink the range from the right``            ``while` `(r > qry[i].r)``                ``remove(r--);` `            ``// Store the answer for the``            ``// current query``           ``ans[qry[i].id] = ans[``0``];``        ``}``        ` `        ``// Print answers to the queries``        ``for` `(``int` `i = ``0``; i < q; i++)``            ``System.out.println(ans[i]);``    ``}``}` `// This code is contributed by Pushpesh Raj.`

## Python3

 `# Python code to implement the approaach` `import` `math` `# N is the maximum number of elements``# in the array``N ``=` `int``(``1e5``) ``+` `5``n, q, block_size ``=` `0``, ``0``, ``0``a ``=` `[``0``] ``*` `N``ans ``=` `[``0``] ``*` `N``cnt ``=` `[``0``] ``*` `N` `# Query structure for storing query details``class` `Query:``    ``def` `__init__(``self``, l, r, ``id``):``        ``self``.l ``=` `l``        ``self``.r ``=` `r``        ``self``.``id` `=` `id` `# Function to compare two queries``def` `cmp``(a, b):``    ``# If the query is in different blocks,``    ``# sort according to lth index``    ``if` `a.l ``/``/` `block_size !``=` `b.l ``/``/` `block_size:``        ``return` `a.l < b.l` `    ``# If the queries are in the same block,``    ``# sort according to the parity of the``    ``# block number``    ``return` `a.r < b.r ``if` `a.l ``/``/` `block_size & ``1` `else` `a.r > b.r` `# Function to add an element to the``# current range``def` `add(x):``    ``cnt[a[x]] ``+``=` `1` `    ``# Increase the answer if the element``    ``# is added for the first time in``    ``# this range``    ``if` `cnt[a[x]] ``=``=` `1``:``        ``ans[``0``] ``+``=` `1` `# Function to remove an element from``# the current range``def` `remove(x):``    ``cnt[a[x]] ``-``=` `1` `    ``# Decrease the answer if the element``    ``# is completely removed from``    ``# this range``    ``if` `cnt[a[x]] ``=``=` `0``:``        ``ans[``0``] ``-``=` `1` `# Driver code``if` `__name__ ``=``=` `'__main__'``:``    ``n ``=` `12``    ``q ``=` `3``    ``block_size ``=` `int``(math.sqrt(n))` `    ``# Example array``    ``array ``=` `[``1``, ``2``, ``2``, ``2``, ``3``, ``4``, ``5``, ``5``, ``5``, ``6``, ``6``, ``6``]` `    ``# Store the elements in the array``    ``for` `i ``in` `range``(n):``        ``a[i ``+` `1``] ``=` `array[i]` `    ``# Example range queries``    ``l1, r1, l2, r2, l3, r3 ``=` `1``, ``4``, ``2``, ``6``, ``3``, ``8` `    ``# Store the query details``    ``qry ``=` `[Query(l1, r1, ``0``), Query(l2, r2, ``1``), Query(l3, r3, ``2``)]` `    ``# Sort the queries according to their``    ``# lth and rth indices``    ``qry.sort(key``=``lambda` `x: ``cmp``(x, x))` `    ``# l and r denote the current range``    ``l, r ``=` `1``, ``0` `    ``# Iterate through all the queries``    ``for` `i ``in` `range``(q):``        ``# Expand the range to the left``        ``while` `l > qry[i].l:``            ``add(l``-``1``)``            ``l ``-``=` `1` `        ``# Expand the range to the right``        ``while` `r < qry[i].r:``            ``add(r``+``1``)``            ``r ``+``=` `1` `        ``# Shrink the range from the left``        ``while` `l < qry[i].l:``            ``remove(l)``            ``l ``+``=` `1` `        ``# Shrink the range from the right``        ``while` `r > qry[i].r:``            ``remove(r)``            ``r ``-``=` `1` `        ``# Store the answer for the``        ``# current query``        ``ans[qry[i].``id``] ``=` `ans[``0``]` `    ``# Print answers to the queries``    ``for` `i ``in` `range``(q):``        ``print``(ans[i])`

## C#

 `using` `System;` `public` `class` `Program``{``  ``// N is the maximum number of elements``  ``// in the array``  ``const` `int` `N = 100005;``  ``static` `int` `n, q, block_size;``  ``static` `int``[] a = ``new` `int``[N], ans = ``new` `int``[N], cnt = ``new` `int``[N];``  ``// Query structure for storing query details``  ``struct` `Query``  ``{``    ``public` `int` `l, r, id;` `    ``public` `Query(``int` `l, ``int` `r, ``int` `id)``    ``{``      ``this``.l = l;``      ``this``.r = r;``      ``this``.id = id;``    ``}``  ``}` `  ``// Function to compare two queries``  ``static` `int` `cmp(Query a, Query b)``  ``{``    ``// If the query is in different blocks,``    ``// sort according to lth index``    ``if` `(a.l / block_size != b.l / block_size)``    ``{``      ``return` `a.l / block_size - b.l / block_size;``    ``}``    ``// If the queries are in the same block,``    ``// sort according to the parity of the``    ``// block number``    ``return` `(a.l / block_size & 1) != 0 ? a.r - b.r : b.r - a.r;``  ``}``  ``// Function to add an element to the``  ``// current range``  ``static` `void` `add(``int` `x)``  ``{``    ``cnt[a[x]]++;``    ``// Increase the answer if the element``    ``// is added for the first time in``    ``// this range``    ``if` `(cnt[a[x]] == 1)``    ``{``      ``ans[0]++;``    ``}``  ``}``  ``// Function to remove an element from``  ``// the current range``  ``static` `void` `remove(``int` `x)``  ``{``    ``// Decrease the answer if the element``    ``// is completely removed from``    ``// this range``    ``cnt[a[x]]--;` `    ``if` `(cnt[a[x]] == 0)``    ``{``      ``ans[0]--;``    ``}``  ``}``  ``// Driver code``  ``public` `static` `void` `Main()``  ``{``    ``n = 12;``    ``q = 3;``    ``block_size = (``int``)Math.Sqrt(n);` `    ``int``[] array = { 1, 2, 2, 2, 3, 4, 5, 5, 5, 6, 6, 6 };` `    ``// Store the elements in the array``    ``for` `(``int` `i = 0; i < n; i++)``    ``{``      ``a[i + 1] = array[i];``    ``}``    ``// Example range queries``    ``Query[] qry = {``      ``new` `Query(1, 4, 0),``      ``new` `Query(2, 6, 1),``      ``new` `Query(3, 8, 2)``      ``};` `    ``// Sort the queries according to their``    ``// lth and rth indices``    ``Array.Sort(qry, cmp);``    ``// l and r denote the current range``    ``int` `l = 1, r = 0;``    ``// Iterate through all the queries``    ``for` `(``int` `i = 0; i < q; i++)``    ``{``      ``// Expand the range to the left``      ``while` `(l > qry[i].l)``      ``{``        ``add(--l);``      ``}``      ``// Expand the range to the right``      ``while` `(r < qry[i].r)``      ``{``        ``add(++r);``      ``}``      ``// Shrink the range from the left``      ``while` `(l < qry[i].l)``      ``{``        ``remove(l++);``      ``}``      ``// Shrink the range from the right``      ``while` `(r > qry[i].r)``      ``{``        ``remove(r--);``      ``}``      ``// Store the answer for the``      ``// current query``      ``ans[qry[i].id] = ans[0];``    ``}``    ``//Print answer``    ``for` `(``int` `i = 0; i < q; i++)``    ``{``      ``Console.WriteLine(ans[i]);``    ``}``  ``}``}`

## Javascript

 `// Javascript code to implement the approaach` `// N is the maximum number of elements``// in the array``const N = 1e5 + 5;``let n = 0, q = 0, block_size = 0;``const a = ``new` `Array(N).fill(0);``const ans = ``new` `Array(N).fill(0);``const cnt = ``new` `Array(N).fill(0);` `// Query structure for storing query details``class Query {``  ``constructor(l, r, id) {``    ``this``.l = l;``    ``this``.r = r;``    ``this``.id = id;``  ``}``}`  `// Function to compare two queries``function` `cmp(a, b) {``    ``// If the query is in different blocks,``    ``// sort according to lth index``  ``if` `(Math.floor(a.l / block_size) !== Math.floor(b.l / block_size)) {``    ``return` `a.l < b.l;``  ``}``    ``// If the queries are in the same block,``    ``// sort according to the parity of the``    ``// block number``  ``return` `a.r < b.r ? (a.l % 2 === 0 ? 1 : -1) * (a.r - b.r) : (a.l % 2 === 0 ? 1 : -1) * (b.r - a.r);``}`  `// Function to add an element to the``// current range``function` `add(x) {``    ` `  ``// Increase the answer if the element``  ``// is added for the first time in``  ``// this range``  ``cnt[a[x]]++;``  ``if` `(cnt[a[x]] === 1) {``    ``ans[0]++;``  ``}``}`  `// Function to remove an element from``// the current range``function` `remove(x) {``  ``cnt[a[x]]--;``  ``// Decrease the answer if the element``  ``// is completely removed from``  ``// this range``  ``if` `(cnt[a[x]] === 0) {``    ``ans[0]--;``  ``}``}`  `// Driver code``n = 12;``q = 3;``block_size = Math.floor(Math.sqrt(n));` `// Example array``const array = [1, 2, 2, 2, 3, 4, 5, 5, 5, 6, 6, 6];` `// Store the elements in the array``for` `(let i = 0; i < n; i++) {``  ``a[i + 1] = array[i];``}` `// Example range queries``const l1 = 1, r1 = 4, l2 = 2, r2 = 6, l3 = 3, r3 = 8;``// Store the query details``const qry = [``new` `Query(l1, r1, 0), ``new` `Query(l2, r2, 1), ``new` `Query(l3, r3, 2)];` `// Sort the queries according to their``// lth and rth indices``qry.sort(cmp);`  `// l and r denote the current range``let l = 1, r = 0;` `// Iterate through all the queries``for` `(let i = 0; i < q; i++) {``    ` `  ``// Expand the range to the left``  ``while` `(l > qry[i].l) {``      ` `    ``add(l - 1);``    ``l--;``  ``}``  ` `  ``// Expand the range to the right``  ``while` `(r < qry[i].r) {``    ``add(r + 1);``    ``r++;``  ``}``  ` `  ``// Shrink the range from the left``  ``while` `(l < qry[i].l) {``    ``remove(l);``    ``l++;``  ``}``  ` `  ``// Shrink the range from the right``  ``while` `(r > qry[i].r) {``    ``remove(r);``    ``r--;``  ``}``  ` `  ``// Store the answer for the``  ``// current query``  ``ans[qry[i].id] = ans[0];``}` `// Print answers to the queries``for` `(let i = 0; i < q; i++) {``  ``console.log(ans[i]);``}` `// This code is contributed by sdeadityasharma`

Output

```4
3
4```

Time Complexity: O(Q * sqrt(N) + N*log N + Q*logQ)
Auxiliary Space: O(N)

### Advantages of the Double-Ended Priority Queue Approach:

• Faster Access Time: The double-ended priority queue approach allows for faster access times than other data structures because it can be accessed from both the front and the back of the queue.
• More Flexibility: Offers more flexibility than other data structures because the data structure allows for both enqueue and dequeue operations, which are typically costly operations. Additionally, the double-ended priority queue approach also allows for the insertion and deletion of elements at any point in the queue.
• Ease of Use: This approach is relatively straightforward to use and understand. This makes it a good choice for applications that require quick access to data.
• Improved Performance: It offers improved performance due to the ability to efficiently search in both directions.
• Flexible Design: This is a flexible design that can be adapted to different needs. It is possible to easily adjust the priority levels for different items in the queue, allowing for greater customization and flexibility.

### Disadvantages of the Double-Ended Priority Queue Approach:

• More Complex: The double-ended priority queue approach is more complex than other data structures and requires more time and effort to implement the data structure correctly.
• Limited Capacity: The double-ended priority queue approach has limited capacity, meaning it can only hold a certain amount of data. This can be a problem if the queue needs to store large amounts of data, as it will not be able to accommodate it.
• More Memory: This approach also requires more memory than other data structures because it needs to store both enqueue and dequeue information.

### Applications of the Double-Ended Priority Queue Approach:

• Scheduling: Often used in scheduling applications because the data structure allows for quick access to data and is relatively easy to implement and can also be used to store and access scheduling information, such as deadlines and appointments.
• Sorting: Also often used in sorting applications because the data structure allows for quick access to data and can also be used to store and access sorted data, such as lists of names or numbers.
• Data Analysis: This approach is also often used in data analysis applications. This approach can also be used to store and access large amounts of data, such as financial records or customer data.
• Task Management: The double-ended priority queue approach can be used for task management. It can be used to store tasks in a priority order, making it easier to prioritize them and ensure the most important tasks are completed first.
• Job Queuing: The double-ended priority queue approach can be used for job queuing to store jobs in priority order, allowing for efficient retrieval of the most important jobs.

### Conclusion:

The double-ended priority queue (DEQ) approach is an alternate sorting approach for MO’s algorithm. It has several advantages over the block sorting approach, such as being able to sort elements in both ascending and descending order, being more efficient, and requiring less storage space. However, it also has some drawbacks, such as requiring extra memory, being limited to a certain number of elements, and not being as flexible as block sorting. Despite these drawbacks, it may still be useful in certain applications due to its advantages.

Related Articles:

My Personal Notes arrow_drop_up