題目:輸入一個單向鏈表,輸出該鏈表中倒數第
k
個結點。鏈表的倒數第
0
個結點為鏈表的尾指標。
分析:為了得到倒數第
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 <stdio.h><br />#include <stdlib.h><br />typedef struct list{<br /> int num;<br /> struct list *next;<br />}LIST;<br />LIST *create_list(int n){<br /> LIST *head=NULL;<br /> LIST *tmp=NULL;<br /> LIST *last=NULL;<br /> int i;<br /> for(i=1;i<=n;i++){<br /> tmp=(LIST *)malloc(sizeof(LIST));<br /> tmp->num=i;<br /> if(1==i)<br /> head=tmp;<br /> else<br /> last->next=tmp;<br /> tmp->next=NULL;<br /> last=tmp;<br /> }<br /> return head;<br />}<br />void print_list(LIST *head){<br /> if(NULL==head){<br /> printf("list is null./n");<br /> return;<br /> }<br /> LIST *p=head;<br /> while(p!=NULL){<br /> printf("list num is %d/n",p->num);<br /> p=p->next;<br /> }<br />}<br />LIST *find_last_k_node(LIST *head,int k){<br /> if(NULL==head)<br /> {<br /> printf("list is null./n");<br /> return NULL;<br /> }<br /> LIST *p=head->next;<br /> LIST *q=head->next;<br /> int i;<br /> for(i=0;i<k;i++)<br /> {<br /> if(NULL==q)<br /> {<br /> printf("list is too short./n");<br /> return NULL;<br /> }<br /> q=q->next;<br /> }<br /> while(q!=NULL){<br /> q=q->next;<br /> p=p->next;<br /> }<br /> return p;<br />}<br />int release_list(LIST *head){<br /> if(NULL==head)<br /> {<br /> printf("head is null./n");<br /> return 0;<br /> }<br /> LIST *tmp=head;<br /> LIST *l=head;<br /> while(l!=NULL){<br /> tmp=l;<br /> l=l->next;<br /> free(tmp);<br /> }<br /> head=NULL;<br /> return 0;<br />}<br />int main(){<br /> LIST *head=NULL;<br /> LIST *pos=NULL;<br /> int n=5;<br /> int k=3;<br /> head=create_list(n);<br /> print_list(head);<br /> printf("******************************/n");<br /> pos=find_last_k_node(head,k);<br /> print_list(pos);<br /> release_list(head);<br /> return 0;<br />}
運行結果:
[root@localhost list]# ./a.out
list num is 1
list num is 2
list num is 3
list num is 4
list num is 5
******************************
list num is 3
list num is 4
list num is 5