剑指 06 从尾到头打印链表

65 篇文章 1 订阅

剑指 06 从尾到头打印链表

原题目

输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回)。

示例 1:

输入:head = [1,3,2]
输出:[2,3,1]

给出链表的定义:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */

自己的第一遍解法

刚开始想到用虚拟头结点指向尾部,一步步翻转过来,但是当时只想到用一个指针指向当前节点,苦于没有想到用另一个指针指向之前的节点,因此反转过程十分痛苦。这里就不上自己一半的代码了,下面看看网上好的解法与剑指上推荐的解法。网上解法来自公众号《代码随想录》。


好的解法

人家的解法好就好在考虑问题比较全面,比如这道题,题目在没有明说是否能改变原链表的情况下,就要做两种考虑,可以改变链表与不改变链表;

改变链表

顾名思义,就是直接把链表的指向改过来,原来的头结点变成尾节点,原来的尾节点现在变成头结点。这里用到双指针法,单个指针(指向当前节点)的解法很困难,因为单个指针确定不了当前节点的前一节点,这样当前节点改变指向不知道上一节点是谁。

class Solution_double_pointer {// 双指针法,会改变链表方向
public:
    vector<int> reversePrint(ListNode* head) {
        vector<int> res {};

        ListNode *pre(nullptr);
        ListNode *cur = head;
        while (cur != nullptr)
        {
            // 记住当前节点的下一指向
            ListNode *temp = cur->next;

            // 反转方向
            cur->next = pre;

            // 双指针移动
            pre = cur;
            cur = temp;
        }
        // 退出while循环条件:当前结点是尾节点指向的null,则pre就是尾节点

        // 在逆序链表上顺序读取值
        ListNode *recordPoint = pre;
        while (recordPoint != nullptr)
        {
            res.push_back(recordPoint->val);
            recordPoint = recordPoint->next;
        }

        return res;

    }
};

代码中注释已经很清楚了,双指针法最重要的就是要使用临时指针temp保存当前节点的下一节点,这样在当前节点cur的指向反转到上一节点pre后,能够移动双指针中的当前指针到下一节点上。

不改变链表

在不改变链表的前提下,我们每次遍历链表都是从前向后,但是题目要求的数值是链表从后向前遍历才能实现的。考虑到顺序反转,与栈的特性(先进后出)十分符合。可以使用一个额外空间的栈,保存各个节点,都保存进去以后,栈顶就是链表的尾节点,栈底是头结点,这样我们隐式地改变了链表的顺序。最后不断弹出栈顶节点并记录节点值,就可以完成题目要求。

这种方法的优点是:不会改变原链表结构

缺点是:需要额外的存储空间

 class Solution_stack {// 栈记录节点,先进后出
 public:
     vector<int> reversePrint(ListNode* head) {
         vector<int> res;
        stack<ListNode*> nodeValue;
        ListNode *cur = head;
        // 将节点存入栈中
        while (cur != nullptr)
        {
            nodeValue.push(cur);
            cur = cur->next;
        }

        while (!nodeValue.empty())
        {
            cur = nodeValue.top();
            res.push_back(cur->val);
            nodeValue.pop();
        }
         return res;
     }
 };

测试用例

这里的测试比较简单,链表无非就是多个节点、单节点、空链表这三种情况。

void test01()
{
    ListNode x0 = 0;
    // x1->x2->x3
    ListNode x1 = 1;
    ListNode x2 = 2;
    ListNode x3 = 3;
    x1.next = &x2;
    x2.next = &x3;

    cout << "x1 val = " << x1.val << endl;
    cout << "x1.next->val = " << x1.next->val << endl;
    cout << "x1.next->next->val = " << x1.next->next->val <<endl;

    Solution so;
    
    //多节点
    vector<int> res_1 = so.reversePrint(&x1);
    for(auto e:res_1) cout << e << " ";
    cout << endl;

    //单节点
    vector<int> res_2 = so.reversePrint(&x0);
    for(auto e:res_2) cout << e << " ";
    cout << endl;

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值