How does the following quicksort method work?
I wrote my own Quicksort method for educational purposes. To improve it, I took a look at the .NET source code to see how the LINQ method is implemented OrderBy()
.
I found the following Quicksort method :
void QuickSort(int[] map, int left, int right) {
do {
int i = left;
int j = right;
int x = map[i + ((j - i) >> 1)];
do {
while (i < map.Length && CompareKeys(x, map[i]) > 0) i++;
while (j >= 0 && CompareKeys(x, map[j]) < 0) j--;
if (i > j) break;
if (i < j) {
int temp = map[i];
map[i] = map[j];
map[j] = temp;
}
i++;
j--;
} while (i <= j);
if (j - left <= right - i) {
if (left < j) QuickSort(map, left, j);
left = i;
}
else {
if (i < right) QuickSort(map, i, right);
right = j;
}
} while (left < right);
}
I am trying to understand the inner workings. AFAIK looks very similar to Hoare's partition layout , but with minor optimizations.
What is not clear to me:
- Why do we only replicate one side of the bar after splitting? (depending on the result
if (j - left <= right - i)
) - Why do we have
do { ... } while (left < right)
all over this? Is it because we are recursing only one side of the pivot as suggested above? - Why is there a pre-
if (i < j)
exchange test ? Isn't expression enoughbreak;
?
In comparison, this is what my actual implementation of Quicksort looks like (direct implementation of Hoare's partition scheme)
void QuickSort(int[] map, int left, int right)
{
if (left < right)
{
int i = left - 1;
int j = right + 1;
int x = map[left + ((right - left) >> 1)];
while (true)
{
do { i++; } while (Compare(map[i], x) < 0);
do { j--; } while (Compare(map[j], x) > 0);
if (i >= j) break;
int temp = map[i];
map[i] = map[j];
map[j] = temp;
}
QuickSort(map, left, j);
QuickSort(map, j + 1, right);
}
}
source to share
Why do we recurse only one side of the pivot after partitionning? (depending on the result if (j - left <= right - i))
To minimize recursion depth (and stack usage). When we process a larger section as soon as possible and only recurse on a smaller section, the depth does not go above log (n)
Why do we have {...} while (left <right) all over the subject?
The before left
and after elements right
are sorted, so these indices are encountered when the entire array is sorted
Why is there a pre-test if (i <j) before replacement?
Just to avoid unnecessary replacement for equal indices
source to share