no.2: leetcode 206. 反转链表
题目描述:
1. 迭代法:
解法一:
- 朴素解法,直接反转,但需要特殊情况
1 -> 2 -> 3
1 <- 2 3
1 < -2 -< 3
大致的反转过程如上面的例子所示,直接上代码
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return head;
/**
pre --> cur --> curN
/\
temp
pre <-- cur temp
/\
pre
*/
// pre 指向已经反转的链表的头节点
ListNode pre = head;
// cur 指向当前将要反转的节点
ListNode cur = pre.next;
// 这里置空是为了放置出现环的情况
// 因为不置空,前面两个节点会出现环
pre.next = null;
while (cur != null) {
ListNode temp = cur.next;
cur.next = pre;
pre = cur;
cur = temp;
}
head = pre;
return head;
}
}
- 上面的解法还需要考虑特殊情况,可以优化,得到下面的解法:
private ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}
解法二:
- 采用虚拟头节点,这样可以直接把链表节点通过头插方式插入到虚拟头节点之后,从而完成反转操作
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null) return head;
// 采用虚拟头节点后,只需要将链表中节点通过头插方式插入该节点后即可
// 这样就完成了反转操作
ListNode dummy = new ListNode();
ListNode cur = head;
ListNode curN = head.next;
while (cur != null) {
cur.next = dummy.next;
dummy.next = cur;
cur = curN;
if (cur != null)
curN = cur.next;
}
return dummy.next;
}
}
2. 递归法
解法一:
- 这个解法存在一些小问题,我为了方便获取最终翻转后链表的头节点,于是直接把链表最后一个节点逐层向上返回,对于每层递归而言,只能得到翻转后链表的头节点,那么我还需要去专门扫描该链表找到尾节点,这样就增加了复杂度,可以对此进行优化
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode reverseList(ListNode head) {
// 采用递归法
// 递归函数的作用为反转以head为头节点的链表,并返回反转后链表的新的头节点
// 递归出口
// 如果链表为空或者只有一个节点,直接返回head即可
// 那么尾节点会直接返回,其也作为最终的头节点
if (head == null || head.next == null) return head;
ListNode newHead = reverseList(head.next);
head.next = null; // 将当前待翻转的节点下一个节点置空,防止出现环
ListNode tail = newHead;
while (tail.next != null) tail = tail.next;
tail.next = head;
return newHead;
}
}
解法二:对解法一进行优化
- 上面解法存在的问题在于我们每次需要遍历得到翻转后链表的尾节点,我们当时得到尾节点的位置是通过遍历翻转链表得来的,而实际上,当前节点的下一个节点就是翻转链表的尾节点,那么head.next.next即表示翻转链表尾节点的后驱节点,那么代码可以变为:
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode cur = reverseList(head.next);
head.next.next = head;
head.next = null;
return cur;
}
}