题目描述
输入一个链表,按链表从尾到头的顺序返回一个ArrayList。
初始代码
/** * struct ListNode { * int val; * struct ListNode *next; * ListNode(int x) : * val(x), next(NULL) { * } * }; */ class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { } };
解法1
思路: 使用头插法得到一个新链表, 该链表中的元素顺序正好和原链表相反. 随后通过遍历新链表, 生成一个vector并返回.
这是我首先想到的方法. 因为对链表的头插法印象比较深刻. 所以最先想到通过头插法来实现链表元素顺序的逆转. 也考虑过另外开辟一个链表的内存会使得该解法的空间复杂度达到O(n). 但毕竟是第一个解法, 先保证正确性再说, 后续在考虑优化.
由于这一解法使用new运算符分配了新链表各个阶段的空间. 因此, 在函数返回之前, 要记得释放掉新链表的结点空间. 好在本身生成vector就需要遍历一次新链表, 在这个遍历的过程中, 顺便也就释放了内存. 就很自然 :)
class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { ListNode* head_new = NULL; for(ListNode *p = head; p != NULL; p = p->next) { ListNode *q = new ListNode(p->val); q->next = head_new; head_new = q; } vector<int> res; for(ListNode *p = head_new; p != NULL;) { res.push_back(p->val); ListNode *q = p->next; delete p; //释放空间 p = q; } return res; } };
解法2
解法2在解法1的基础上, 进行优化. 参考了其它人的优秀解题思路, 发现可以在链表内部就完成反转工作, 没必要另外开辟空间. 其实根本的思想和"头插法"是一脉相承的. 在遍历一次原链表的过程中, 通过修改指针域来边遍历边翻转. 稍微的难点是要理解清楚每一次指针域赋值的含义, 它们执行的先后顺序不能随意变更.
Solution { public: vector<int> printListFromTailToHead(ListNode* head) { ListNode *pre, *tmp, *cur; pre = NULL; cur = head; while(cur) { tmp = cur -> next; cur -> next = pre; pre = cur; cur = tmp; } vector<int> res; while(pre){ res.push_back(pre->val); pre = pre -> next; } return res; } };
解法3
同样是参考了题解区的置顶题解, 对这个简单的题目再进行多维的解决, 从而达到"做一题等于做多题"的效果.
本解法采用递归来实现链表的反转. 其实递归和反转可以说是一对孪生兄弟, 递归在触底反弹之后, 执行的就是一个逆向的遍历过程.
关于本题使用递归, 有两种方式声明vector<int> ret变量. </int>
- 在函数内部声明. 从而在每一层递归都需要声明一次该变量. 一定程度上会加大内存消耗.
- 在函数外部声明, 即把ret声明为Solution类的一个公有变量. 从而函数每次直接调用ret执行操作即可, 不需要在每一层递归都声明一个新的ret.
下面分别是两种实现的代码:
在函数内部声明ret
class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { vector<int> ret; if(!head) return ret; ret = printListFromTailToHead(head->next); ret.push_back(head->val); return ret; } };
在函数外部将ret声明为Solution类的public成员变量
class Solution { public: vector<int> ret; vector<int> printListFromTailToHead(ListNode* head) { if(!head) return ret; printListFromTailToHead(head->next); ret.push_back(head->val); return ret; } };
解法4
解法4是直接利用std命名空间内的reverse()方法来实现vector内部元素的翻转. 虽然说这种直接调用库函数的方法显得很不"道德". 但单看效果, 这确实不失为一种办法.
class Solution { public: vector<int> printListFromTailToHead(ListNode* head) { vector<int> ret; ListNode *p = head; while(p){ ret.push_back(p->val); p = p->next; } std::reverse(ret.begin(), ret.end()); return ret; } };