【问题标题】:Reversing Doublely Linked Deque in C在 C 中反转双链接双端队列
【发布时间】:2011-03-18 05:42:40
【问题描述】:

我无法在 C 中反转我的双链双端队列列表(只有一个后哨),我正在通过切换指针来接近它,这是我到目前为止的代码:

/* 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;
 }
}

但它似乎不起作用,我一直在用一个看起来像这样的双端队列测试它:1、2、3 输出是:3,这个过程似乎弄乱了数字的实际值。 IE。 2变成2.90085e-309...我认为指针切换搞砸了,但我找不到问题。即使这并不意味着我的代码是正确的;它编译得很好。

【问题讨论】:

  • 如果这是一个家庭作业,你应该在你的问题中添加 homework 标签。

标签: c linked-list deque


【解决方案1】:

像双端队列这样的链接结构很容易递归,所以在处理链接结构时我倾向于使用递归风格。这也允许我们以增量方式编写它,以便我们可以轻松地测试每个功能。像你的函数那样循环有很多缺点:你可以很容易地引入fencepost errors,它往往会导致令人困惑的大型函数。

首先,您决定通过交换指针来做到这一点,对吧?所以写一个函数来交换指针:

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

现在,编写一个反转单个节点中的指针的函数:

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

现在,递归地把它们放在一起:

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
}

我不确定你的结构是如何设计的;我的函数假定您的双端队列是循环的,并且您将在哨兵上调用它。

编辑:如果你的双端队列不是循环的,你也需要在哨兵上调用swapPointersInCirListDeque(q),所以在if 语句之前移动swapPointersInCirListDeque(q)

如果您打算在此之后使用 backSentinel,您也应该更改它,因为它现在是列表的前面。如果您有frontSentinel,您只需将swapCirListDequePointers(&(q->frontSentinel),&(q->backSentinel)); 添加到swapPointersInCirListDeque。否则,您必须将第一个节点与 q 一起传递,并将 q->backSentinel 设置为该节点。

【讨论】:

    【解决方案2】:

    如果它是一个双向链表,则根本不需要更改任何指针。只需交换有效负载:

    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
    

    如果后面的哨兵是指last 指针(因为没有第一个指针可用),那么您需要后退一步并抛出双端队列来找到它。但是很难相信会出现这种情况,因为这将是一个相当低效的双端队列(应该是一个双端队列)。

    【讨论】:

    • 在有效载荷中你的意思是交换值?
    • @Dacto,是的。由于列表的 结构 没有改变,因此更改指针没有意义(请原谅我对双关语的拙劣尝试)。
    • @paxdiablo 您可以更改列表的结构(通过更改指针)您可以更改结构中的值,就像您所做的那样;两种方式都有效。更改指针会稍微快一些,因为您一次只需要跟踪一个节点(q),而您必须跟踪两个(pointer1pointer2)需要更多操作和堆栈空间。
    • @paxdiablo 另外,我认为双端队列是循环的,所以没有第一个和最后一个,只有单个哨兵。
    • @kerkeslager,我怀疑速度是否会受到您存储的项目数量的很大影响。在我看来,有效负载交换是三个操作,与指针交换相同。如果有效负载比指针大得多,它可能会更慢,但除非你每秒执行数千次,否则不会产生影响。至于堆栈空间,它只是一个额外的指针,远远小于用于递归解决方案的堆栈:-)
    【解决方案3】:

    您已经收到了一些建议;这是另一种可能性:

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

    并且还假设了两个变量(暂时是全局变量),分别命名为 headtail,它们分别指向双端队列的头部和尾部。

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

    至少目前,这假定任何哨兵——只是指示列表末端的空指针。

    如果您只是将ints 存储在双端队列中,Paxdiablo 的建议是一个很好的建议(除了创建一个双链接节点来仅保存int 是一种巨大的浪费)。假设实际上您存储了足够大的数据以使双链接节点有意义,那么您也更愿意避免在不必要的情况下移动该数据,至少作为一般规则。

    【讨论】:

      猜你喜欢
      • 2017-02-28
      • 2018-07-02
      • 2014-10-12
      • 1970-01-01
      • 2010-12-28
      • 1970-01-01
      • 1970-01-01
      • 2011-02-14
      • 2023-03-28
      相关资源
      最近更新 更多