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) {List13 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 };
注意:
「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