QuickSort and Hoare Partition

前端 未结 7 1140
轻奢々
轻奢々 2020-12-01 14:33

I have a hard time translating QuickSort with Hoare partitioning into C code, and can\'t find out why. The code I\'m using is shown below:

void QuickSort(in         


        
相关标签:
7条回答
  • 2020-12-01 15:11

    To answer the question of "Why does Hoare partitioning work?":

    Let's simplify the values in the array to just three kinds: L values (those less than the pivot value), E values (those equal to the pivot value), and G value (those larger than the pivot value).

    We'll also give a special name to one location in the array; we'll call this location s, and it's the location where the j pointer is when the procedure finishes. Do we know ahead of time which location s is? No, but we know that some location will meet that description.

    With these terms, we can express the goal of the partitioning procedure in slightly different terms: it is to split a single array into two smaller sub-arrays which are not mis-sorted with respect to each other. That "not mis-sorted" requirement is satisfied if the following conditions are true:

    1. The "low" sub-array, that goes from the left end of the array up to and includes s, contains no G values.
    2. The "high" sub-array, that starts immediately after s and continues to the right end, contains no L values.

    That's really all we need to do. We don't even need to worry where the E values wind up on any given pass. As long as each pass gets the sub-arrays right with respect to each other, later passes will take care of any disorder that exists inside any sub-array.

    So now let's address the question from the other side: how does the partitioning procedure ensure that there are no G values in s or to the left of it, and no L values to the right of s?

    Well, "the set of values to the right of s" is the same as "the set of cells the j pointer moves over before it reaches s". And "the set of values to the left of and including s" is the same as "the set of values that the i pointer moves over before j reaches s".

    That means that any values which are misplaced will, on some iteration of the loop, be under one of our two pointers. (For convenience, let's say it's the j pointer pointing at a L value, though it works exactly the same for the i pointer pointing at a G value.) Where will the i pointer be, when the j pointer is on a misplaced value? We know it will be:

    1. at a location in the "low" subarray, where the L value can go with no problems;
    2. pointing at a value that's either an E or a G value, which can easily replace the L value under the j pointer. (If it wasn't on an E or a G value, it wouldn't have stopped there.)

    Note that sometimes the i and j pointer will actually both stop on E values. When this happens, the values will be switched, even though there's no need for it. This doesn't do any harm, though; we said before that the placement of the E values can't cause mis-sorting between the sub-arrays.

    So, to sum up, Hoare partitioning works because:

    1. It separates an array into smaller sub-arrays which are not mis-sorted relative to each other;
    2. If you keep doing that and recursively sorting the sub-arrays, eventually there will be nothing left of the array that's unsorted.
    0 讨论(0)
  • 2020-12-01 15:13
    1. move pivot to first. (eg, use median of three. switch to insertion sort for small input size.)
    2. partition,
      • repetitively swap currently leftmost 1 with currently rightmost 0.
        0 -- cmp(val, pivot) == true, 1 -- cmp(val, pivot) == false.
        stop if not left < right.
      • after that, swap pivot with rightmost 0.
    0 讨论(0)
  • 2020-12-01 15:21

    Your final code is wrong, since the initial value of j should be r + 1 instead of r. Otherwise your partition function always ignore the last value.

    Actually, HoarePartition works because for any array A[p...r] which contains at least 2 elements(i.e. p < r), every element of A[p...j] is <= every element of A[j+1...r] when it terminates. So the next two segments that the main algorithm recurs on are [start...q] and [q+1...end]

    So the right C code is as follows:

    void QuickSort(int a[],int start,int end) {
        if (end <= start) return;
        int q=HoarePartition(a,start,end);
        QuickSort(a,start,q);
        QuickSort(a,q + 1,end);
    }
    
    int HoarePartition (int a[],int p, int r) {
        int x=a[p],i=p-1,j=r+1;
        while (1) {
            do  j--; while (a[j] > x);
            do  i++; while (a[i] < x);
            if  (i < j) 
                swap(&a[i],&a[j]);
            else 
                return j;
        }
    }
    

    More clarifications:

    1. partition part is just the translation of the pseudocode. (Note the return value is j)

    2. for the recursive part, note that the base case checking (end <= start instead of end <= start + 1 otherwise you will skip the [2 1] subarray )

    0 讨论(0)
  • 2020-12-01 15:21

    First of all u misunderstood the Hoare's partition algorithm,which can be see from the translated code in c, Since u considered pivot as leftmost element of subarray.

    Ill explain u considering the leftmost element as pivot.

    int HoarePartition (int a[],int p, int r) 
    

    Here p and r represents the lower and upper bound of array which can be part of a larger array also(subarray) to be partitioned.

    so we start with the pointers(marker) initially pointing to before and after end points of array(simply bcoz using do while loop).Therefore,

    i=p-1,
    
    j=r+1;    //here u made mistake
    

    Now as per partitioning we want every element to the left of pivot to be less than or equal to pivot and greater than on right side of pivot.

    So we will move 'i' marker untill we get element which is greaterthan or equal to pivot. And similarly 'j' marker untill we find element less than or equal to pivot.

    Now if i < j we swap the elements bcoz both the elements are in wrong part of array. So code will be

    do  j--; while (a[j] <= x);                 //look at inequality sign
    do  i++; while (a[i] >= x);
    if  (i < j) 
        swap(&a[i],&a[j]);
    

    Now if 'i' is not less than 'j',that means now there is no element in between to swap so we return 'j' position.

    So now the array after partitioned lower half is from 'start to j'

    upper half is from 'j+1 to end'

    so code will look like

    void QuickSort(int a[],int start,int end) {
        int q=HoarePartition(a,start,end);
        if (end<=start) return;
        QuickSort(a,start,q);
        QuickSort(a,q+1,end);
    }
    
    0 讨论(0)
  • 2020-12-01 15:23

    Straightforward implementation in java.

    public class QuickSortWithHoarePartition {
    
        public static void sort(int[] array) {
            sortHelper(array, 0, array.length - 1);
        }
    
        private static void sortHelper(int[] array, int p, int r) {
            if (p < r) {
                int q = doHoarePartitioning(array, p, r);
                sortHelper(array, p, q);
                sortHelper(array, q + 1, r);
            }
        }
    
        private static int doHoarePartitioning(int[] array, int p, int r) {
            int pivot = array[p];
            int i = p - 1;
            int j = r + 1;
    
            while (true) {
    
                do {
                    i++;
                }
                while (array[i] < pivot);
    
                do {
                    j--;
                }
                while (array[j] > pivot);
    
                if (i < j) {
                    swap(array, i, j);
                } else {
                    return j;
                }
            }
    
        }
    
        private static void swap(int[] array, int i, int j) {
            int temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }
    }
    
    0 讨论(0)
  • 2020-12-01 15:26

    You last C code works. But it's not intuitive. And now I'm studying CLRS luckily. In my opinion, The pseudocode of CLRS is wrong.(At 2e) At last, I find that it would be right if changing a place.

     Hoare-Partition (A, p, r)
     x ← A[p]
         i ← p − 1
         j ← r + 1
     while  TRUE
            repeat   j ←  j − 1
                until     A[j] ≤ x
        repeat   i ←  i + 1
                until     A[i] ≥ x
        if  i < j
                  exchange  A[i] ↔ A[j]
        else  
                  exchnage  A[r] ↔ A[i]  
                  return   i
    

    Yes, Add a exchange A[r] ↔ A[i] can make it works. Why? Because A[i] is now bigger than A[r] OR i == r. So We must exchange to guarantee the feature of a partition.

    0 讨论(0)
提交回复
热议问题