13.查找鏈表中倒數第k個結點

 題目:輸入一個單向鏈表,輸出該鏈表中倒數第k個結點。鏈表的倒數第0個結點爲鏈表的尾指針。

鏈表結點定義如下: struct ListNode
{
      int       m_nKey;
      ListNode* m_pNext;
};

分析:爲了得到倒數第k個結點,很自然的想法是先走到鏈表的尾端,再從尾端回溯k步。可是輸入的是單向鏈表,

只有從前往後的指針而沒有從後往前的指針。因此我們需要打開我們的思路。
既然不能從尾結點開始遍歷這個鏈表,我們還是把思路回到頭結點上來。假設整個鏈表有n個結點,那麼倒數第k

個結點是從頭結點開始的第n-k-1個結點(從0開始計數)。如果我們能夠得到鏈表中結點的個數n,那我們只要從

頭結點開始往後走n-k-1步就可以了。如何得到結點數n?這個不難,只需要從頭開始遍歷鏈表,每經過一個結點,

計數器加一就行了。
這種思路的時間複雜度是O(n),但需要遍歷鏈表兩次。第一次得到鏈表中結點個數n,第二次得到從頭結點開始的

第n­-k-1個結點即倒數第k個結點。
如果鏈表的結點數不多,這是一種很好的方法。但如果輸入的鏈表的結點個數很多,有可能不能一次性把整個鏈表

都從硬盤讀入物理內存,那麼遍歷兩遍意味着一個結點需要兩次從硬盤讀入到物理內存。我們知道把數據從硬盤讀

入到內存是非常耗時間的操作。我們能不能把鏈表遍歷的次數減少到1?如果可以,將能有效地提高代碼執行的時間

效率。
如果我們在遍歷時維持兩個指針,第一個指針從鏈表的頭指針開始遍歷,在第k-1步之前,第二個指針保持不動;在

第k-1步開始,第二個指針也開始從鏈表的頭指針開始遍歷。由於兩個指針的距離保持在k-1,當第一個(走在前面的)

指針到達鏈表的尾結點時,第二個指針(走在後面的)指針正好是倒數第k個結點。
這種思路只需要遍歷鏈表一次。對於很長的鏈表,只需要把每個結點從硬盤導入到內存一次。因此這一方法的時間效率

前面的方法要高。

#include<iostream>

using namespace std;

struct ListNode
{
 int data;
 ListNode *next;

};
ListNode *p=NULL,*q=NULL;
ListNode * find(ListNode *head,int k)
{
 if(head==NULL)
  return NULL;

  
 
 p=q=head;
 for(int i=1;i<=k;i++)
 {
  if(p->next!=NULL)
   p=p->next;
  else
   return NULL;


 }
 
 while(p!=NULL)
 {
  p=p->next;
  q=q->next;
 }

 return q;
}

ListNode * insert(ListNode * & head,int i)
{

 if(head==NULL)
 {
  ListNode *q=new ListNode();
  q->data=i;
  q->next=NULL;
  head=q;
 }
 else
 {
  ListNode *q=new ListNode();
  q->data=i;
  q->next=head;
  head=q;
 }
 return head;
}

int main()
{
 ListNode *head=NULL;
 insert(head,1);
 insert(head,2);
 insert(head,3);
 insert(head,4);
 insert(head,5);
 insert(head,6);
 insert(head,7);
 insert(head,8);
 insert(head,9);
 insert(head,10);
 
 cout<<find(head,2)->data<<endl;

}




 

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