反转链表中的每 k 个节点块

Posted

技术标签:

【中文标题】反转链表中的每 k 个节点块【英文标题】:Reverse every k block of nodes in a link list 【发布时间】:2015-07-09 21:44:46 【问题描述】:

我试图解决这个问题,即反转链接列表中的每 k 个节点块。我在每个外部 while 循环中处理 2*k 个元素。是否可以通过只处理每个外部 while 循环中的 k 个元素或不使用 hasknodes() 函数来完成?

样本输入:1->2->3->4->5 和 k = 3

样本输出:3->2->1->4->5

 struct node *rev(struct node *head,int k)
    
        if(k == 0 || k == 1) 
            return head;
        
        int i;
        struct node *prev,*temp,*curr,*newhead,*p,*thead;
        p = head;
        thead = head;
        newhead = NULL;
        while(p && hasknodes(p,k)) 
            prev = NULL;
            curr = p;
            i = 0;
            while(curr && i < k) 
                temp = curr->next;
                curr->next = prev;
                prev = curr;
                curr= temp;
                i++;
            
            if(newhead == NULL) 
                newhead = prev;
            
            else 
                thead->next = prev;
            
            p->next = curr;
            head = p;
            if(p) 
                p = p->next;
            
        
        if(newhead == NULL) 
            return head;
        
        return newhead;
    
//The function hasknodes(p,k) checks if there are k elements present from the current position p.

【问题讨论】:

如果可以撤消最后一段的反转,则不必知道剩余长度。 样本输出不应该是2-&gt;1-&gt;4-&gt;3-&gt;5吗? 其他限制?你能用一个向量作为临时的吗? @AlexandruBarbarosie 那错了。查看编辑。 @DOUGLASO.MOEN 我们不能使用向量 【参考方案1】:

其实你不需要调用函数hasknodes;而是开始拾取节点并以相反的顺序链接它们(就像您在内部 while 循环中所做的那样),如果您过早到达列表的末尾,则重新附加反向列表的元素。然而,这种方法的缺点是代码变得有点复杂。

正如第一个评论者所写:O(2*n) 实际上与 O(n) 相同,因为 O(n) 意味着您的问题可以在时间 与 n 成比例 的时间内解决。所以,你基本上已经完成了:-)

【讨论】:

我看到你提到了第一条评论,但没有提到你在回答中基本上重复的第二条评论 我猜重新添加会消耗额外的内存。 @shivam mitra:不,重新附加不会消耗额外的内存。这只是调整节点链接的问题。【参考方案2】:

将反转视为从一个列表弹出并推入另一个列表会很有帮助。您还需要知道 k 的前一个块的尾部,以便附加下一个。所以在伪代码中:

// let list be the head of the input list
prev_tail = sentinel; // fake previous tail node to append the first block
while list is not NULL 
  tail = list
  block = NULL
  for k times and while list isn't NULL
    push(block, pop(list))
  prev_tail->next = block
  prev_tail = tail;
return sentinel->next;

现在在 C 中,push 和 pop 以通常的方式实现:

typedef struct node_s  
  struct node_s *next;
  ...
 Node;

Node *reverse_blocks(Node *list, int k) 
  Node sentinel[1] = NULL;
  Node *prev_tail = sentinel;
  while (list) 
    Node *block = NULL;
    Node *tail = list;
    for (int i = 0; list && i < k; i++) 
      Node *pop = list;
      list = list->next;
      pop->next = block; // push(block, pop)
      block = pop;
    
    prev_tail->next = block;
    prev_tail = tail;
  
  return sentinel->next;

【讨论】:

【参考方案3】:

您可以为此编写一个简单的递归解决方案:

struct node *reverse (struct node *head, int k)

   struct node* current = head;
   struct node* next = NULL;
   struct node* prev = NULL;
   int count = 0;   

     /*reverse first k nodes of the linked list */
     while (current != NULL && count < k)
    
       next  = current->next;
       current->next = prev;
       prev = current;
       current = next;
       count++;
    

   /* next is now a pointer to (k+1)th node,Recursively call for the   
      list starting from current.And make rest of the list as next of first node */
   if(next !=  NULL)
    
       head->next = reverse(next, k); 
   

   /* prev is new head of the input list */
   return prev;

【讨论】:

以上是关于反转链表中的每 k 个节点块的主要内容,如果未能解决你的问题,请参考以下文章

链表小题(反转链表 求倒数第k个结点 删除链表指定结点)

剑指offer(十四,十五)链表中倒数第k个结点,反转链表

反转链表

JZ3.从尾到头打印链表;JZ14.链表中倒数第K个节点;JZ15.反转链表;JZ16.合并两个有序链表;JZ36.两个链表的第一个公共节点;JZ55.链表中环的入口节点;JZ56.删除链表中重复节点

JZ3.从尾到头打印链表;JZ14.链表中倒数第K个节点;JZ15.反转链表;JZ16.合并两个有序链表;JZ36.两个链表的第一个公共节点;JZ55.链表中环的入口节点;JZ56.删除链表中重复节点

24. 两两交换链表中的节点