剑指 Offer 24. 反转链表
题目:
思路:
【1】本身对链表的处理其实离不开遍历链表,准确点的话其实这道题双指针的效率应该是最佳的,毕竟就只用了额外的两个变量。而且也只遍历了一遍。
【2】当然采用递归也是可以的,本身只考虑反转而没有其他要求的话,递归用的变量是最少的,但是内存空间就不一定了,毕竟调用栈也是需要内存空间的,所以其实空间复杂度认真追究的话应该是O(N),但是捞在,别人喜欢问你如果用单个变量处理的时候,你有思考过,有了解过,因为它返回的是原本的头部节点,反转后的尾部节点。
代码展示:
双指针循环处理:
//时间0 ms击败100% //内存41.1 MB击败29.76% //采用双指针进行循环,因为涉及到对变量的更换,其实会需要三个变量,由于本身就会给一个head,所以额外需要两个 /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseList(ListNode head) { ListNode tmp = null, pre = null; while(head != null) { // 暂存后继节点 cur.next tmp = head.next; // 修改 next 引用指向 head.next = pre; // pre 暂存 cur pre = head; // cur 访问下一节点 head = tmp; } return pre; } }
递归单变量进行处理:
//使用单个变量进行链表的反转的函数为toReverseList,但是它返回的是原本的头部节点,反转后已经变为了尾部节点,如果纯粹只是反转,这种更节省,
//但是如果需要返回头部节点,即反转前的尾部节点就需要额外处理了 //时间0 ms击败100% //内存41.2 MB击败15.80% /** * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; * ListNode(int x) { val = x; } * } */ class Solution { public ListNode reverseList(ListNode head) { ListNode res = head; while (res!= null && res.next != null){ res = res.next; } toReverseList(head); return res; } public ListNode toReverseList(ListNode head) { if (head == null || head.next == null){ return head; } ListNode temp = toReverseList(head.next); head.next = null; temp.next = head; return head; } }
递归的第二种写法:
这种较上一种减少了一次循环,但是增加了一个变量,如果是两个变量的话,还不如双指针循环效率来得高,且节省空间。
class Solution { public ListNode reverseList(ListNode head) { return recur(head, null); // 调用递归并返回 } private ListNode recur(ListNode cur, ListNode pre) { if (cur == null) return pre; // 终止条件 ListNode res = recur(cur.next, cur); // 递归后继节点 cur.next = pre; // 修改节点引用指向 return res; // 返回反转链表的头节点 } }