輸入一個鏈表,從尾到頭打印鏈表每個節點的值。
輸入描述:輸入為鏈表的表頭
輸出描述:輸出為需要打印的“新鏈表”的表頭
一、問題分析
初拿到這個題目時,這應該是考察單向鏈表這一數據結構。單向鏈表的遍歷總是從頭指針逐項遍歷各個節點,現在要求從尾到頭打印節點的值,我們可以在遍歷時把各節點壓入棧內,最后出棧打印各個節點值,即可達到要求。
實現之前,我們先來看看如何創建一個鏈表。
1,鏈表節點的數據結構定義
1 struct ListNode {
2 int val; 3 struct ListNode *next; 4 ListNode(int x) : 5 val(x), next(NULL) { 6 } 7 };
在鏈表的定義中,包含自己的整形成員(當然也可以定義為其它數據類型,如double),以及下一個節點的位置信息(next)。這里我們還定義了節點的構造函數(ListNode),方便節點的初始化。
2,鏈表的創建
這里我們考慮單向鏈表。如果通過用戶輸入來創建鏈表,我們可以進行如下操作:
1)創建頭節點head,指向NULL。因為此時沒有任何節點
2)為加入的節點分配空間,賦初值(如1中考慮的int類型)並指向NULL。判斷head==NULL?head指向該節點:“上一節點”指向該節點
3)更新新加入的節點為“上一節點”
4)判斷節點是否添加結束,若否,重復2,3,4步驟繼續添加節點。
二、問題的解決思路
1,利用stack反轉輸出鏈表節點值

1 #include <iostream> 2 #include <vector> 3 #include <stack> 4 using namespace std; 5 6 //鏈表節點定義 7 struct ListNode { 8 int val; 9 struct ListNode *next; 10 ListNode(int x) : 11 val(x), next(NULL) { 12 } 13 }; 14 15 //鏈表從尾到頭輸出節點值方法實現 16 class Solution { 17 public: 18 //方法1,通過stack 這個container來實現反轉鏈表 19 vector<int> printListFromTailToHead(struct ListNode* head) { 20 vector<int> result; 21 if(head == NULL) 22 return result; 23 stack<ListNode*> reverse; 24 ListNode* node = head; 25 while(node != NULL) { 26 reverse.push(node); 27 node = node->next; 28 } 29 while(!reverse.empty()) { 30 node = reverse.top(); 31 result.push_back(node->val); 32 reverse.pop(); 33 } 34 return result; 35 } 36 //方法2,原地反轉鏈表,不介入其它container 37 //不斷的使“下一個節點”指向“前一個”節點 38 vector<int> printListFromTailToHead2(struct ListNode* head) { 39 vector<int> vec; 40 ListNode *buf = head; 41 ListNode *pre = buf; 42 if(head == NULL) 43 return vec; 44 while(head->next != NULL){ 45 buf = head->next; 46 head->next = buf->next; 47 buf->next = pre; 48 pre = buf; 49 } 50 while(buf){ 51 vec.push_back(buf->val); 52 buf = buf->next; 53 } 54 return vec; 55 } 56 }; 57 58 struct ListNode* CreateListNode(struct ListNode* head) { 59 struct ListNode *p1, *p2; 60 int i = 1; 61 p1 = p2 = (struct ListNode*)malloc(sizeof(ListNode)); 62 cout << "Please input the 1st node, it's address is p1_addr = " << p1 << endl; 63 cout << "And input -1 to quit."; 64 cin >> (p1->val); 65 p1->next = NULL; 66 67 while (p1->val != -1) { 68 if (NULL == head) { 69 head = p1; 70 } 71 else 72 p2->next = p1; 73 p2 = p1; 74 p1 = (struct ListNode*)malloc(sizeof(ListNode)); 75 ++i; 76 cout << "Please input the " << i << " node," << "it's address is p" << i << "_addr = " << p1 <<endl; 77 cin >> (p1->val); 78 } 79 free(p1); 80 p2->next = NULL; 81 p1 = NULL; 82 cout << "End of creating ListNode." << endl; 83 return head; 84 } 85 86 int main () { 87 std::vector<int> v; 88 struct ListNode* head = NULL; 89 head = CreateListNode(head); 90 91 Solution s; 92 v = s.printListFromTailToHead2(head); 93 for (int var : v) { 94 cout << var << ' '; 95 } 96 97 //測試節點的初始化,與本題無關 98 struct ListNode myListNode(100); 99 cout << myListNode.val << endl; 100 101 }
上述程序在gcc version 6.1.0下編譯通過。
在上面的程序中,實現反轉的方法有兩個:
1)利用stack先入后出的特性實現鏈表的反轉
2)不借用其它container,不斷的使“后一個”節點指向“前一個節點”來原地反轉鏈表。下面來談談原地反轉鏈表
2,原地反轉鏈表
我們看函數printListFromTailToHead2()。首先,我們先定義兩個指針(buf, pre),並與head共同指向鏈表的頭節點。然后,通過head->next遍歷原始鏈表,並同時更新buf指向已經遍歷到的節點(buf = head->next),而這期間head始終指向頭節點。然后,更新head—>next指向buf的下一個節點(head->next = buf->next)。接着,通過buf->next = pre, 使buf指向前一個節點,緊接着更新pre,使其指向當前節點,以便下一輪更新。
當head->next遍歷完鏈表后,buf指向了原始鏈表最后一個節點,head->next的值變為了NULL。現在從buf開始遍歷,即可反向遍歷鏈表。