简体   繁体   中英

Iterative QuickSort in Doubly Linked List in C

I have here a function to quicksort a doubly linked list that uses recursion method. I wonder how can I change this function from recursive to iterative quicksort. I've been trying but I just couldn't get the logic on how it's done.

Edit: I have revised the code and I couldn't get the list sorted. I just followed the algorithm on how quicksort is implemented using array. It has no errors though.

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

struct Node
{
    int val;
    struct Node *next;
    struct Node *prev;
};

struct Stack
{
    struct Node* top;
};

typedef struct Stack *stackPtr;

struct Node *CreateNode(int data)
{
    struct Node *newNode = (struct Node *)malloc(sizeof(struct Node));
    newNode->next = newNode->prev = NULL;
    newNode->val = data;
    
    return newNode;
}

stackPtr CreateStack()
{
    stackPtr stack = (stackPtr)malloc(sizeof(struct Stack));
    stack->top = NULL;
}

void Push(stackPtr stack, int data)
{
    struct Node *newNode = CreateNode(data);
    newNode->next = stack->top;
    stack->top = newNode;   
}

void Pop(stackPtr stack)
{
    struct Node *temp;
    temp = stack->top;
    stack->top = stack->top->next;
    free(temp);
}

struct Node *partition(struct Node *left, struct Node *right)
{
    struct Node *pivot = right;
    struct Node *i = left->prev;
    struct Node *ptr;
    for (ptr = left; ptr != right; ptr = ptr->next)
    {
        if (ptr->val <= pivot->val)
        {
            i = (i == NULL ? left : i->next);
            int temp = i->val;
            i->val = ptr->val;
            ptr->val = temp;
        }
    }
    i = (i == NULL ? left : i->next);
    int temp = i->val;
    i->val = pivot->val;
    pivot->val = temp;
    return i;
}
void QuickSort(struct Node *left, struct Node *right)
{    
    stackPtr auxStack = CreateStack();
    Push(auxStack, left->val);
    Push(auxStack, right->val);
    
    while(auxStack->top != NULL)
    {
        Pop(auxStack);
    }
    
    struct Node *pivot = partition(left, right);
    
    if((pivot->val - 1) > left->val)
    {
        Push(auxStack, left->val);
        Push(auxStack, (pivot->val - 1));
    }
    
    if((pivot->val + 1) < right->val)
    {
        Push(auxStack, (pivot->val + 1));
        Push(auxStack, right->val);
    }
}

int main()
{
    struct Node *head = malloc(sizeof(struct Node));
    head->val = 2;
    head->prev = NULL;
    struct Node *l1 = malloc(sizeof(struct Node));
    l1->val = 8;
    l1->prev = head;
    head->next = l1;
    struct Node *l2 = malloc(sizeof(struct Node));
    l2->val = 3;
    l2->prev = l1;
    l1->next = l2;
    struct Node *l3 = malloc(sizeof(struct Node));
    l3->val = 5;
    l3->prev = l2;
    l2->next = l3;
    struct Node *l4 = malloc(sizeof(struct Node));
    l4->val = 10;
    l4->prev = l3;
    l3->next = l4;
    l4->next = NULL;
    // 2<=>8<=>3<=>5<=>10=>NULL

    QuickSort(head, l4);
    while (head != NULL)
    {
        printf("%d ", head->val);
        head = head->next;
    }
    return 0;
}

Let's say you have a working recursive quicksort function:

void quicksort_rec(int *a, const int *end)
{
    if (a + 1 < end) {
        int *pivot = partition(a, end);

        quicksort_rec(a, pivot);
        quicksort_rec(pivot + 1, end);
    }
}

(This is a quicksort on an array and it takes a range in the form of a pointer to the start and (exclusive) end of the array. Never mind the details of the partition function.)

When you call quicksort_rec recursively, the local variables of the called functions are stored on the call stack: Pivots and ranges of "parent" recursions are still available as the recursion depth grows.

If you want to turn this into an iterative function, you must maintain a stack manually. (John Bollinger has described this in more detail and better than I can in the comments.)

So instead of calling quicksort(a, b) , you must push a and b on the stack to "save them for later". The last things pushed on a stack are the first things popped off, so the right-hand array will be sorted next.

Given a stack implementation that stores pointers to int , we can turn the recursive function above into an iterative function:

void quicksort_iter(int *a, int *end)
{
    Stack stack = {0};
    
    push(&stack, a);
    push(&stack, end);

    while (isempty(&stack)) {
        end = pop(&stack);
        a = pop(&stack);
        
        if (a + 1 < end) {
            int *pivot = partition(a, end);
            
            push(&stack, a);
            push(&stack, pivot);
        
            push(&stack, pivot + 1);
            push(&stack, end);
        }
    }
}

This code always pushes or pops two items simultaneously. Note that the begin and end pointers must be popped in the reverse order in which they were pushed. You could also create a struct of pwo pointers and always push one such struct, which may be cleaner. You could also optimize this by not pushing sub-arrays on the stack if they are known to have fewer than two items.

The exact implementation is left to the reader, yadda, yadda, ... :) But here's a full implementation of the code above to get you started.

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