这是我参与更文挑战的第17天,活动详情查看:更文挑战
链表与数组一样,是线性数据结构,使用双指针技巧,可以巧妙解决很多问题。
反转链表
题意:反转一个单链表。
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
反转链表,可以通过定义一个新链表实现反转,但是这样造成极大浪费。我们知道,在链表中,每个节点有一个指针域指向后继节点。那么只需要改变指针的指向,把每个节点的指针指向其前驱节点(表头指针指向null),就可以完成反转链表。
使用双指针技巧,定义两个指针,cur和pre。指针cur指向当前处理的节点,指针pre指向cur的前驱节点。初始化时,cur指向链表头结点,pre指向null。开始反转时,先使用临时指针保存cur的后继节点,再将cur的指针指向pre,改变cur的后继节点。最后移动cur和pre,当cur`为null时,完成反转。如下:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode current = head;
while (current != null) {
ListNode next = current.next;
current.next = pre;
pre = current;
current = next;
}
return pre;
}
}
删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
示例 1:
- 输入:head = [1,2,3,4,5], n = 2
- 输出:[1,2,3,5]
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode slow = dummy;
ListNode fast = dummy;
while (n-- > 0) {
fast = fast.next;
}
// 记住 待删除节点slow 的上一节点
ListNode prev = null;
while (fast != null) {
prev = slow;
slow = slow.next;
fast = fast.next;
}
// 上一节点的next指针绕过 待删除节点slow 直接指向slow的下一节点
prev.next = slow.next;
return dummy.next;
}
}
环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
进阶:你能用 O(1)(即,常量)内存解决此问题吗?
可以使用快慢指针法, 分别定义 fast 和 slow指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
- 为什么fast 走两个节点,slow走一个节点,有环的话,一定会在环内相遇呢,而不是永远的错开呢?
- 这个其实可以类比两个速度不同的人跑步,如果他们在直路上行驶,快跑者将首先到达目的地。但是,如果它们在圆形跑道上跑步,那么快跑者如果继续跑步就会追上慢跑者。
- 下一个问题:这两个指针的适当速度应该是多少?
- 一个安全的选择是每次移动慢指针一步,而移动快指针两步。每一次迭代,快速指针将额外移动一步。如果环的长度为 M,经过 M 次迭代后,快指针肯定会多绕环一周,并赶上慢指针。
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null || head.next == null){
return false;
}
ListNode fast = head.next;
ListNode slow = head;
while (fast != slow && fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return fast == slow;
}
}