简体   繁体   中英

QUICK SORT: Printing Memory Addresses instead of array elements:

I have written the code for Quick-Sorting an array of integers, but upon printing it is showing kind of memory addresses on the terminal. I have been trying to figure out for a long time, but no sucess.

I have tried to compile it on online gdb compiler which gives an error: Segmentation Fault(core dumped), otherwise, I have tried to modify the code severalk times

#include<stdio.h>
void swap(int* a, int* b){
    int t = *a;
    *a = *b;
    *b = t;
}
int part(int arr[], int l, int r){    //for partioning the array
    int i=l, j=0;

    while(j<r){
            if(arr[j]<arr[r]){
                swap(&arr[j], &arr[i]);
                i++;
            }
        j++;
    }
    swap(&arr[i], &arr[r]);
return i;
}
void qsort(int arr[], int l, int r){    //recursive function for quicksort
    if(l<r){
        int p = part(arr, l, r);
        qsort(arr, l, p-1);
        qsort(arr, p+1, r);
    }
}
int main(void){
    int arr[] = {4, 10, 17, 5, 5, 6, 1 , 3, 0};

    qsort(arr, 0, 8);                 //function called

    for(int i=0; i<9; i++){            
        printf("%d ", arr[i]);       //for printing the array
    }
return 0;
}

Actual Result:
11016472 1974520423 11016472 6356864 6 6356800 4214784 11015616 1974521024

I was expecting the array to be sorted.

In part, when l is greater than 0, starting j at 0 in part includes part of the array to the “left” of l in processing and may result in i (which was initialized to l ) being incremented too many times, past r .

Changing int i=l, j=0; to int i=l, j=l; results in the program printing the desired output for the case in the question.

This problem could have been found by modifying qsort and part to print their parameters each time they are called, and to either print when they return or to indent the previous printing by the current recursion depth (which can be tracked, for debugging purposes, with a static counter). This promptly reveals a call to qsort with r set to 9, beyond the array, which then leads to examining how it came to be that way.

You need to set j to l , not 0 , upon entering part :

...
int part(int arr[], int l, int r){    //for partioning the array
    int i=l, j=l;

    while(j<r){ // line 10
...

If you don't do this, you will end up reading (and possibly writing) past the bounds of array when r is greater than 8 .

Your partitioning is broken. It appears to be loosely based on two algorithms, neither of which you implemented correctly.

int part(int arr[], int l, int r){    //for partitioning the array

this tells us we have some array and we want to partition the range between l and r . It isn't clear whether that goal is inclusive of those indexes, but it doesn't really matter because the subsequent code is broken anyway.

int i=l, j=0; // HERE. Why is j 0 ??

Later on, you access arr[j] multiple times, both read and write. If the range you're partitioning is in [l..r], there is no guarantee whatsoever that j , starting at zero, is in that range. Starting j at l is appropriate.

But there's a better way.

First, this is C. Within part the id arr isn't an array; it's a pointer . Let us suppose we have a sequence where the l is always starting at zero, and the r is simply the length. In that case the l argument would be fairly pointless, and our part function could just start like this:

int part(int arr[], int len)

Fair enough. Now we can implement a simple partition that chooses a pivot, partitions based on that pivot, and returns the index of where the pivot ended up. I'll not go in to the related topic of choosing a "good" pivot. There are several approaches, and I encourage you to investigate yourself. In the interest of simplicity we'll use a right-most-element pivot for this example:

int part(int arr[], int len)
{
    if (len <= 1)
        return 0;

    int pvt = 0;
    for (int i=0; i<len; ++i)
    {
        if (arr[i] < arr[len-1]) 
            swap(arr+i, arr + pvt++);
    }
    swap(arr+pvt, arr+len-1);

    return pvt;
}

Simple enough. Now how does this help the actual quick sort? It helps by using the offset address as the lead argument when partitioning the sequence. It also helps because now we can do to q_sort the same thing we did to part , namely get rid of one of the arguments and just use a base address and length:

void q_sort(int arr[], int len)
{
    if (len <= 1)
        return;

    int p = part(arr, len);
    q_sort(arr, p);
    q_sort(arr+p+1, len-p-1);
}

That's it. Note the second recursive call uses pointer arithmetic to position the base-address of the target sequence at one-past the pivot location. The length is adjusted accordingly.

The full program appears below, including a test harness that generates a 30 element sequence, then sorts and displays the results:

Code

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void swap(int* a, int* b)
{
    int t = *a;
    *a = *b;
    *b = t;
}

int part(int arr[], int len)
{
    if (len <= 1)
        return 0;

    int pvt = 0;
    for (int i=0; i<len; ++i)
    {
        if (arr[i] < arr[len-1])
            swap(arr+i, arr + pvt++);
    }
    swap(arr+pvt, arr+len-1);

    return pvt;
}

void q_sort(int arr[], int len)
{
    if (len <= 1)
        return;

    int p = part(arr, len);
    q_sort(arr, p);
    q_sort(arr+p+1, len-p-1);
}

#define N_ELEMS     30

int main(void)
{
    int arr[N_ELEMS];

    srand((unsigned int)time(NULL));
    for (int i=0; i<N_ELEMS; ++i)
    {
        arr[i] = 1 + (rand() % 99);
        printf("%d ", arr[i]);
    }
    fputc('\n', stdout);


    q_sort(arr, N_ELEMS);

    for (int i=0; i<N_ELEMS; ++i)
    {
        printf("%d ", arr[i]);
    }
    fputc('\n', stdout);

    return 0;
}

Output (varies, obviously)

21 95 18 10 57 60 60 6 37 72 44 99 91 25 14 5 50 58 16 18 81 5 52 42 15 40 58 16 76 61 
5 5 6 10 14 15 16 16 18 18 21 25 37 40 42 44 50 52 57 58 58 60 60 61 72 76 81 91 95 99 

The benefits here are two fold. First, there is no requirement the initial caller provide both ends of a sequence. Second, the functions are actually simpler .

Hope it helps.

The first time you call part() 'i' will never be incremented since no value is less than arr[8]. The function will return 'i' which is 0. You then call qsort(arr, l,p - 1 (which is -1)). At this point you will be swapping with arr[-1]. Most of the values of the arr array will be be swapped with random data from the stack due to the incorrect indices. That is why you getting the strange values.

int part(int arr[], int l, int r){    //for partioning the array
int i=l, j=0;

while(j<r){
        if(arr[j]<arr[r]){
            swap(&arr[j], &arr[i]);
            i++;
        }
    j++;
}
swap(&arr[i], &arr[r]);
return i;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM