面試題—鏈表的‘部分’翻轉

問題:

         給出一個單鏈表(不帶頭節點)和一個數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結束。


■下面是‘摘節點頭插’的簡單圖示:


650) this.width=650;" width="395" height="256" title="無標題.png" style="width:395px;height:178px;" src="http://s4.51cto.com/wyfs02/M01/80/85/wKiom1dDu22j--UMAAAOqK4yEFE776.png" alt="wKiom1dDu22j--UMAAAOqK4yEFE776.png" />


下面是詳細的實現:

//鏈表節點的結構
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用來標識翻轉的是第幾部分。下面是‘部分’翻轉的簡單圖示:


650) this.width=650;" width="527" height="262" title="無標題.png" style="width:527px;height:229px;" src="http://s4.51cto.com/wyfs02/M00/80/87/wKiom1dDxTWTny68AAAksTjP87k777.png" alt="wKiom1dDxTWTny68AAAksTjP87k777.png" />


下面是‘部分’翻轉的實現代碼:

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




本文出自 “無心的執着” 博客,請務必保留此出處http://10740590.blog.51cto.com/10730590/1782421

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章