1. 程式人生 > >06 從尾到頭列印新連結串列

06 從尾到頭列印新連結串列

題目描述:

輸入一個連結串列,按連結串列值從尾到頭的順序返回一個ArrayList。

 

解題思路:

 1)使用list容器:順序訪問連結串列,使用push_front()將元素插入list容器的前方。

 2)使用棧

 3)使用遞迴函式:遞迴在本質上是一個棧結構

 4)用反向迭代器(object.rbegin()object.rend()

 5)頭插法(複雜度高)

 

測試用例:

 1)功能測試:(輸入的連結串列有多個節點;只有一個節點)

 2)特殊輸入測試:(輸入的連結串列頭節點指標為nullptr)

 

程式碼:


1)使用list容器:

 1 /**
 2 *  struct ListNode {
 3 *        int val;
 4 *        struct ListNode *next;
 5 *        ListNode(int x) :
 6 *              val(x), next(NULL) {
 7 *        }
 8 *  };
 9 */
10 class Solution {
11 public:
12     vector<int> printListFromTailToHead(ListNode* head) {
13 if (head==NULL) 14 return vector<int>{}; //error: return 0;nullptr;vector<int>{0}; 15 vector<int> res; 16 list<int> bactToFront; 17 ListNode* currentNode = head; 18 while (currentNode!=NULL){ //error : currentNode->next 19 bactToFront.push_front(currentNode->val);
20 currentNode = currentNode->next; 21 } 22 //遍歷bactToFront,按順序複製給vector 23 //for(int i=0;i<bactToFront.size();i++) 24 //res.push_back(bactToFront[i]); // bactToFront[i]: type 'list' does not provide a subscript operator 25 for(auto iter = bactToFront.begin();iter!=bactToFront.end();iter++)//list<int>::iterator 26 res.push_back(*iter); 27 return res; 28 } 29 };
List

注意:

「1」連結串列為空<=>頭指標為空,即判斷head==NULL。而不是head->next==NULL

「2」當連結串列為空的時候,不用處理任何步驟,直接返回即可。由於返回值是vector<int>值。因此return vector<int>{};

          此處,返回0;nullptr;vector<int>{0};vector<int>;都是錯的。

「3」判斷連結串列是否結束:當前指標域不為空,即:currentNode!=NULL(證明指向下一個節點)

          而不是currentNode->next!=NULL(說明當前指標的下一個節點有後繼節點,此種情況最後一個節點訪問不到)

「4」遍歷list容器應該使用迭代器,list不支援下標訪問


2)使用棧:

 1 /**
 2 *  struct ListNode {
 3 *        int val;
 4 *        struct ListNode *next;
 5 *        ListNode(int x) :
 6 *              val(x), next(NULL) {
 7 *        }
 8 *  };
 9 */
10 class Solution {
11 public:
12     vector<int> printListFromTailToHead(ListNode* head) {
13         if (head==NULL)
14             return vector<int>{};  //error: return 0;nullptr;vector<int>{0};
15         vector<int> res;
16         stack<int> frontToBack;
17         ListNode* currrent = head;
18         while(currrent!=NULL){
19             frontToBack.push(currrent->val); //stack 儲存使用函式push()
20             currrent = currrent->next;
21         }
22         int len = frontToBack.size();
23         for(int i = 0;i<len;i++){ //
24             res.push_back(frontToBack.top()); //讀取棧頂元素
25             frontToBack.pop(); //刪除棧頂元素
26         }
27         return res;
28     }
29 };
stack

注意:

「1」Stack(棧)是一種後進先出的資料結構,也就是LIFO(last in first out) ,最後加入棧的元素將最先被取出來,在棧的同一端進行資料的插入與取出,這一段叫做“棧頂”。

「2」stack 儲存使用函式push() (將元素加入棧中,沒有返回值)

「3」size()函式返回棧的大小

「4」empty()函式返回一個bool值,棧為空時返回true,否則返回false

「5」top()函式的返回值是棧頂元素(注意並沒有刪掉棧頂元素),即讀取棧頂元素

「6」pop()函式將棧頂元素刪掉,沒有返回值

「7」swap()函式可以交換兩個棧的元素

「8」emplace()函式可以將一個元素加入棧中,與push的區別在於:

         · stack<Node> mystack;

         · mystack.emplace(1,2);

         · mystack.push(Node(1,2));

         emplace可以直接傳入Node的建構函式的引數,push需要手動構造

「9」錯誤程式碼:

1 for(int i = 0;i<frontToBack.size();i++){ //
2      res.push_back(frontToBack.top()); //讀取棧頂元素
3      frontToBack.pop(); //刪除棧頂元素
4 }

line 1的frontToBack.size()一直在改變(因為每次迴圈都會刪除一個元素)

修改如下:

1 int len = frontToBack.size();
2 for(int i = 0;i<len;i++){ //
3      res.push_back(frontToBack.top()); //讀取棧頂元素
4      frontToBack.pop(); //刪除棧頂元素
5  }

「10」使用while讀取stack

1 while(!stack.empty()) {              

2     res.push_back(frontToBack.top());

3     frontToBack.pop();               

4 }                                    


 3)使用遞迴函式:

 1 class Solution {
 2  public:
 3   vector<int> dev;
 4   vector<int>& printListFromTailToHead(ListNode* head) {
 5     if(head!=NULL) {
 6       if(head->next!=NULL) {
 7         dev=printListFromTailToHead(head->next);
 8       }
 9       dev.push_back(head->val);
10     }
11     return dev;
12   }
13 };
recursion

注意:

「1」基於遞迴的程式碼看起來很簡潔,但是有一個問題:當連結串列非常長的時候,就會導致函式呼叫的層級很深,從而有可能導致函式呼叫棧溢位。

「2」推薦使用棧結構(stack)


 4)用反向迭代器 :

 1 class Solution {
 2 public:
 3     vector<int> printListFromTailToHead(ListNode* head) {
 4         if (head==NULL)
 5             return vector<int>{};  //error: return 0;nullptr;vector<int>{0};
 6         vector<int> res;
 7         ListNode* currrent = head;
 8         while(currrent!=NULL){
 9             res.push_back(currrent->val); //stack 儲存使用函式push()
10             currrent = currrent->next;
11         }
12         return vector<int>(res.rbegin(),res.rend()); //返回一個臨時物件
13     }
14 };
rbegin/rend

注意:

「1」獲取迭代器:c.begin()  c.end()   返回指向c的首元素和尾元素之後位置的迭代器  (end並不是返回尾元素)

                                c.cbegin()  c.cend()   返回const_iterator

「2」反向容器的額外成員(不支援forward_list):

    reverse_iterator  按逆序定址元素的迭代器

    const_reverse_iterator  不能修改元素的逆序迭代器

          c.rbegin()  c.rend()  返回指向c的尾元素首元素之前的位置的迭代器

    c.crbegin()  c.crend()  返回const_reverse_iterator


5)頭插法(複雜度高)

 1 class Solution {
 2 public:
 3 vector<int> printListFromTailToHead(ListNode* head) {
 4         vector<int> v;
 5         while(head != NULL)
 6         {
 7             v.insert(v.begin(),head->val);
 8             head = head->next;
 9         }
10         return v;
11     }
12 };
head_insert

注意:
 v.insert(v.begin(),head->val); 等價於 v.push_front(head->val);
每次插入相當於把當前陣列元素全部向後移動一個位置,再插入當前元素,這一塊的時間複雜度就是O(n^2),效率低。


 

 

基礎知識:

連結串列結構,基礎 推薦部落格:

https://www.cnblogs.com/byonecry/p/4458821.html

https://i.cnblogs.com/EditPosts.aspx?postid=9966012&update=1