24. 两两交换链表中的节点
思路:找到要交换的两个节点的前一个节点就可以进行交换,修改链表指针基本操作。注意,有虚拟头结点操作更方便。
时间复杂度:O(n)
class Solution {
public ListNode swapPairs(ListNode head) {
if (head == null) {
return head;
}
ListNode dummy = new ListNode();
dummy.next = head;
ListNode i = dummy;
ListNode j = head;
while (i.next != null && j.next != null) {
i.next = j.next;
j.next = j.next.next;
i.next.next = j;
i = j;
j = j.next;
}
return dummy.next;
}
}
19.删除链表的倒数第N个节点
思路:双指针法。通过双指针实现一次遍历找到倒数第n个节点。注意,有虚拟头结点操作更方便。
时间复杂度:O(n)
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) { // 双指针法
ListNode dummy = new ListNode();
dummy.next = head;
ListNode left = dummy;
ListNode right = dummy;
for (int i = 0; i < n; i++) {
right = right.next;
}
while (right.next != null) {
left = left.next;
right = right.next;
}
left.next = left.next.next;
return dummy.next;
}
}
面试题 02.07. 链表相交
思路:先判断链表是否相交,链表不相交返回null,相交的话根据记录的链表长度,让长的链表指针先走,走到和短的链表同样长的时候,两个链表的指针同时再走,就可以找到第一个相交节点。
时间复杂度:O(n)
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headB == null || headA == null) {
return null;
}
// 先判断是否相交
ListNode i = headA;
ListNode j = headB;
int lenA = 0, lenB = 0;
while (i.next != null) {
i = i.next;
lenA++;
}
while (j.next != null) {
j = j.next;
lenB++;
}
if (i != j) {
return null;
}
i = headA;
j = headB;
while (lenA > lenB) {
i = i.next;
lenA--;
}
while (lenB > lenA) {
j = j.next;
lenB--;
}
while (i != null && j != null) {
if (i == j) {
return i;
}
i = i.next;
j = j.next;
}
return null;
}
}
142.环形链表II
思路:定义快慢指针,慢指针每次走一步,快指针每次比慢指针多走一步。首先判断是否有环,如果有环的话,快指针一定先进环,慢指针进环后第一圈快慢指针一定相遇,没有环的话返回null。通过数学推导,可以确定,快指针从相遇的节点开始走一步,慢指针从第一个节点开始走一步,两个指针再次相遇的时候就是入环节点。
这里引用卡哥网站上的内容记录一下数学推导过程
相遇时: slow指针走过的节点数为:
x + y, fast指针走过的节点数:x + y + n (y + z)。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y): x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示头结点到环形入口节点的的距离。
所以要求x ,将x单独放在左面:x = n (y + z) - y ,
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。
得出结论:从头结点出发一个指针,从相遇节点也出发一个指针,这两个指针每次只走一个节点,那么当这两个指针相遇的时候就是 环形入口的节点。
时间复杂度:O(n)
我的代码实现
public class Solution {
public ListNode detectCycle(ListNode head) {
if (head == null || head.next == null) {
return null;
}
// 判断是否有环
ListNode slow = head;
ListNode fast = head;
while (slow.next != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast || slow == null || fast == null) {
break;
}
}
if (slow != fast) {
return null;
}
// 寻找入环节点
slow = head;
while (slow != fast) {
slow = slow.next;
fast = fast.next;
if (slow == fast) {
break;
}
}
return slow;
}
}
卡哥代码实现
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {// 有环
ListNode index1 = fast;
ListNode index2 = head;
// 两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}