06 从尾到头打印新链表

Posted guoxinxin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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




以上是关于06 从尾到头打印新链表的主要内容,如果未能解决你的问题,请参考以下文章

从尾到头打印链表

从尾到头打印链表

《剑指Offer—— 06. 从尾到头打印链表》代码

剑指 Offer 06. 从尾到头打印链表

剑指Offer06. 从尾到头打印链表

剑指Offer06. 从尾到头打印链表