所谓的双指针也就是两个变量,在一些链表问题中使用双指针解法可以有效率地解决问题。
1.寻找链表中间结点
比如力扣876给定一个头结点为 head 的非空单链表,返回链表的中间结点。如果有两个中间结点,则返回第二个中间结点。
思路:这道题就可以使用快慢双指针的思想进行解决,利用两个指针来进行数据索引,一个快的,一个慢的,俗称“龟兔赛跑”。设置两个指针均指向链表头部,慢指针走一步,快指针则走两步。这样当快指针到达链表末尾时,慢指针就正好指向了链表的中间节点。
var middleNode = function(head) {
let fastPointer = head, slowPointer = head;
while (fastPointer && fastPointer.next) {
slowPointer = slowPointer.next;
fastPointer = fastPointer.next.next;
}
return slowPointer;
}
思考:
1).链表数据为偶数时,中间结点有两个,为什么返回靠后的中间结点?
如果链表长度为奇数,那么只有一个中间节点,而慢指针正好指向这个中间节点,所以返回的是唯一的中间节。
但如果链表长度为偶数,有两个中间节点。此时,慢指针指向其中的一个中间节点,而快指针在链表末尾的下一个节点,因此,慢指针指向的是靠后的那个中间节点。
为什么要选择返回靠后的中间节点呢?这其实是一个约定俗成的做法,可以看做是一种设计上的决策。在某些应用中,返回靠后的中间节点可能更符合预期的结果,而在另一些情况下可能会选择返回靠前的中间节点。重要的是,在实现链表操作时,始终遵循相同的规则,以确保一致性。
2).比如
1,2,3,4,5
使用标准的快慢指针就是后面的4,而在很多数组问题中会是前面的3,为什么会这样?还有就是数组和链表的的索引起始位置不同,数组为0,链表为1.因此在数组双指针问题,要比链表往左移动一位。
2.寻找倒数第K个元素
找出单向链表中倒数第 k 个节点。
思路:设置两个指针均指向链表头部,先让快指针走k步,此时快指针指向第
k + 1
个结点,再让快慢指针同时走,当快指针遍历到链表尾结点时,慢指针此时所指的就是倒数第k
个结点。
var getKthFromEnd = function(head, k) {
let slowPointer = fastPointer = head;
while (k > 0) {
fastPointer = fastPointer.next;
k--;
}
while (fastPointer) {
fastPointer = fastPointer.next;
slowPointer = slowPointer.next;
}
return slowPointer;
}
3.旋转链表
力扣61题,给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
思路:
先用双指针策略找到倒数K的位置,将链表分成两个链表,之后再将两个链表拼接起来即可。
因为K有可能大于链表长度,所以首先遍历一遍链表获取长度
lengthOfListNode
,若k = k % lengthOfListNode === 0
,即刚好旋转一圈,则不用旋转,直接返回头结点。否则先让先让快指针走k
步,再让快慢指针同时走,当快指针遍历到链表尾结点时,慢指针此时所指的要断开的结点。让快指针指向的结点链接到原链表头部,慢指针指向的结点断开和下一结点的联系。最后返回结束时慢指针指向的结点的下一结点。
var rotateRight = function(head, k) {
let slowPointer = fastPointer = tempPointer = head;
let lengthOfListNode = 0;
let responseListNode = new ListNode();
while (head === null | k === 0) return head;
// 先得出链表长度
while (head) {
head = head.next;
lengthOfListNode++;
}
// 这里刚好旋转一圈
if (k % lengthOfListNode === 0) return tempPointer;
// k取模是为了防止k大于链表长度的情况
while (k % lengthOfListNode > 0) {
fastPointer = fastPointer.next;
k--;
}
while (fastPointer.next) {
fastPointer = fastPointer.next;
slowPointer = slowPointer.next;
}
responseListNode = slowPointer.next;
slowPointer.next = null;
fastPointer.next = tempPointer;
return responseListNode;
};
4.总结
快慢指针真的是一种简单又很有效率的方法,还可以将其应用到很多链表题目当中。以后刷算法题或者面试时,遇到链表的题目可以优先考虑一下。