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 };
注意:
「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 };
注意:
「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 };
注意:
「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 };
注意:
「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 };
注意:
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
prac 02
//如果需要修改原始数据,最好询问一下面试官。 通常打印是只读工作,不修改内容 //考虑使用栈实现 //递归的本质是栈结构,考虑使用递归实现 class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { stack<int> array; while (head != nullptr){ //null error array.push(head->val); head = head->next; } vector<int> ArrayList; while(!array.empty()){ ArrayList.push_back(array.top()); array.pop(); } return ArrayList; } };
//如果需要修改原始数据,最好询问一下面试官。 通常打印是只读工作,不修改内容 //递归的本质是栈结构,考虑使用递归实现 //先递归输出后面的节点,在输出该节点自身 class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { vector<int> ArrayList; if (head!=nullptr){ if (head->next!=nullptr){ ArrayList = printListFromTailToHead(head->next); } ArrayList.push_back(head->val);// return the last elements } return ArrayList; } };
#prac autumn
1 class Solution { 2 public: 3 vector<int> printListFromTailToHead(ListNode* head) { 4 vector<int> res; 5 if(head==nullptr) 6 return res; 7 ListNode* p = head; 8 while(p){ 9 res.push_back(p->val); 10 p = p->next; 11 } 12 reverse(res.begin(),res.end()); 13 return res; 14 } 15 };