鏈表面試題(一):反轉鏈表的算法實現


關於鏈表的考察

  鏈表是面試里面經常涉及到的考點,因為鏈表的結構相比於Hashmap、Hashtable、Concurrenthashmap或者圖等數據結構簡單許多,對於后者更多面試的側重點在於其底層實現。比如Hashmap中Entry<k,v>等操作、如何擴容、容量的設定等。鏈表的考察更側重於代碼的書寫和思路的形成。雖然說,鏈表的結構簡單,但是涉及到指針的操作,容易引申出一些挑戰性的考題,其中也牽涉到諸多小的細節的考慮,更能看出代碼書寫的能力和功底。

面試題:反轉鏈表

題目:定義一個函數,輸入一個鏈表的頭結點,反轉該鏈表並輸出反轉后鏈表的頭結點。

  下面給出了鏈表結點的定義:

1 struct ListNode {
2     int val;
3     struct ListNode *next;
4     ListNode(int x) :
5             val(x), next(NULL) {
6     }
7 };

分析:

  鏈表前后元素的關聯就是通過指針實現的,每個鏈表都有一個next指針指向下一個結點,末尾的節點的next域則置NULL;

  反轉鏈表就是要求修改指針的指向。下面的圖就是反轉前和反轉后的效果。

反轉前:

反轉后:

下面來談談如何對鏈表進行反轉。

  假設我們現在正在對結點v進行反轉操作,即原來結點u的next域指向v(圖中已經調整完畢,現在指向前一個結點),v的next域指向w。現在要做的是將v的next域指向u。從圖中我們可以看出,當把v的next指針指向u的同時,原先指向的w就已經無法被正常的訪問到了,為了避免“斷鏈”,我們必須在指針更改指向之前,保存修改結點的下一結點。同時我們也必須存儲上一個結點,因為next域即將修改指向該結點。因此定義三個指針,分別指向當前遍歷的結點,前一個結點和后一個結點。

算法實現如下:

 1 ListNode* ReverseList(ListNode* pHead)
 2 {
 3     ListNode* pReversedHead = NULL;
 4     ListNode* pNode = pHead;
 5     ListNode* pPrev = NULL;
 6     while(pNode != NULL)
 7     {
 8         ListNode* pNext = pNode->m_pNext;
 9 
10         if(pNext == NULL)
11             pReversedHead = pNode;
12 
13         pNode->m_pNext = pPrev;
14 
15         pPrev = pNode;
16         pNode = pNext;
17     }
18 
19     return pReversedHead;
20 }

  當然,上面的源碼中用到了四個指針,看完源碼就會發現和上面分析的原理並沒有相悖。或者下面這樣也是可以的,兩者的思路一致,沒有差別。只不過下面的代碼必須注意一點,跳出while循環的時候,最后一個結點的next域別忘記指向前一個結點,否則就會導致“斷鏈”。

 1     ListNode* ReverseList(ListNode* pHead) {
 2         ListNode *root=pHead; 
 3         ListNode *pre=NULL;  
 4         ListNode *next=NULL;
 5         if(pHead==NULL) return NULL; 
 6     while(root->next){  
 7         next=root->next;   
 8         root->next=pre;      
 9         pre=root;       
10         root=next;     
11     }    
12         root->next=pre; 
13         return root; 
14     } 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM