面試知識點--鏈表如何逆轉

這個問題應該是面試最常見的題型之一了,很多大公司都會經常提問,如阿里巴巴。它的原理簡單,學過數據結構,C語言,熟悉鏈表的同學應該很容易就掌握。但如果不熟練的話,也容易把代碼寫複雜了,或者寫不出來。
下面我帶大家分析一下這個算法。

先定義一個鏈表節點,

struct ListNode
{

int val;
ListNode *next;
ListNode(int v) : val(v), next(NULL) {}
};

這個節點結構很簡單,有一個整數值,一個指向下個節點的指針。ListNode(int v) : val(v), next(NULL) {}是C/C++常用的初始化方法,這個可以看做是ListNode結構的構造函數,val(v)是對val初始化,next(NULL)對next進行初始化,這種初始化速度比較快。

然後創建一個0,1,2,3共4個元素的鏈表。

ListNode* createList()
{
ListNode *root = new ListNode(0);
ListNode *p1 = new ListNode(1);
ListNode *p2 = new ListNode(2);
ListNode *p3 = new ListNode(3);
root->next = p1;
p1->next = p2;
p2->next = p3;
return root;
}

我們採用new來創建,使用C++來實現。創建完成後,生成一個單鏈表,共有4個元素。

然後,以下是核心的逆轉算法函數,我們會詳細的進行分析。

ListNode* reverseList(ListNode *root)
{
//如果鏈表爲空,直接返回null
if (root == NULL)
return root;
//定義cur指針,指向當前列表的當前元素
ListNode *cur = root;
//定義pre指針,指向前一個元素
ListNode *pre = NULL;
//定義post指針,指向後一個元素
ListNode *post = NULL;
//鏈表逆轉後,指向逆轉鏈表的首元素,或者表示新的逆轉鏈表頭
ListNode *revRoot = NULL;
//只要當前指向不爲null,就一直循環
while (cur != NULL)
{
//將當前元素的指向位置賦值給post,即讓post指向當前元素的下一個元素
post = cur->next;
//如果post爲null,說明已經到了鏈表的末尾,則讓revRoot指向當前元素
if (post == NULL)
revRoot = cur;
//將當前元素的前一個元素的位置賦值給cur,即讓當前元素指向前一個元素,而不再指向後一個元素
cur->next = pre;
//將前一個元素的指向賦值成當前指向,即pre指向當前元素
pre = cur;
//同上,cur指向後一個元素
cur = post;
}
return revRoot;
}

我們看一下這個循環的執行過程,爲方便理解,我這裏畫了一下循環執行圖,

1、初始狀態

這裏寫圖片描述

2、第一次循環執行後

這裏寫圖片描述

3、第二次循環執行後

這裏寫圖片描述

4、第三次循環執行後

這裏寫圖片描述
5、第四次循環執行後

這裏寫圖片描述

6、最終狀態

這裏寫圖片描述

然後我們寫一個main函數來測試一下。

int main()
{
ListNode *root = createList();
cout << "init List is: ";
showList(root);
root = reverseList(root);
cout << "reverse List is: ";
showList(root);
deleteList(root);
}

顯示函數:

void showList(ListNode* root)
{
while(root != NULL)
{
cout << root->val << " ";
root = root->next;
}
cout << endl;
}

刪除函數:

void deleteList(ListNode *root)
{
ListNode *p = root;
while(root != NULL)
{
p = root->next;
delete(root);
root = p;
}
}

我們看到執行結果如下所示,
init List is:
0 1 2 3
reverse List is:
3 2 1 0

以上就是鏈表逆轉的典型寫法,大家都明白了嗎?如有因爲歡迎留言交流。
另外, 鏈表還可以使用遞歸的方式實現,如下是遞歸實現的方法,有興趣的也可以研究一下它的實現邏輯。

//遞歸法實現鏈表的逆置

pNode reverseList_reverse(pNode head)
{
pNode current_head, head_next;
if (head == NULL)
return NULL;
if (head->next == NULL)//邊界條件
return head;
else{
current_head = head;//記下當前的頭結點a0
head_next = head->next;//記下當前頭結點後面的結點a1
head = reverseList_reverse(head_next);//返回(a1...an)逆轉後的頭結點
head_next->next = current_head;//用上面保存的地址(逆轉後的尾結點)指向原來的頭結點a0
current_head->next = NULL;//將a0的next域置零
}
return head;//返回a0
}

本文參考瞭如下文章鏈接:
https://www.cnblogs.com/kaituorensheng/archive/2014/01/18/3524888.html
http://blog.csdn.net/worldwindjp/article/details/18861093
對以上文章的作者表示感謝。

這裏寫圖片描述
本公衆號將以推送Android各種技術乾貨或碎片化知識,以及整理老司機日常工作中踩過的坑涉及到的經驗知識爲主,也會不定期將正在學習使用的新技術總結出來進行分享。每天一點乾貨小知識把你的碎片時間充分利用起來。

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