摘要
本文主要介绍了几个LeetCode链表相关的题目,涵盖了 24. 两两交换链表中的节点、19.删除链表的倒数第N个节点、160.链表相交、142.环形链表II 等题目。最后,ChatGPT为链表专题进行了总结。
1、24. 两两交换链表中的节点
1.1 思路
链表操作模拟题 遍历链表cur表示当前节点,设置p1, p2分表代表需要交换的第一个节点和第二个节点 分为三步,p1.next = p2.next, p2.next = p1, cur.next = p2
1.2 代码
// 链表操作模拟题
// 遍历链表cur表示当前节点,设置p1, p2分表代表需要交换的第一个节点和第二个节点
// 分为三步,p1.next = p2.next, p2.next = p1, cur.next = p2
public ListNode swapPairs(ListNode head) {
ListNode dummy = new ListNode(-1, head);
ListNode cur = dummy;
ListNode p1, p2;
while ((p1 = cur.next) != null && (p2 = p1.next) != null) {
ListNode next = p1;
p1.next = p2.next;
p2.next = p1;
cur.next = p2;
cur = next;
}
return dummy.next;
}
2、19.删除链表的倒数第N个节点
2.1 思路
链表操作模拟题 1.计算链表的长度;2.获取倒数第n个节点的头节点;3.删除节点
注意事项
-
设置虚拟节点,因为删除头节点需要使用虚拟节点
-
获取倒数第n个节点的头节点中,
getNode(dummy, (len - 1) - n)- 因为有虚拟节点,len - 1 才是链表的长度
- 可以代入示例,如
head = [1,2,3,4,5], n = 2,判断是否正确
2.2 代码
// 链表操作模拟题
// 1.计算链表的长度;2.获取倒数第n个节点的头节点;3.删除节点
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(-1, head);
// 1.计算链表的长度
int len = getLength(dummy);
// 2.获取倒数第n个节点的头节点
ListNode prev = getNode(dummy, (len - 1) - n);
// 3.删除节点
prev.next = prev.next.next;
return dummy.next;
}
public int getLength(ListNode head) {
ListNode cur = head;
int len = 0;
while (cur != null) {
len++;
cur = cur.next;
}
return len;
}
public ListNode getNode(ListNode head, int index) {
ListNode cur = head;
while (index > 0 && cur != null) {
index--;
cur = cur.next;
}
return cur;
}
3、160.链表相交
3.1 思路
双指针,同时遍历headA、headB,curA和curB分别代表它们的当前节点 headA遍历完时,curA指向headB;headB遍历完时,curB指向headA 如果headA与headB相交,curA=curB=相交点,反之直至结束,此时curA==curB==null 因为headA=diffA+common,headB=diffB+common,则满足 headA + diffB = headB + diffA
3.2 代码
// 双指针,同时遍历headA、headB,curA和curB分别代表它们的当前节点
// headA遍历完时,curA指向headB;headB遍历完时,curB指向headA
// 如果headA与headB相交,curA=curB=相交点,反之直至结束,此时curA==curB==null
// 因为headA=diffA+common,headB=diffB+common,则满足 headA + diffB = headB + diffA
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
while (curA != curB) {
curA = (curA != null) ? curA.next : headB;
curB = (curB != null) ? curB.next : headA;
}
return curA;
}
4、142.环形链表II
4.1 思路
1、如何想到快慢指针?
如果链表无环,快慢指针不肯能相遇;而且快指针每次移动2步,一个节点一个节点的去靠近慢指针,如果链表有环,快慢指针一定会再环中相遇。
2、如何找到环的入口?
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。
推到公式如下:
- 第一次相遇时:2 (x + y ) = x + y + n (y + z)
- 数学公式推导:x + y = n (y + z)
- 数学公式推导:x = (n -1) (y + z) + z
- 数学公式推导:x = z
所以当slow,fast第一次相遇后,定义两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
4.2 代码
error-1
错误的代码
...
if(fast == null) {
return null;
}
...
正确的代码
if(fast == null || fast.next == null) {
return null;
}
如何判断是链表无环跳出循环还是slow==fast跳出循环的情况?
只用判断fast和fast.next就像,因为快指针fast永远走在慢指针slow的前面,fast不为空slow一定不为空,fast或fast.next为空一定是链表无环,并且因为快指针fast一次走2步(fast=fast.next.next),所以也需要判断fast.next不能为空
error-2
错误的代码
...
while ((slow = slow.next) != null && (fast = fast.next.next) != null) {
if (slow == fast) {
break;
}
}
...
正确的代码
while(fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if(slow == fast) {
break;
}
}
错误的原因
- 快指针fast比慢指针slow快,只需要判断fast指针即可
- 错误的代码中
(fast = fast.next.next) != null,并没有判断fast.next为空的情况
AC
// 快慢指针,遍历链表,定义slow,fast即快慢指针
// 当slow,fast第一次相遇后,定义两个指针,从头结点和相遇结点,各走一步,直到相遇,相遇点即为环入口
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) {
break;
}
}
if(fast == null || fast.next == null) {
return null;
}
// 第二次相遇
ListNode idx1 = head;
ListNode idx2 = slow;
while(idx1 != idx2) {
idx1 = idx1.next;
idx2 = idx2.next;
}
return idx1;
}
5、总结
链表是数据结构中的一个重要部分,以下是关于链表的一些LeetCode题目以及它们的总结:
-
203. 移除链表元素
这道题目要求移除链表中所有值等于给定值的节点。可以使用迭代或递归来处理。遍历链表,如果节点的值等于给定值,就跳过这个节点。
-
707. 设计链表
这是一道设计题,要求实现一个单链表的基本操作,包括插入、删除、获取等。需要特别注意边界条件和链表为空的情况。
-
206. 反转链表
这个问题要求反转一个单链表。可以使用迭代或递归来解决。迭代的方法需要使用三个指针来反转节点的指向关系,递归的方法则递归地反转每个节点。
-
24. 两两交换链表中的节点
这是一道要求交换链表中相邻节点的题目。可以使用迭代或递归来解决。迭代方法需要注意节点的连接顺序,递归方法则递归地处理相邻的节点。
-
19. 删除链表的倒数第N个节点
这个问题要求删除链表中倒数第N个节点。可以使用快慢指针来解决。快指针先走N步,然后快慢指针一起走,当快指针到达链表末尾时,慢指针指向的节点就是要删除的节点。
-
160. 链表相交
这个问题要求判断两个链表是否相交,并找到它们的交点。可以使用双指针法来解决。分别从两个链表的头节点开始,遍历到末尾后跳到另一个链表的头节点继续遍历,直到两个指针相遇或同时到达末尾。
-
142. 环形链表 II
这个问题要求找到环形链表的入口节点。使用快慢指针来判断是否有环,然后从头节点和相遇点同时开始遍历,直到两个指针相遇,相遇点就是环的入口。
这些题目涵盖了链表的基本操作和一些高级问题,对于理解和掌握链表数据结构非常有帮助。可以通过练习这些题目来提高链表操作的熟练度。