题目描述
反转一个单链表
示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?
我的解法
思路
最直接的想法是定义一个新的链表,对原链表进行遍历找到最后一个节点接到新链表后面,然后在原链表中删除该节点,直到原链表为空
编码过程中出现的问题
原因:在生成新链表时,每次循环都将原来的节点覆盖了
解决:新增了有关新链表的遍历操作
对应Java代码
时间复杂度O(n²)都能超过100%的用户吗。。。力扣的这个时间计算可能存在随机性
class Solution {
public ListNode reverseList(ListNode head) {
ListNode dummyNode = new ListNode(0,head);
ListNode newNode = new ListNode(0);
ListNode newCur = newNode;
while (dummyNode.next != null) {
ListNode cur = dummyNode;
while (cur.next.next != null) {
cur = cur.next;
}
newCur.next = cur.next;
newCur = newCur.next;
cur.next = cur.next.next; //删除原链表尾部结点
}
return newNode.next;
}
}
复杂度分析
时间复杂度:O(n²)
空间复杂度:O(1)
更优解法
此部分转载于公众号代码随想录
思路
如果再定义一个新的链表,实现链表元素的反转,其实这是对内存空间的浪费。其实只需要改变链表的next指针的指向,直接将链表反转 ,而不用重新定义一个新的链表,如图所示:
对应Java代码
运行时间上果然短了很多
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while (cur !=null ) {
ListNode tmp = cur.next; //保存cur的下一个节点用于后续遍历
cur.next = pre; // 翻转操作
// 更新pre 和 cur指针
pre = cur;
cur = tmp;
}
return pre;
}
}
复杂度分析
时间复杂度:O(n)
空间复杂度:O(1)
另一种方法
因为题目里提到有迭代和递归两种方法,现来记录下两者的区别
递归(recursion):递归常被用来描述以自相似方法重复事物的过程,在数学和计算机科学中,指的是在函数定义中使用函数自身的方法。(A调用A)
迭代(iteration):重复反馈过程的活动,每一次迭代的结果会作为下一次迭代的初始值。(A重复调用B)
上面使用的是迭代法,这里记录递归法,算法逻辑上是一样的,只是编码逻辑上不同
对应Java代码
class Solution {
public ListNode reverse(ListNode pre,ListNode cur) { // 因为是递归,被调用的方法肯定要单独拿出来
if(cur == null) { return pre; } // 函数体内定义递归的终止条件
ListNode tmp = cur.next;
cur.next = pre;
return reverse(cur,tmp); // 递归
}
public ListNode reverseList(ListNode head) {
// 递归前的初始化
return reverse(null,head);
}
}
收获总结
- 当你做的题思路混乱做了很多次才通过的时候,后面回想起来大概率还是会一片混乱的,好的方法肯定是逻辑清晰,且写法明确的。
- 那么有没有可以加深印象的方法呢?其实我觉得没有什么好的方法,最朴素的方法就是多做,每天都简单回顾下之前学到的方法,而且只是粗略的回想起算法的步骤轮廓是不够的,隔一段时间再上手做一遍;久而久之思维也会发生变化,看到题目后第一时间想到的就不会是暴力求解了。