【问题标题】:Pairwise swapping of elements in a linked list (by changing pointers)链表中元素的成对交换(通过更改指针)
【发布时间】:2017-03-11 03:45:30
【问题描述】:

问题陈述如下:

给定一个单链表,编写一个函数来成对交换元素。例如,如果链表是 1->2->3->4->5->6->7,则函数应将其更改为 2->1->4->3->6->5 ->7,如果链表是 1->2->3->4->5->6,那么函数应该将其更改为 2->1->4->3->6->5

这是我的解决方案:

void swapPairs(Node* head) {
    if (!head->next || !head)return head;
    Node* current = head->next;
    Node*prev = head;

    while (true) {
        Node* next = current->next;
        current->next = prev;
        if (!next || !next->next) {
            prev->next = next;
            break;
        }

        prev->next = next->next;
        prev = next;
        current = prev->next;   
    }
}

解决方案的问题是,当它到达函数的末尾时,头部不再指向列表的开头,因为 current 和 prev 都已移动。我在网上看到其他解决方案与我在这里做的事情相同,但也许因为它们是用 C 而不是 C++ 实现的,所以它们的代码的某些方面允许头部保持指向开始列表。

我意识到这可能是一个非常容易解决的问题,但我目前还没有看到解决方案。 在整个算法中保持列表开头的最佳方式是什么?

【问题讨论】:

    标签: c++ algorithm linked-list


    【解决方案1】:

    一旦你经历了轻微的范式转变,手头的任务就会简单得多。乍一看可能比较复杂,但稍微琢磨一下,应该很简单了。

    简单地说:当您遍历列表时,不要携带指向每对列表元素中第一个元素的指针,而是携带指向每对列表元素中第一个元素的指针。从表面上看,这听起来很复杂。但是一旦尘埃落定,这将大大简化手头的任务。

    您必须单独存储一个头指针,并将其传递给swapPairs()

    Node *head;
    
    // The list gets populated here, then ...
    
    swapPairs(head);
    

    这就是你现在正在做的事情。但与其这样做,不如将指针传递给头节点:

    swapPairs(&head);
    
    // ...
    
    void swapPairs(Node **ptr)
    {
        while ( (*ptr) && (*ptr)->next)
        {
              // swap the next two elements in the list
    
              // ...
    
              // Now, advance by two elements, after swapping them.
              ptr= &(*ptr)->next->next;
        }
    }
    

    while 循环的主体将交换列表中接下来的两个元素,现在让我们跳过这部分,只关注遍历这个链表的这一点逻辑,一对元素位于一次。这是怎么回事?

    嗯,你正试图遍历列表,一次两个元素。

    请记住,ptr 不再是指向该对的第一个元素的指针。它是指向该对的第一个元素的指针恰好存在的位置的指针。所以最初的ptr 指向你原来的head 指针。

    一旦你理解了这一点,下一个精神上的飞跃就是明白只要列表中至少还有两个元素,你就想循环迭代:

        while ( (*ptr) && (*ptr)->next)
    

    *ptr 是列表的下一个元素,也是对中的第一个元素,因此 (*ptr)->next 将是指向对中第二个元素的指针。由于我们的迭代方式,ptr 可以通过合同证明永远不会是NULL。因此,如果 *ptr 为 NULL,则您到达了列表的末尾。但是如果*ptr 不为NULL,则至少还剩下一个元素,并且 (*ptr)->next 是“下一个第二个元素”。如果 (*ptr)->next 为 NULL,则原始列表中有奇数个元素,因此在最后一次迭代中,您最终以 *ptr 指向奇数鸭。而且你也完成了这种情况,不需要再继续了。

    最后:

    ptr= &(*ptr)->next->next;
    

    这只是将ptr 前进了列表的两个元素。请记住,ptr 是一个指针,指向指向列表中接下来两个元素中第一个元素的指针“恰好存在”的任何位置,现在这将 ptr 设置为指向指向第一个 *** 的指针的位置next**** 列表中的两个元素恰好存在。拿一张纸,画一些图表,然后自己弄清楚。不要通过“Go”,不要收取 200 美元,直到它沉入其中。

    一旦你把你的大脑围绕着它,唯一剩下的就是交换对。就是这样。循环体可以简单地是:

    Node *first=*ptr;
    Node *second=first->next;
    Node *next=second->next;
    
    *ptr=second;
    second->next=first;
    first->next=next;
    

    就是这样。你完成了。你猜怎么着?您原来的head 现在自动指向交换列表的右头节点。

    现在,不是那么容易吗?

    【讨论】:

    • 有道理。这基本上与其他答案做同样的事情,对吗?
    【解决方案2】:

    将函数的签名改为

    Node* swapPairs(Node* head)
    

    并在第一次交换后保存新的头,然后在你的while 循环结束} 之后,返回那个新头,以替换调用者持有的旧列表头,这将使用类似的东西:

    list = swapPairs(list);
    

    【讨论】:

      【解决方案3】:

      您可以在函数的开头保留一个指向新头的指针,并用它更新函数末尾的head。请注意,我将参数从Node* 更改为Node **,因为我们想要更新指针值,以便在更新head 时,我们写入传递给此函数的相同内存位置。因此,当我们从该函数的调用者中返回时,head 的正确值将可用。

      void swapPairs(Node **head) {
        if (!(*head)->next || !(*head))
          return;
        Node *new_head = (*head)->next;    // the new_head
        Node *current = (*head)->next;
        Node *prev = (*head);
      
        while (true) {
          Node *next = current->next;
          current->next = prev;
          if (!next || !next->next) {
              prev->next = next;
              break;
          }
      
          prev->next = next->next;
          prev = next;
          current = prev->next;   
        }
        *head = new_head;   // now update the head
      } 
      

      【讨论】:

        猜你喜欢
        • 2021-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-07
        • 1970-01-01
        相关资源
        最近更新 更多