views:

83

answers:

3

I'm having trouble reversing my doublely linked deque list (with only a back sentinel) in C, I'm approaching it by switching the pointers and here is the code I have so far:

/* Reverse the deque

 param:  q  pointer to the deque
 pre: q is not null and q is not empty
 post:  the deque is reversed
*/
/* reverseCirListDeque */
void reverseCirListDeque(struct cirListDeque *q)
{
 struct DLink *back = q->backSentinel;
 struct DLink *second = q->backSentinel->prev;
 struct DLink *third = q->backSentinel->next;

 while (second != q->backSentinel->next){
  back->next = second;
  third = back->prev;
  back->next->prev = back;
  back = second;
  second = third;
 }
}

But it doesn't seem to work, I've been testing it with a deque that looks like this: 1, 2, 3 The output is: 3 and this process seems to mess up the actual value of the numbers. ie. 2 becomes 2.90085e-309... I think the pointer switching is messed up but I cannot find the problem. And even though it doesn't mean my code is correct; it compiles fine.

+1  A: 

If it's a doubly linked list, you shouldn't need to change any pointers at all. Just swap over the payloads:

pointer1 = first
pointer2 = last
while pointer1 != pointer2 and pointer2->next != pointer1:
    temp = pointer1->payload
    pointer1->payload = pointer2->payload
    pointer2->payload = temp
    pointer1 = pointer1->next
    pointer2 = pointer2->prev

If by back sentinel you mean the last pointer (as in no first pointer is available), then you need to step backwards throw the deque to find it. It's hard to believe however that this would be the case since it would be a fairly inefficient deque (which is supposed to be a double ended queue).

paxdiablo
in payload you mean just swap the values?
Dacto
@Dacto, yes. There's no point (forgive my poor attempt at a pun) in changing pointers since the _structure_ of the list is not changing.
paxdiablo
@paxdiablo You can change either the structure of the list (by changing the pointers) *or* you can change the values in the structure, as you did; both ways work. It's slightly faster to change the pointers because then you only have to keep track of one node at a time (`q`) whereas your way you have to keep track of two (`pointer1` and `pointer2`) requiring more operations and stack space.
kerkeslager
@paxdiablo Also, I think the deque is circular, so there is no first and last, only the single sentinel.
kerkeslager
@kerkeslager, I doubt the speed is going to be much affected by the number of items you're _storing_. It appears to me that a payload swap is three operations, same as a pointer swap. It may be slower if the payload is substantially larger than the pointers but, unless you're doing this many thousands of times a second, it's not going to have an impact. As for stack space, it's only an extra pointer, far less stack than that used for a recursive solution :-)
paxdiablo
@paxdiablo I agree that the difference in speed is probably irrelevant. However, my solution does *not* use more stack space, at least not on any decent optimizing compiler: see http://stackoverflow.com/questions/310974/what-is-tail-call-optimization . Basically, my recursive function gets optimized into a loop (but with better compiler checking beforehand).
kerkeslager
+2  A: 

Linked structures like deques lend themselves readily to recursion, so I tend to favor a recursive style when dealing with linked structures. This also allows us to write it incrementally so that we can test each function easily. Looping as your function does has many downsides: you can easily introduce fencepost errors and it tends toward large functions that are confusing.

First, you've decided to do this by swapping the pointers, right? So write a function to swap pointers:

void swapCirListDequePointers(
    struct cirListDeque** left,
    struct cirListDeque** right)
{
    struct cirListDeque* temp = *left;
    *left = *right;
    *right = temp;
}

Now, write a function that reverses the pointers in a single node:

void swapPointersInCirListDeque(struct cirListDeque* q)
{
    swapCirListDequePointers(&(q->prev),&(q->next));
}

Now, put it together recursively:

void reverseCirListDeque(struct cirListDeque* q)
{
    if(q == q->backSentinel)
        return;

    swapPointersInCirListDeque(q);

    // Leave this call in tail position so that compiler can optimize it
    reverseCirListDeque(q->prev); // Tricky; this used to be q->next
}

I'm not sure exactly how your struct is designed; my function assumes that your deque is circular and that you'll be calling this on the sentinel.

EDIT: If your deque isn't circular, you'll want to call swapPointersInCirListDeque(q) on the sentinel as well, so move swapPointersInCirListDeque(q) before the if statement.

If you plan to use the backSentinel after this, you should change that also, since it's now the front of the list. If you have a frontSentinel, you can just add swapCirListDequePointers(&(q->frontSentinel),&(q->backSentinel)); to swapPointersInCirListDeque. Otherwise, you'll have to pass in the first node along with q and set q->backSentinel to that.

kerkeslager
A: 

You've been given a couple of suggestions already; here's another possibility:

// Assumes a node something like:
typedef struct node { 
    struct node *next, *prev;
    int data;
} node;

and also assumes a couple of variables (globals for the moment) named head and tail that point to the head and tail of the deque, respectively.

void reverse()  {
    node *pos = head;
    node *temp = pos->next;

    head = tail;
    tail = pos;

    while (pos != NULL) {
        node *t = pos->prev;
        pos->prev = pos->next;
        pos->next = t;
        pos = temp;
        if (temp)
            temp = temp->next;
    }   
}

At least for the moment, this does not assume any sentinels -- just NULL pointers to signal the ends of the list.

If you're just storing ints in the deque, Paxdiablo's suggestion is a good one (except that creating a doubly-linked node to hold only an int is a massive waste). Assuming that in reality you were storing something large enough for doubly-linked nodes to make sense, you'd also prefer to avoid moving that data around any more than necessary, at least as a general rule.

Jerry Coffin