一、LC24. 两两交换链表中的节点
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
讲解视频:帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点_哔哩哔哩_bilibili
创立虚拟头节点。翻转两个节点需要从他们的前一个节点开始(三个节点),这样才能记住顺序。
dummy ->1->2->3
dummy->2->1->3 (dummy->2, 2->1, 1->3)
如果节点数量为奇数,最后会多一个节点,cur.next != null 但是cur.next.next==null。
如果节点数量为偶数,最后会没有多出的节点 cur.next==null && cur.next.next==null。
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode();
dummyHead.next = head;
ListNode cur = dummyHead;
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;
tmp.next = tmp1;
cur = cur.next.next;//current往后挪两个
}
return dummyHead.next;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
二、LC19.删除链表的倒数第N个节点
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
讲解视频:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili
双指针法:快慢指针。创立虚拟头节点。
快指针先走n+1步,之后和慢指针同步每次走1步。当快指针走到null时,慢指针走到倒数n+1个节点。
第一次走n+1步,不然最后慢指针落在倒数第n个节点,没有办法删除(需要前一个节点的指针)。
当快指针走到null时,慢指针比他少走了n+1步。倒数第n个数比null差了n个,需要n步才能走到。而删除时需要前一个节点的信息,所以需要慢指针落在倒数n+1的位置上进行删除操作。
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode();
dummyHead.next = head;
ListNode fast = dummyHead;
ListNode slow = dummyHead;
for (int i = 0; i < n + 1; i++) {
fast = fast.next;
}
while (fast != null) {
fast = fast.next;
slow = slow.next;
}
//删除
slow.next = slow.next.next;
return dummyHead.next;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(1)
三、面试题 02.07. 链表相交
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
讲解:代码随想录
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置。此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。否则循环退出返回空指针。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int sizeA = 0;
int sizeB = 0;
ListNode curA = headA;
ListNode curB = headB;
//计算两个列表长度
while (curA != null) {
sizeA++;
curA = curA.next;
}
while (curB != null) {
sizeB++;
curB = curB.next;
}
//让两个链表对齐
if (sizeA > sizeB) {
while (sizeA - sizeB > 0) {
headA = headA.next;
sizeA--;
}
} else {
while (sizeB - sizeA > 0) {
headB = headB.next;
sizeB--;
}
}
//开始寻找相交节点,遇到相同直接返回
for (int i = 0; i < sizeA; i++) {
if (headA == headB) {
return headA;
} else {
headA = headA.next;
headB = headB.next;
}
}
return null;
}
}
- 时间复杂度:O(m+n)
- 空间复杂度:O(1)
四、LC142.环形链表II
力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
视频讲解:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili
第一步:快慢指针判断链表是否有环。快指针每次走两步,慢指针每次走一步。相对于慢指针,快指针以每次一个节点的速度去靠近慢指针,所以一定会相遇,快指针不会跳过慢指针。
第二步:寻找到环入口
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。
那么相遇时: slow指针走过的节点数为: x + y
, fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
快指针肯定能追上慢指针,因为快指针每次比慢指针多走一步,慢指针进入环内一圈以内肯定会被快指针追上(最多两者差一圈n-1,快指针每次多走一步,多走一圈就能追上)。
因为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指针。
说明从头结点出发一个指针,和从相遇地点出发一个指针,两者最后会在环入口处相遇。(如果n=1,x=z;如果n>1,那么相遇地点的指针在环内走n圈但最后还是会从头节点出发的指针在环入口处相遇)
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {//有环,找入口
ListNode index1 = slow;
ListNode index2 = head;
while (index1 != index2) {
index1 = index1.next;
index2 = index2.next;
}
return index1;
}
}
return null;
}
}
- 时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度(慢指针没有在环内走满一圈,走的总距离小于链表总长度),快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n。
- 空间复杂度: O(1)