In QuickSort, ideal situation is when median is always chosen as pivot as this results in minimum time. In this article, Merge Sort Tree is used to find median for different ranges in QuickSort and number of comparisons are analyzed.

Examples:

Input : arr = {4, 3, 5, 1, 2} Output : 11ExplanationWe have to make 11 comparisons when we apply quick sort to the array.

If we carefully analyze the quick sort algorithm then every time the array is given to the quick sort function, the array always consists of a permutation of the numbers in some range L to R. Initially, it’s [1 to N], then its [1 to pivot – 1] and [pivot + 1 to N] and so on. Also it’s not easy to observe that the relative ordering of numbers in every possible array does not change. Now in order to get the pivot element we just need to get the middle number i.e the (r – l + 2)/2^{th} number among the array.

To do this efficiently we can use a Persistent Segment Tree, a Fenwick Tree, or a Merge Sort Tree. This Article focuses on the Merge Sort Tree Implementation.

In the Modified Quick Sort Algorithm where we chose the pivot element of the array as the median of the array. Now, determining the median requires us to find the middle element considered, after sorting the array which is in itself a O(n*log(n)) operation where n is the size of the array.

Let’s say we have a range L to R then the median of this range is calculated as:

Median of A[L; R] = Middle element of sorted(A[L; R]) = (R - L + 1)/2^{th}element of sorted(A[L; R])

Let’s consider we have P partitions during the quick sort algorithm which means we have to find the pivot by sorting the array range from L to R where L and R are the starting and ending points of each partition. This is costly.

**But**, we have a permutation of numbers from L to R in every partition, so we can just find the ceil((R – L + 1)/2)^{th} smallest number in this range as we know when we would have sorted this partition then it would always would have been this element that would have ended up as being the median element as a result also the pivot. Now the elements less than pivot go to the left subtree and the ones greater than it go in the right subtree also mantaining their order.

We repeat this procedure for all partitions P and find the comparisons involved in each partition. Since in the Current Partition all the elements from L to R of that partition are compared to the pivot, we have (R – L + 1) comparisons in the current partition. We also need to consider, by recursively calculating, the total comparisons in the left and right subtrees formed too. Thus we conclude,

Total Comparisons = Comparisons in Current Partition + Comparisons in Left partition + Comparisons in right partition

We discussed above the approach to be used to find the pivot element efficiently here the

K^{th} order statistics using merge sort tree can be used to find the same as discussed.

`// CPP program to implement number of comparisons` `// in modified quick sort` `#include <bits/stdc++.h>` `using` `namespace` `std;` ` ` `const` `int` `MAX = 1000;` ` ` `// Constructs a segment tree and stores tree[]` `void` `buildTree(` `int` `treeIndex, ` `int` `l, ` `int` `r, ` `int` `arr[],` ` ` `vector<` `int` `> tree[])` `{` ` ` ` ` `/* l => start of range,` ` ` `r => ending of a range` ` ` `treeIndex => index in the Segment Tree/Merge ` ` ` `Sort Tree */` ` ` `/* leaf node */` ` ` `if` `(l == r) {` ` ` `tree[treeIndex].push_back(arr[l - 1]);` ` ` `return` `;` ` ` `}` ` ` ` ` `int` `mid = (l + r) / 2;` ` ` ` ` `/* building left subtree */` ` ` `buildTree(2 * treeIndex, l, mid, arr, tree);` ` ` ` ` `/* building left subtree */` ` ` `buildTree(2 * treeIndex + 1, mid + 1, r, arr, tree);` ` ` ` ` `/* merging left and right child in sorted order */` ` ` `merge(tree[2 * treeIndex].begin(),` ` ` `tree[2 * treeIndex].end(),` ` ` `tree[2 * treeIndex + 1].begin(),` ` ` `tree[2 * treeIndex + 1].end(),` ` ` `back_inserter(tree[treeIndex]));` `}` ` ` `// Returns the Kth smallest number in query range` `int` `queryRec(` `int` `segmentStart, ` `int` `segmentEnd,` ` ` `int` `queryStart, ` `int` `queryEnd, ` `int` `treeIndex,` ` ` `int` `K, vector<` `int` `> tree[])` `{` ` ` `/* segmentStart => start of a Segment,` ` ` `segmentEnd => ending of a Segment,` ` ` `queryStart => start of a query range,` ` ` `queryEnd => ending of a query range,` ` ` `treeIndex => index in the Segment ` ` ` `Tree/Merge Sort Tree,` ` ` `K => kth smallest number to find */` ` ` `if` `(segmentStart == segmentEnd)` ` ` `return` `tree[treeIndex][0];` ` ` ` ` `int` `mid = (segmentStart + segmentEnd) / 2;` ` ` ` ` `// finds the last index in the segment` ` ` `// which is <= queryEnd` ` ` `int` `last_in_query_range = ` ` ` `(upper_bound(tree[2 * treeIndex].begin(),` ` ` `tree[2 * treeIndex].end(), queryEnd)` ` ` `- tree[2 * treeIndex].begin());` ` ` ` ` `// finds the first index in the segment` ` ` `// which is >= queryStart` ` ` `int` `first_in_query_range = ` ` ` `(lower_bound(tree[2 * treeIndex].begin(),` ` ` `tree[2 * treeIndex].end(), queryStart)` ` ` `- tree[2 * treeIndex].begin());` ` ` ` ` `int` `M = last_in_query_range - first_in_query_range;` ` ` ` ` `if` `(M >= K) {` ` ` ` ` `// Kth smallest is in left subtree,` ` ` `// so recursively call left subtree for Kth` ` ` `// smallest number` ` ` `return` `queryRec(segmentStart, mid, queryStart,` ` ` `queryEnd, 2 * treeIndex, K, tree);` ` ` `}` ` ` ` ` `else` `{` ` ` ` ` `// Kth smallest is in right subtree,` ` ` `// so recursively call right subtree for the` ` ` `// (K-M)th smallest number` ` ` `return` `queryRec(mid + 1, segmentEnd, queryStart,` ` ` `queryEnd, 2 * treeIndex + 1, K - M, tree);` ` ` `}` `}` ` ` `// A wrapper over query()` `int` `query(` `int` `queryStart, ` `int` `queryEnd, ` `int` `K, ` `int` `n, ` `int` `arr[],` ` ` `vector<` `int` `> tree[])` `{` ` ` ` ` `return` `queryRec(1, n, queryStart, queryEnd,` ` ` `1, K, tree);` `}` ` ` `/* Calculates total Comparisons Involved in Quick Sort` ` ` `Has the following parameters:` ` ` ` ` `start => starting index of array` ` ` `end => ending index of array` ` ` `n => size of array` ` ` `tree => Merge Sort Tree */` ` ` `int` `quickSortComparisons(` `int` `start, ` `int` `end, ` `int` `n, ` `int` `arr[],` ` ` `vector<` `int` `> tree[])` `{` ` ` `/* Base Case */` ` ` `if` `(start >= end)` ` ` `return` `0;` ` ` ` ` `// Compute the middle point of range and the pivot` ` ` `int` `middlePoint = (end - start + 2) / 2;` ` ` `int` `pivot = query(start, end, middlePoint, n, arr, tree);` ` ` ` ` `/* Total Comparisons = (Comparisons in Left part + ` ` ` `Comparisons of right +` ` ` `Comparisons in parent) */` ` ` ` ` `// count comparisons in parent array` ` ` `int` `comparisons_in_parent = (end - start + 1);` ` ` ` ` `// count comparisons involved in left partition` ` ` `int` `comparisons_in_left_part = ` ` ` `quickSortComparisons(start, pivot - 1, n, arr, tree);` ` ` ` ` `// count comparisons involved in right partition` ` ` `int` `comparisons_in_right_part = ` ` ` `quickSortComparisons(pivot + 1, end, n, arr, tree);` ` ` ` ` `// Return Total Comparisons` ` ` `return` `comparisons_in_left_part + ` ` ` `comparisons_in_parent + ` ` ` `comparisons_in_right_part;` `}` ` ` `// Driver code` `int` `main()` `{` ` ` `int` `arr[] = { 4, 3, 5, 1, 2 };` ` ` ` ` `int` `n = ` `sizeof` `(arr) / ` `sizeof` `(arr[0]);` ` ` ` ` `// Construct segment tree in tree[]` ` ` `vector<` `int` `> tree[MAX];` ` ` `buildTree(1, 1, n, arr, tree);` ` ` ` ` `cout << ` `"Number of Comparisons = "` ` ` `<< quickSortComparisons(1, n, n, arr, tree);;` ` ` ` ` `return` `0;` `}` |

Output:

Number of Comparisons = 11

Complexity is O(log^{2}(n)) per query for computing pivot

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the **DSA Self Paced Course** at a student-friendly price and become industry ready. To complete your preparation from learning a language to DS Algo and many more, please refer **Complete Interview Preparation Course****.**

In case you wish to attend live classes with industry experts, please refer **Geeks Classes Live** and **Geeks Classes Live USA**