双指针
寻找中间节点
-
快指针一次跳俩步,慢指针一次跳一步,两指针同时移动
-
当快指针指向节点为空(偶数个节点)或快指针指向节点的后继节点为空(奇数个节点)时,两指针停止移动
-
此时,慢指针指向链表中间节点
/**
* 寻找中间节点
* @param head
* @return
*/
public static ListNode middleNode(ListNode head) {
// 1.快慢指针
ListNode slow = head, fast = head;
// 2.快指针指向尾节点
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
// 3.返回中间节点
return slow;
}
寻找倒数第K个节点
-
快指针跳到第K+1个节点,此时慢指针与快指针相距K个节点
-
快慢指针同时移动,当快指针指向链表末端(即空节点)时,两指针停止移动
-
此时,慢指针指向链表的倒数第K个节点
/**
* 寻找倒数第K个节点
* @param head
* @param k
* @return
*/
public static ListNode getKthFromEnd(ListNode head, int k) {
// 1.快慢指针
ListNode fast = head;
ListNode slow = head;
// 2.快指针指向 K+1
while (fast != null && k > 0) {
fast = fast.next;
k--;
}
// 3.快指针指向链表末
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
// 4.返回倒数第K节点
return slow;
}
旋转链表
-
常见的情景题:把链表的每个节点,都向右移动K个位置
-
这个是有两种思路的:反转链表、转化为寻找倒数第 K-1 个节点
-
反转链表暂且不表,这里可以看看第二种方法:转化为寻找倒数第 K-1 个节点
-
把链表的每个节点,都向右移动K个位置 => 把链表的后 K 个节点,都旋转成前 K 个节点
-
那就把问题转换成了:转化为寻找倒数第 K-1 个节点:
-
此时慢指针指向了倒数第 K-1 个节点,快指针指向了链表的尾节点
-
倒数第 K 个节点为头节点(断掉慢指针指向节点的后继,快指针指向原头节点)
/**
* 旋转链表
*
* @param head
* @param k
* @return
*/
public static ListNode rotateRight(ListNode head, int k) {
if (head == null || k == 0) {
return head;
}
// 1.快慢节点
ListNode temp = head;
ListNode fast = head;
ListNode slow = head;
// 2.获取链表长度
int len = 0;
while (head != null) {
head = head.next;
len++;
}
// 3.以首尾旋转
if (k % len == 0) {
return temp;
}
// 4.快指针先走K步
while ((k % len) > 0) {
k--;
fast = fast.next;
}
// 5.快慢指针同时走
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 6.获得截断处
ListNode res = slow.next;
slow.next = null;
// 7.重置头节点
fast.next = temp;
return res;
}