题目描述
输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。
思路一:反转链表后再读出
看到这道题我们很自然而然的就会想到,既然让我们从尾到头进行一个打印,那很明显,我们可以先把链表进行反转,再从头到尾打印一遍即可。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
vector<int> result;
//反转链表
ListNode* pre = NULL;
ListNode* cur = head;
while(cur != NULL){
ListNode* next = cur->next;//存放当前节点的下一节点
cur->next = pre;//cur从指向next,改为指向pre
pre = cur ;//右移
cur = next;//下一节点赋值给当前节点
}
//反转完成时,
while(pre!= nullptr){
result.push_back(pre->val);
pre = pre->next;
}
return result;
}
};
思路二:用栈去实现
如果我们不希望对于输入的数据进行修改。那我们可以在从头到尾遍历链表的时候,用栈记录下当前节点的值。当完成链表的遍历的时候,然后根据栈先进后出的特性,从栈顶开始逐个输出节点的值,那么就实现了从尾到头打印链表的目的。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> printListFromTailToHead(ListNode* head) {
stack<int> nodes;//存放节点的值
vector<int> result;
ListNode* pNode = head;
//用栈去记录数值
while(pNode!=nullptr){
nodes.push(pNode->val);
pNode = pNode->next;
}
//用vertor去读取从栈顶到栈底的元素
while(!nodes.empty()){
result.push_back(nodes.top());
nodes.pop();
}
return result;
思路三:递归实现
既然我们想到了用栈去实现这个函数,而递归的本质是一个栈的结构。所以我们很自然地又想到了用递归去实现。
思路就是,我们先输出当前节点的下一节点的值,再输出当前节点的值,即可完成。
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
/**
* struct ListNode {
* int val;
* struct ListNode *next;
* ListNode(int x) :
* val(x), next(NULL) {
* }
* };
*/
class Solution {
public:
vector<int> result;
vector<int> printListFromTailToHead(ListNode* head) {
if(head != nullptr)
{
if(head->next != nullptr)
{
printListFromTailToHead(head->next);
}
result.push_back(head->val);
}
return result;
}
};
递归会一直执行到链表的最后一个节点,然后发现下一节点为空,不再进行调用,然后把当前节点的值添加进result之中。然后添加完后返回递归调用的上一级,添加它的上一个节点的值,依次类推,直到添加完成链表所有的值。
小结
思路一引起了输入的改变。
思路三递归的代码看上去很简洁,但是如果当链表非常长的时候,就会导致函数调用的层级很深,从而有可能导致函数调用栈溢出。
针对代码的鲁棒性,而言最好的是思路二。
参考文献
《剑指offer》
牛课网刷题链接link.