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 enough break;

    ?

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);
    }
}

      

+3


source to share


1 answer


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

+3


source







All Articles