【问题标题】:Linked list reversed in groups of K nodes以 K 个节点为一组反转的链表
【发布时间】:2020-12-25 19:50:33
【问题描述】:

最初,面试官问的问题是反转我很容易解决的链表。
现在他说要反转 K 节点组中的列表。

例如,如果列表是[1,2,3,4,5,6]K=4,那么o/p = [4,3,2,1,5,6].。所以我修改了现有的解决方案来实现它,但它仍然使整个列表的输出反转(i.e [6,5,4,3,2,1])。请参阅下面的程序。这可能需要一些小的改变,但我无法弄清楚。任何人都可以帮助解决问题吗?

ListNode *reverseKGroup(ListNode *head, int k) 
{
    if (k == 0 || k == 1)
        return head;

    if (head == NULL)
        return NULL;

    int counter = 0;
    ListNode *fastPtr = head;
    ListNode *currentPtr = head;
    ListNode *nextPtr = NULL;
    ListNode *prevPtr = NULL;
        
    while (fastPtr)
    {
        counter++;

        //one chain formed from list, reverse it
        if (counter == k)
        {
            fastPtr = fastPtr->next;
            
            while (counter)
            {
                nextPtr = currentPtr->next;
                currentPtr->next = prevPtr;
                prevPtr = currentPtr;
                currentPtr = nextPtr;
                counter--;
            }

            //last node
            if (currentPtr->next == NULL)
            {
                currentPtr->next = prevPtr;
                prevPtr = currentPtr;
                break;
            }
        }
        else
        {
            fastPtr = fastPtr->next;
        }
    }
    
    return prevPtr;
}

【问题讨论】:

  • 您是否尝试过调试您的程序?或者尝试过用纸笔绘制和重绘列表以及对列表执行的操作?
  • "现在他说要反转K节点组中的列表。例如,如果列表是[1,2,3,4,5,6]并且K=4那么o/p = [4,3,2,1,5,6]。”所以他想让你把前K个节点倒过来,让下一个节点按顺序排列? “在 K 节点组中”听起来像 [1,2,3,4,5,6,7,8,9,10,11,12] 会变成 [9,10,11,12,6,7,8 ,9,1,2,3,4].
  • 对我来说,不清楚你想要什么。您只是想反转前 k 个元素吗?
  • @4386427 对每组K个元素,他要反转组,如果最后一组少于K个元素他不会反转。
  • @user2520119 你到底做了什么来从反转列表转变为按组反转它?

标签: c algorithm linked-list reverse singly-linked-list


【解决方案1】:

您的方法还可以,但是您没有正确链接反向列表片段:

  • 您可以分解fastPtr = fastPtr->next;,因为它在if 语句的两个分支中执行。

  • 最后一个节点的测试是假的:如果列表长度是k 的倍数,if (currentPtr->next == NULL) 将取消引用空指针。

  • 您应该保留一个指向存储反向列表片段头部的位置的指针。循环开始时这个指针指向变量head,每次分片反转后,应该指向反转前分片第一个节点的next成员,也就是CurrentNode的值在反向循环的开始。

通过这些小的修改,您的代码运行良好。

请注意,这个问题很棘手。如果面试官要求您以交互方式解决问题,他们可能对您解决问题的方法感兴趣。在不到半小时的时间内构建一个有效而优雅的解决方案将是非常好的。

这是带有测试平台的修改版本:

#include <stdio.h>

typedef struct ListNode {
    struct ListNode *next;
    int data;
} ListNode;

ListNode *reverseKGroup(ListNode *head, int k) {
    if (k > 1) {                     // no need to test for `head != NULL`
        int counter = 0;
        ListNode **start = &head;    // place where to store the head of the reversed fragment
        ListNode *currentPtr = head; // pointer to the first node of the fragment
        ListNode *fastPtr = head;    // pointer to the node after the end of the fragment

        while (fastPtr) {
            fastPtr = fastPtr->next;
            counter++;
            if (counter == k) {
                // k nodes between CurrentPtr and fastPtr: reverse the fragment
                ListNode *lastPtr = currentPtr;
                ListNode *prevPtr = fastPtr;
                while (counter) {
                    ListNode *nextPtr = currentPtr->next;
                    currentPtr->next = prevPtr;
                    prevPtr = currentPtr;
                    currentPtr = nextPtr;
                    counter--;
                }
                *start = prevPtr;
                start = &lastPtr->next;
            }
        }
    }
    return head;
}

void printList(const char *prefix, const ListNode *p, const char *suffix) {
    while (p) {
        printf("%s%d", prefix, p->data);
        prefix = ", ";
        p = p->next;
    }
    printf("%s", suffix);
}

ListNode *test(ListNode *list, int k) {
    printf("reverseKGroup(%d): ", k);
    list = reverseKGroup(list, k);
    printList("", list, "\n");
    return reverseKGroup(list, k); // undo k-reversal
}

int main() {
    ListNode a[10], *list = a;
    for (int i = 0; i < 10; i++) {
        a[i].next = i + 1 < 10 ? &a[i + 1] : NULL;
        a[i].data = i + 1;
    }
    for (int k = 0; k <= 11; k++) {
        list = test(list, k);
    }
    return 0;
}

输出:

reverseKGroup(0): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 reverseKGroup(1): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 reverseKGroup(2): 2, 1, 4, 3, 6, 5, 8, 7, 10, 9 reverseKGroup(3): 3, 2, 1, 6, 5, 4, 9, 8, 7, 10 reverseKGroup(4): 4, 3, 2, 1, 8, 7, 6, 5, 9, 10 reverseKGroup(5): 5, 4, 3, 2, 1, 10, 9, 8, 7, 6 reverseKGroup(6): 6, 5, 4, 3, 2, 1, 7, 8, 9, 10 reverseKGroup(7): 7, 6, 5, 4, 3, 2, 1, 8, 9, 10 reverseKGroup(8): 8, 7, 6, 5, 4, 3, 2, 1, 9, 10 reverseKGroup(9): 9, 8, 7, 6, 5, 4, 3, 2, 1, 10 reverseKGroup(10): 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 reverseKGroup(11): 1, 2, 3, 4, 5, 6, 7, 8, 9, 10

【讨论】:

    【解决方案2】:

    最初,面试官问的问题是反向链接 我很容易解决的清单。现在他说要在组中反转列表 K 个节点。

    首先,不要在面试中做任何测试作业。不要让别人操纵你和你的时间。忽略这些提供测试任务的公司。

    对于任务,例如,使用递归函数不是一个好主意,因为对于大列表,您可能会遇到堆栈溢出。

    另外引入一种结构来声明列表本身会更好。

    我可以建议以下解决方案。

    #include <stdio.h>
    #include <stdlib.h>
    
    struct Node
    {
        int data;
        struct Node *next;
    };
    
    struct List
    {
        struct Node *head;
    };
    
    int push_front( struct List *list, int data )
    {
        struct Node *new_node = malloc( sizeof( struct Node ) );
        int success = new_node != NULL;
        
        if ( success )
        {
            new_node->data = data;
            new_node->next = list->head;
            list->head = new_node;
        }
        
        return success;
    }
    
    void clear( struct List *list )
    {
        while ( list->head != NULL )
        {
            struct Node *tmp = list->head;
            list->head = list->head->next;
            free( tmp );
        }
    }
    
    FILE * display( const struct List *list, FILE *fp )
    {
        for ( const struct Node *current = list->head; current != NULL; current = current->next )
        {
            fprintf( fp, "%d -> ", current->data );
        }
        
        fputs( "null", fp );
        
        return fp;
    }
    
    void reverse_n( struct List *list, size_t n )
    {
        if ( !( n < 2 ) )
        {
            for( struct Node **first = &list->head, *last = list->head; last != NULL; )
            {
                for ( size_t i = n; --i != 0 && last; )
                {
                    last = last->next;
                }
                
                if ( last != NULL )
                {
                    struct Node *next = ( *first )->next;
                    ( *first )->next = last->next;
                    last = *first;
    
                    for ( size_t i = n; --i != 0; )
                    {
                        struct Node *tmp = next;
                        next = next->next;
                        tmp->next = *first;
                        *first = tmp;
                    }
                    
                    first = &last->next;
                    last  = last->next;
                }
            }
        }
    }
    
    int main(void) 
    {
        struct List list = { .head = NULL };
        const int N = 10;
        
        for ( size_t i = 2; i < N + 1; i++ )
        {
            for ( int j = N; j != 0; --j )
            {
                push_front( &list, j - 1 );
            }
        
            putc( '\n', display( &list, stdout ) );
        
            reverse_n( &list, i );
        
            putc( '\n', display( &list, stdout ) );
            
            putc( '\n', stdout );
            
            clear( &list );
        }
    
        return 0;
    }
    

    程序输出是

    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    1 -> 0 -> 3 -> 2 -> 5 -> 4 -> 7 -> 6 -> 9 -> 8 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    2 -> 1 -> 0 -> 5 -> 4 -> 3 -> 8 -> 7 -> 6 -> 9 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    3 -> 2 -> 1 -> 0 -> 7 -> 6 -> 5 -> 4 -> 8 -> 9 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    4 -> 3 -> 2 -> 1 -> 0 -> 9 -> 8 -> 7 -> 6 -> 5 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 6 -> 7 -> 8 -> 9 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 7 -> 8 -> 9 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 8 -> 9 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> 9 -> null
    
    0 -> 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> null
    9 -> 8 -> 7 -> 6 -> 5 -> 4 -> 3 -> 2 -> 1 -> 0 -> null
    

    【讨论】:

      【解决方案3】:

      > 它将整个列表的输出反转....

      这是因为当下次控件进入这个if块时

      if(counter == k)
      

      指针prevPtr 仍然指向列表中的相同节点,它在之前的迭代中操作列表时所指向的节点。您需要在反转一组k 节点时将prevPtr 设置为NULL。除此之外,您还需要明确地处理列表的headtail。列表的head 将是第一组k 节点的kth 节点,而列表的tail 将是最后一组k 节点的第一个节点当列表中的节点是k 的倍数时,或者当列表中的节点不是k 的倍数时,它将指向剩余列表的第一个节点。在反转每组k节点时,需要注意列表的tail

      根据上述细节粗略修改您的代码:

      ListNode* reverseKGroup(ListNode* head, int k) 
      {
          if(k==0 || k==1)
              return head;
      
          if(head == NULL)
              return NULL;
      
          int counter = 0;
          ListNode* fastPtr = head;
          ListNode* currentPtr = head;
          ListNode* nextPtr = NULL;
          ListNode* prevPtr = NULL;
          ListNode* tail = NULL;
          ListNode* currLast = NULL;
          int set_head = 0;
          int set_currLast = 0;
              
          while(fastPtr)
          {
              counter++;
      
              //one chain formed from list, reveser it
              if(counter == k)
              {
                  fastPtr = fastPtr->next;
      
                  prevPtr = NULL;
                  set_currLast = 0;
                  while(counter)
                  {
                      nextPtr = currentPtr->next;
                      currentPtr->next = prevPtr;
      
                      // when reversing group of k nodes, the first node will be
                      // the last when whole group reversed
                      if (!set_currLast) 
                      {
                         currLast = currentPtr;
                         set_currLast = 1;
                      }
                      prevPtr = currentPtr;
                      currentPtr = nextPtr;
                      counter--;
                  }
      
                  // Need to set head just once only
                  if (!set_head) {
                     tail = head;
                     head = prevPtr;
                     set_head = 1;
                  } else {
                     // the tail need to set after reversing every k nodes group
                     tail->next = prevPtr;
                     tail = currLast;
                  }
                  // For the last group which will be of size less than k
                  tail->next = nextPtr;
              }
              else
              {
                  fastPtr = fastPtr->next;
              }
          }
      
          return head;
      }
      

      您可以使用递归来反转列表中k 节点组中的节点。代码看起来很干净,更容易理解:

      struct ListNode * revll(struct ListNode *head, int k) {
              struct ListNode * prev = NULL;
              struct ListNode * curr = head;
              struct ListNode * tmp = NULL;
              int count = k;
      
              tmp = head;
              while (tmp && count) {
                      count--;
                      tmp = tmp->next;
              }
              if (count != 0) {
                      return head;
              }
      
              while ((curr != NULL) && (count < k)) {
                      tmp = curr->next;
                      curr->next = prev;
                      prev = curr;
                      curr = tmp;
                      count++;
              }
      
              if (tmp) {
                      head->next = revll(tmp, k);
              }
              return prev;
      }
      

      【讨论】:

      • 您应该警告递归函数不是尾递归的,因此它可能会在很长的列表和较小的k 值上导致堆栈溢出
      猜你喜欢
      • 2011-10-31
      • 1970-01-01
      • 1970-01-01
      • 2016-07-09
      • 2018-07-28
      • 2020-04-08
      • 1970-01-01
      • 2016-08-08
      相关资源
      最近更新 更多