剑指 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;
}