Leetcode 24. 两两交换链表中的节点
题目链接
思路:
代码:
/**
* 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 swapPairs(ListNode head) {
// 设置一个虚拟头节点
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
// 定义一个指针令其指向虚拟头节点
ListNode cur = dummyHead;
// cur.next和cur.next.next就是要交换的节点,所以都不能为null
while (cur.next != null && cur.next.next != null) {
// 定义一个临时节点来存储第一个要交换的节点
ListNode tmp = cur.next;
// 定义一个临时节点来存储第二对要交换节点的第一个节点
ListNode tmp1 = cur.next.next.next;
// 步骤一
cur.next = cur.next.next;
// 步骤二
cur.next.next = tmp;
// 步骤三
cur.next.next.next = tmp1;
// 交换完成,cur向后移动两位,准备进行下一轮交换
cur = cur.next.next;
}
// 交换完成返回头节点
return dummyHead.next;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
Leetcode 19.删除链表的倒数第N个节点
题目链接
思路:双指针法,定义两个指针使其指向虚拟头节点,先让快指针移动n步,这里由于使用了虚拟头节点,所以移动n步所指向的位置为要删除节点的前一个节点,下面就是令快慢指针同时移动,直到快指针的下一个节点指向null,即快指针遍历完链表,然后删除目标节点。
代码:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
if (head == null || n <= 0) {
return head;
}
// 定义一个虚拟头节点
ListNode dummyHead = new ListNode(-1);
dummyHead.next = head;
ListNode slowIndex = dummyHead;
ListNode fastIndex = dummyHead;
// 这里fast指针先走n步,走到要删除节点的前一个节点,使快慢指针之间相差n
for (int i = 0; i < n; i++) {
fastIndex = fastIndex.next;
}
// 这时快慢指针同时移动
while (fastIndex.next != null) {
fastIndex = fastIndex.next;
slowIndex = slowIndex.next;
}
// 删除目标节点
slowIndex.next = slowIndex.next.next;
return dummyHead.next;
}
}
Leetcode 面试题 02.07. 链表相交
题目链接
思路:
代码:
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
int lenA = 0;
int lenB = 0;
// 分别求出链表A和链表B的长度
while (curA != null) {
curA = curA.next;
lenA++;
}
while (curB != null) {
curB = curB.next;
lenB++;
}
curA = headA;
curB = headB;
// 令curA为长链表的头,lenA为长链表的长度
if (lenB > lenA) {
ListNode tmpNode = curA;
curA = curB;
curB = tmpNode;
int tmpLen = lenA;
lenA = lenB;
lenB = tmpLen;
}
// 求出链表的长度差
int gap = lenA - lenB;
// 移动curA使curA与curB在同一起点上(末尾位置堆起)
while (gap-- > 0) {
curA = curA.next;
}
// 遍历curA和curB,遇到相等则直接返回
while (curA != null) {
if (curA == curB) {
return curA;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
}
- 时间复杂度:O(n + m)
- 空间复杂度:O(1)
Leetcode 142.环形链表II
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fastIndex = head;
ListNode slowIndex = head;
while (fastIndex != null && fastIndex.next != null) {
slowIndex = slowIndex.next;
fastIndex = fastIndex.next.next;
// 相遇,说明有环
if (slowIndex == fastIndex) {
ListNode index1 = fastIndex;
ListNode index2 = head;
// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
链表总结
- 虚拟头节点的使用,由于操作头节点和其他节点存在差异,所以使用虚拟头节点能够统一代码逻辑,这里需要注意的是使用了虚拟头节点,这个虚拟头节点就变成了链表的首位置,原本链表的index位置则变成了index+1的位置。
- 经典题目有:
- 链表的基本操作
- 反转链表
- 删除倒数第N个节点
- 链表相交
- 环形链表