问题:

         给出一个单链表(不带头节点)和一个数K,请翻转此单链表?


例如:1->2->3->4->5       k = 0;   翻转过后:1->2->3->4->5

          1->2->3->4->5       K = 2;   翻转过后:2->1->4->3->5

          1->2->3->4->5       K = 10; 翻转过后:5->4->3->2->1


        在讨论链表的‘部分’翻转问题之前,先回顾一下链表的逆置问题,如何将一个链表进行逆置?我们以前采用的方式是‘摘节点头插’的方法。假设右链表1->2->3->4->5,新建一个节点指针newHead=NULL,用cur指针指向链表的头节点,将摘下的节点用tmp进行保存,然后将tmp和newHead进行连接,将newHead指向tmp的位置上,如此循环直到cur为NULL结束。


■下面是‘摘节点头插’的简单图示:


面试题—链表的‘部分’翻转_单链表


下面是详细的实现:

//链表节点的结构
struct Node
{
     int data;
     Node* _next;
};

void Reverse(Node* list)
{
     if (list == NULL)
     {
          return;
     }
     Node* cur = list;
     Node* newHead = NULL;
     while (cur)
     {
          Node* tmp = cur;
          cur = cur->_next;
          tmp->_next = newHead;
          newHead = tmp;
     }
}

       

        通过上面的解释,读者应该对链表的逆置应该不会陌生了,下面就还看一下链表的‘部分’翻转的问题:

        链表的‘部分’翻转,它和链表的逆置还是不一样的,对于给定的K,其实就是没此逆置K个节点,然后连接上后面翻转过后的K个节点,若最后剩余的不足K个节点,同样也对其进行翻转,然后进行连接。所以我们可以借助一下‘摘节点头插’的方式,分部分进行‘摘节点头插’。


        假设1->2->3->4->5,k = 2; 即就是将1和2进行翻转,3和4进行翻转,5进行翻转,然后将1和2翻转后的结果与3和4翻转后的结果和5翻转后的结果进行连接,得到2->1->4->3->5.


        在完成代码之前,先考虑链表若为空的情况,针对这个问题,如果k =0/1时的情况,k若大于链表节点的总个数又是什么情况?当链表为空时,可以直接返回,或者k=0/1时,就不需要对其进行翻转,也可以直接进行返回。若K大于链表节点的总个数,即就是相当于对链表进行逆置。


        在完成链表‘部分’的翻转,需要sectionHead(指向‘部分’的头节点)、sectionTail(指向‘部分’的尾节点)、cur(指向整个链表)、newHead(指向翻转完成的链表头节点)、prevsectionTail(指向这一部分之前部分的尾节点)、tmp(用来做中间保存节点的指针)这几个节点指针,sectionNum用来标识翻转的是第几部分。下面是‘部分’翻转的简单图示:


面试题—链表的‘部分’翻转_逆置_02


下面是‘部分’翻转的实现代码:

Node* RolloverList(Node* list, int k)
{
     if (k <= 1 || list == NULL)
     {
          return list;
     }
     
     Node* cur = list;      //指向链表的头节点
     Node* newHead = NULL;      //指向逆置后的链表
     Node* sectionHead = NULL;     //指向需要逆置部分的头节点
     Node* sectionTail = NULL;      //指向需要逆置部分的尾节点
     Node* prevsectionTail = NULL;    //指向部分尾节点的指针
     int sectionNum = 0;   //用来标记链表翻转到第几部分
     
     while (cur)
     {
          int count = k;     //记录部分逆置的节点数
          prevsectionTail = sectionTail;
          sectionTail = cur;
          
          while (count-- && cur != NULL)     //使用的方法还是摘节点头插(进行部分逆置)
          {
               Node* tmp = cur;
               cur = cur->_next;
               tmp->_next = sectionHead;     //sectionHead相当于链表逆置中的newHead(先将部分进行逆置)
               sectionHead = tmp;
          }
          
          ++sectionNum;   //统计逆置了几部分
          //如果sectionNum为1时,逆置的部分为第一部分,就应该确定逆置后的链表的头节点
          if (sectionNum == 1)    
          {
               newHead = sectionHead;        
          }
          else    //证明逆置的为链表后面的部分,需要将后面的部分和前面的部分进行连接起来
          {
               prevsectionTail->_next = sectionHead;
          }
     }
     
     //出循环cur == NULL
     sectionTail->_next = NULL;
     return newHead;
}