双指针
/**
* LeetCode 876. 链表的中间结点
* https://leetcode.cn/problems/middle-of-the-linked-list/
*
* 使用经典的快慢指针解决:
* 用两个指针slow和fast一起遍历链表,slow一次走一步,fast一次走两步
* 那么当fast到达链表的末尾时,slow必然位于中间
* */
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
/**
* 输入一个链表,输出改链表中倒数第K个节点。从1开始计数,即链表末尾的节点是倒数第一个节点
*
* 示例:
* 给定一个链表:1->2->3->4->5,和k= 2
* 返回链表: 4->5
*
* 思路:快慢双指针
* 先将fast向后遍历到第k + 1个节点,slow仍然指向链表的第一个节点,此时指针fast与slow二者之间刚好间隔k个节点
* 之后两个指针同步向后走,当fast走到链表的尾部空节点时,slow指针刚好指向倒数第K个节点
* */
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode fast = head;
ListNode slow = head;
while (fast != null && k > 0) {
fast = fast.next;
k--;
}
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;
}
```java
/**
* leetcode 61. 旋转链表
* https://leetcode.cn/problems/rotate-list/
* 方法02:
* 先用双指针找到倒数第K个元素,把链表被分成两条,然后将两个链表调整一下重新连接起来就行
*
* 具体步骤:
* 1、K有可能大于链表长度:先获取链表长度len,然后k = k % len, 如果k = 0,就不用旋转,直接返回头节点
* 2、找到链表断开的位置:快指针先走K步,然后慢指针和快指针一起走。当fast指向链表末尾时,slow指向倒数第K个元素,也就是链表断开的地方
* 3、重新调整连接链表:将fast.next改成head, newHead = slow.next, 然后断开slow和下一节点的联系 slow.next= null
* */
public ListNode rotateRight(ListNode head, int k) {
if (head == null || k == 0) {
return head;
}
// 求链表的长度 处理K大于Len的场景
ListNode cur = head;
int len = 0;
while (cur != null) {
len++;
cur = cur.next;
}
k = k % len;
if (k == 0) {
return head;
}
// 找到链表的倒数第K个元素,也即链表断开的地方
ListNode fast = head;
ListNode slow = head;
while (fast != null && k > 0) {
fast = fast.next;
k--;
}
while (fast.next != null) {
slow = slow.next;
fast = fast.next;
}
// 重新链接链表
fast.next = head;
head = slow.next;
slow.next = null;
return head;
}
删除链表元素
/**
* leetcode 203. 移除链表元素
* https://leetcode.cn/problems/remove-linked-list-elements/
*/
public ListNode removeElements(ListNode head, int val) {
if (head == null) {
return head;
}
ListNode dummyHead = new ListNode(-0);
dummyHead.next = head;
ListNode cur = dummyHead;
while (cur.next != null) {
if (cur.next.val == val) {
cur.next = cur.next.next;
continue;
}
cur = cur.next;
}
return dummyHead.next;
}
/**
* leetcode 19. 删除链表的倒数第 N个结点
* https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
*/
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode fast = head;
ListNode slow = head;
while (n > 0) {
fast = fast.next;
n--;
}
if (fast == null) {
return slow.next;
}
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return head;
}
/**
* leetcode 83. 删除排序链表中的重复元素
* https://leetcode.cn/problems/remove-duplicates-from-sorted-list/
*/
public ListNode deleteDuplicates(ListNode head) {
if (head == null) {
return head;
}
ListNode cur = head;
while (cur.next != null) {
if (cur.val == cur.next.val) {
cur.next = cur.next.next;
continue;
}
cur = cur.next;
}
return head;
}
/**
* 82. 删除排序链表中的重复元素 II
* https://leetcode.cn/problems/remove-duplicates-from-sorted-list-ii/
*
* 由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。
* 由于链表的头节点可能会被删除,因此我们需要额外使用一个哑节点(dummy node)指向链表的头节点。
*
* 具体地,我们用指针 cur 指向链表的哑节点,随后开始对链表进行遍历。
* 如果当前 cur.next与 cur.next.next对应的元素相同,那么我们就需要将 cur.next以及所有后面拥有相同元素值的链表节点全部删除。
* 我们记下这个元素值 target,随后不断将 cur.next从链表中移除,直到 cur.next为空节点或者其元素值不等于target 为止。
* 此时,我们将链表中所有元素值为 target 的节点全部删除。
*
* 如果当前 cur.next与 cur.next.next对应的元素不相同,那么说明链表中只有一个元素值为 cur.next的节点,
* 那么我们就可以将 cur指向 cur.next
*
* 当遍历完整个链表之后,我们返回链表的的哑节点的下一个节点 dummy.next即可。
*/
public ListNode deleteDuplicates02(ListNode head) {
if (head == null) {
return head;
}
ListNode dummy = new ListNode(0, head);
ListNode cur = dummy;
while (cur.next != null && cur.next.next != null) {
if (cur.next.val == cur.next.next.val) {
int target = cur.next.val;
while (cur.next != null && cur.next.val == target) {
cur.next = cur.next.next;
}
continue;
}
cur = cur.next;
}
return dummy.next;
}