劍指offer——從尾到頭打印鏈表節點的值


輸入一個鏈表,從尾到頭打印鏈表每個節點的值。

輸入描述:輸入為鏈表的表頭

輸出描述:輸出為需要打印的“新鏈表”的表頭


 

一、問題分析


 

  初拿到這個題目時,這應該是考察單向鏈表這一數據結構。單向鏈表的遍歷總是從頭指針逐項遍歷各個節點,現在要求從尾到頭打印節點的值,我們可以在遍歷時把各節點壓入棧內,最后出棧打印各個節點值,即可達到要求。

  實現之前,我們先來看看如何創建一個鏈表。

  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 }
View Code

  上述程序在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開始遍歷,即可反向遍歷鏈表。


免責聲明!

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



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