本文是链表笔试真题的刷题笔记,涵盖移除元素、找中间节点、找倒数第 K 个节点、合并有序链表、链表分割、回文判断、相交判定、环形链表与入口点、反转链表共 10 道高频题。每道题都给出题目链接、解题思路、完整代码与易错点,适合面试前集中复习。
阅读建议
- 先独立思考:每道题先看题目链接,自己尝试 10 分钟,再对照思路与代码。
- 关注易错点:每道题末尾的「注意」是面试高频失分点,务必理解。
- 动手验证:建议在本地 IDE 或 LeetCode 在线环境跑通每段代码,加深记忆。
复杂度速查表
| 题目 | 时间复杂度 | 空间复杂度 | 核心技巧 |
|---|---|---|---|
| 1. 移除链表元素 | O(n) | O(1) | 双指针遍历 |
| 2. 链表的中间节点 | O(n) | O(1) | 先求长度再走一半 |
| 3. 链表的K个节点 | O(n) | O(1) | 长度减 K |
| 4. 合并两个有序链表 | O(n+m) | O(1) | 尾插法 + 哨兵节点 |
| 5. 链表分割 | O(n) | O(1) | 双链表拆分再拼接 |
| 6. 链表的回文结构 | O(n) | O(1) | 找中点 + 反转后半段 |
| 7. 判定链表相交 | O(n+m) | O(1) | 长度对齐 |
| 8. 环形链表 | O(n) | O(1) | 快慢指针 |
| 9. 求环的入口点 | O(n) | O(1) | 快慢指针 + 数学推导 |
| 10. 反转单链表 | O(n) | O(1) | 三指针迭代 |
链表进阶:链表笔试真题
- 1.移除链表元素
- 2.链表的中间节点
- 3.链表的K个节点
- [链表的K个节点 1,搞出链表的尺寸,然后减去K 2,通过循环找出节点 3,将其设置为头节点 注意:链表可能为null,K可能大于链表尺寸,或者为0](#链表的K个节点 1,搞出链表的尺寸,然后减去K 2,通过循环找出节点 3,将其设置为头节点 注意:链表可能为null,K可能大于链表尺寸,或者为0)
- 4.合并两个有序链表
- [合并两个有序链表 !在这里插入图片描述(https://i-blog.csdnimg.cn/direct/771b9945bf124ea68c4ad7378b3b50ba.png) 注意:最后要判断cur1或者cur2](#合并两个有序链表
注意:最后要判断cur1或者cur2) - [5.给定 x, 把一个链表整理成前半部分小于 x, 后半部分大于等于 x 的形式](#5.给定 x, 把一个链表整理成前半部分小于 x, 后半部分大于等于 x 的形式)
- 6.链表的回文结构
- [方法一: 1.首先将链表复制一份,注意傀儡节点; 2.将复制的链表逆置.再分别遍历进行比较;](#方法一: 1.首先将链表复制一份,注意傀儡节点; 2.将复制的链表逆置.再分别遍历进行比较;)
- [方法二: 1.取中间节点,从内部截取后半段做为链表; 2.再逆置后半段的链表; 3.以后半端的链表为基准,与前面进行比较;](#方法二: 1.取中间节点,从内部截取后半段做为链表; 2.再逆置后半段的链表; 3.以后半端的链表为基准,与前面进行比较;)
- 7.判定链表相交
- [判定链表相交 1.首先要对齐,题目给定的链表长度是不一样的,需要设置两个节点用于对齐这两个链表; 2.判断如果A的大小比B大,对齐B,减小长度到B; 3.判断如果B的大小比A大,对齐A,减小长度到A; 4.然后分别遍历比较这两个链表,注意比较的是==相同节点==,注意不断同时更新两个链表;](#判定链表相交 1.首先要对齐,题目给定的链表长度是不一样的,需要设置两个节点用于对齐这两个链表; 2.判断如果A的大小比B大,对齐B,减小长度到B; 3.判断如果B的大小比A大,对齐A,减小长度到A; 4.然后分别遍历比较这两个链表,注意比较的是==相同节点==,注意不断同时更新两个链表;)
- 8.环形链表
- [环形链表 这题采用双指针法,快慢指针相等时,表示有环;](#环形链表 这题采用双指针法,快慢指针相等时,表示有环;)
- 9.求环的入口点
- [2.这和找出如环点,先创建节点指向head,在创建节点指向slow或fast; 注意:这里是数学原理,包括前面用双指针判断是否有环,感兴趣可以自己证明,这个结论就是得出的结果是它们相差是成倍数的,只要运行就会相遇(带环)](#2.这和找出如环点,先创建节点指向head,在创建节点指向slow或fast; 注意:这里是数学原理,包括前面用双指针判断是否有环,感兴趣可以自己证明,这个结论就是得出的结果是它们相差是成倍数的,只要运行就会相遇(带环))
- 10.反转单链表
1.移除链表元素

解析
这里要删除所有该整数的节点
首先我们需要找到被删除的节点以及它前一个节点,定义两个节点;
ListNode cur = head.next
ListNode pre = head

答案
java
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null){
return null;
}
ListNode cur = head.next;
ListNode pre = head;
while(cur!=null){
if(cur.val==val){
pre.next = cur.next;
cur = cur.next;
}
else{
pre = cur;
cur = cur.next;
}
}
if(head.val==val){
head = head.next;
return head;
}
return head;
}
}
2.链表的中间节点

java
public ListNode middleNode(ListNode head) {
int size = 0;
for(ListNode cur = head;cur!=null;cur = cur.next){
size++;
}
ListNode cur = head;
for(int i = 0;i<size/2;i++){
cur = cur.next;
}
head = cur;
return head;
}
3.链表的K个节点
链表的K个节点
1,搞出链表的尺寸,然后减去K
2,通过循环找出节点
3,将其设置为头节点
注意:链表可能为null,K可能大于链表尺寸,或者为0
java
public class Solution {
public ListNode FindKthToTail(ListNode head,int k) {
if(head==null){
return head;
}
int size = 0;
for(ListNode cur = head;cur!=null;cur = cur.next){
size++;
}
if(k==0){
return null;
}
if(k>size){
k= size/k;
}
k = size-k;
ListNode cur = head;
for(int i = 0;i<k;i++){
cur = cur.next;
}
head = cur;
return head;
}
4.合并两个有序链表
合并两个有序链表

注意:最后要判断cur1或者cur2
java
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode Dhead = new ListNode(0);
ListNode Dtail = Dhead;
ListNode cur1 = list1;
ListNode cur2 = list2;
while(cur1!=null&&cur2!=null){
if(cur1.val<cur2.val){
Dtail.next = cur1;
cur1 = cur1.next;
Dtail = Dtail.next;
}
else{
Dtail.next = cur2;
cur2 = cur2.next;
Dtail = Dtail.next;
}
}
if(cur1==null){
Dtail.next = cur2;
}
if(cur2==null){
Dtail.next = cur1;
}
return Dhead.next;
}
5.给定 x, 把一个链表整理成前半部分小于 x, 后半部分大于等于 x 的形式
java
/*
解题思路:
1. 构造两个新链表lessHead和greatHead,为了后序尾插简单,将链表构造成带头结点的
2. 遍历链表,将小于x的节点往lessHead尾部插入,较大的节点往greatHead尾部插入
3. 将greatHead拼接在lessHead之后形成一个链表并返回
*/
public class Partition {
public ListNode partition(ListNode pHead, int x) {
if(pHead == null){
return null;
}
// 1. 构造两个新链表---为了后序尾插简单,将链表构造成带头结点的
ListNode lessHead = new ListNode(0);
ListNode tailL = lessHead;
ListNode greatHead = new ListNode(0);
ListNode tailG = greatHead;
// 遍历链表,将小于x的节点往lessHead尾部插入,较大的节点往greatHead尾部插入
ListNode cur = pHead;
while(cur != null){
if(cur.val < x){
tailL.next = cur;
tailL = cur;
}else{
tailG.next = cur;
tailG = cur;
}
cur = cur.next;
}
// 将greatHead拼接在lessHead之后形成一个链表
tailL.next = greatHead.next;
tailG.next = null;
return lessHead.next;
}
}
6.链表的回文结构
方法一:
1.首先将链表复制一份,注意傀儡节点;
2.将复制的链表逆置.再分别遍历进行比较;
java
public boolean chkPalindrome(ListNode A) {
if (A == null) {
return false;
}
if (A.next == null) {
return true;
}
write code here
ListNode head = new ListNode(0);
ListNode tail = head;
for (ListNode cur = A; cur != null; cur = cur.next) {
tail.next = cur;
tail = tail.next;
}
head = head.next;
ListNode cur = head;
ListNode pre = null;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
head = pre;
ListNode cur1 = A;
ListNode cur2 = head;
while (cur1 != null && cur2 != null) {
if (cur1.val != cur2.val) {
return false;
}
cur1 = cur1.next;
cur2 = cur2.next;
}
return true;
}
方法二:
1.取中间节点,从内部截取后半段做为链表;
2.再逆置后半段的链表;
3.以后半端的链表为基准,与前面进行比较;
java
public boolean chkPalindrome(ListNode A) {
if (A == null) {
return false;
}
if (A.next == null) {
return true;
}
int size = 0;
for (ListNode cur = A; cur != null; cur = cur.next) {
size++;
}
ListNode cur = A;
for (int i = 0; i < size / 2; i++) {
cur = cur.next;
}
ListNode B = cur;
ListNode start = cur;
ListNode pre = null;
while(start!=null){
if(start.next==null){
B = start;
}
ListNode next = start.next;
start.next = pre;
pre = start;
start = next;
}
ListNode cur1 = A;
ListNode cur2 = B;
while(cur2!=null&&cur2!=null){
if(cur2.val!=cur1.val){
return false;
}
cur1 = cur1.next;
cur2 = cur2.next;
}
return true;
}
7.判定链表相交
判定链表相交
1.首先要对齐,题目给定的链表长度是不一样的,需要设置两个节点用于对齐这两个链表;
2.判断如果A的大小比B大,对齐B,减小长度到B;
3.判断如果B的大小比A大,对齐A,减小长度到A;
4.然后分别遍历比较这两个链表,注意比较的是相同节点,注意不断同时更新两个链表;
java
public class Solution {
public int getSize(ListNode list) {
int size = 0;
for (ListNode cur = list; cur != null; cur = cur.next) {
size++;
}
return size;
}
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode curA = headA;
ListNode curB = headB;
if (headA == null || headB == null) {
return null;
}
int sizeA = getSize(headA);
int sizeB = getSize(headB);
if (sizeA < sizeB) {
int diff = sizeB - sizeA;
for (int i = 0; i < diff; i++) {
curB = curB.next;
}
} else {
int diff = sizeA - sizeB;
for (int i = 0; i < diff; i++) {
curA = curA.next;
}
}
while (curA != null && curB != null) {
if (curA == curB) {
return curB;
}
curA = curA.next;
curB = curB.next;
}
return null;
}
8.环形链表
环形链表
这题采用双指针法,快慢指针相等时,表示有环;
java
public class Solution {
public boolean hasCycle(ListNode head) {
if(head == null||head.next==null){
return false;
}
ListNode slow = head;
ListNode fast = head;
while(fast!=null&&fast.next!=null){
slow = slow.next;
fast = fast.next.next;
if(slow==fast){
return true;
}
}
return false;
}
9.求环的入口点
1.先判断是否有环,这一步很重要,用双指针;
java
if (head == null || head.next == null) {
return null;
}
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;
}
2.这和找出如环点,先创建节点指向head,在创建节点指向slow或fast;
注意:这里是数学原理,包括前面用双指针判断是否有环,感兴趣可以自己证明,这个结论就是得出的结果是它们相差是成倍数的,只要运行就会相遇(带环)
类似于跑圈,校园里总会有身体不错的,他速度快,把人家套圈了
java
ListNode cur1 = head;
ListNode cur2 = fast;
while (cur1 != cur2) {
cur1 = cur1.next;
cur2 = cur2.next;
}
return cur1;
}
10.反转单链表
这个用的是三指针法;
图解:

这是三个指针

java
public ListNode reverseList(ListNode head) {
if(head == null){
return head;
}
if(head.next == null){
return head;
}
ListNode pre = null;
ListNode cur = head;
while(cur!=null){
ListNode next = cur.next;
if(next == null){
head = cur;
}
cur.next = pre;
pre = cur;
cur = next;
}
return head;
}