链表面试题
1.删除链表中等于给定值val的所有节点。203. 移除链表元素 - 力扣(LeetCode)
/*** 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 removeElements(ListNode head, int val) {if (head == null) {return head;}head.next = removeElements(head.next, val);return head.val == val ? head.next : head;}
}
空链表处理:若 head 为 null,直接返回 head
递归处理后续节点:
head.next = removeElements(head.next, val); 这是递归调用。对当前节点 head 的下一个节点调用 removeElements 方法,目的是递归地删除后续链表中值为 val 的节点,并将处理后的后续链表重新连接到当前节点 head 的 next 指针上。
判断当前节点是否删除:
return head.val == val? head.next : head; 检查当前节点 head 的值是否等于要删除的值 val。
如果 head.val == val,说明当前节点需要被删除,此时返回 head.next,即跳过当前节点,返回下一个节点作为新的链表头(如果当前节点是头节点,那么新的头节点就是原来的第二个节点)。
如果 head.val != val,说明当前节点不需要删除,直接返回 head,保持当前节点在链表中的位置不变。
2. 反转⼀个单链表。 206. 反转链表 - 力扣(LeetCode)
/*** 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 reverseList(ListNode head) {if(head==null){return null;}if(head.next==null){return head;}ListNode cur=head.next;head.next=null;while(cur!=null){ListNode curN=cur.next;cur.next=head;head=cur;cur=curN;}return head;}
}
特殊情况:head 为 null 或只有一个节点时,直接返回 head。
初始化:cur 指向 head 下一个节点,防止数据丢失,head.next 置 null,将头节点设置为末尾节点。
链表反转过程:
进入 while 循环,只要 cur 不为 null,就持续进行反转操作。
在每次循环中,先用 curN 保存 cur 的下一个节点,防止后续丢失节点连接。
然后将 cur 的 next 指针指向 head,实现当前节点与前一个节点的反向连接。
之后更新 head 为当前节点 cur,使 head 始终指向已反转部分的头节点。
最后将 cur 移动到保存的下一个节点 curN,继续下一轮操作。
返回结果:
当 cur 变为 null 时,循环结束,此时 head 指向的就是反转后链表的头节点,直接返回 head。
3. 给定⼀个带有头结点head的⾮空单链表,返回链表的中间结点。如果有两个中间结点,则返回第⼆个中间结点。206. 反转链表 - 力扣(LeetCode)
/*** 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 middleNode(ListNode head) {ListNode kuai=head;ListNode man=head;while(kuai!=null&&kuai.next!=null){kuai=kuai.next.next;man=man.next;}return man;}
}
方法1:可以通过size()方法来求出链表的长度,然后再运用for循环,移动到链表长度/2+1即为题目中所要的节点。
方法2:初始化指针:定义快慢指针 kuai 和 man,都指向链表头节点 head。
移动指针:在 kuai 和 kuai.next 都不为空时,kuai 每次移动两步(kuai = kuai.next.next),man 每次移动一步(man = man.next)。
返回结果:当 kuai 无法再移动两步时,man 指向的就是链表中间节点,返回 man。原理:s=vt
4. 输⼊⼀个链表,输出该链表中倒数第k个结点。面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode)
/*** 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 int kthToLast(ListNode head, int k) {ListNode kuai=head;ListNode man=head;for(int i=0;i<k-1;i++){kuai=kuai.next;}while(kuai.next!=null){kuai=kuai.next;man=man.next;}return man.val;}
}
方法一:同上一题一样,通过size()方法获得链表长度,然后再移动链表长度-k个节点后,所指向的节点就是倒数第k个节点。
方法二: 初始化指针:定义快慢指针 kuai 和 man,都初始指向链表头节点 head。
移动快指针:通过循环让 kuai 指针先移动 k - 1 步。
同步移动快慢指针:当 kuai 的下一个节点不为空时,同时移动 kuai 和 man 指针,直到 kuai 到达链表末尾。
返回结果:此时 man 指向倒数第 k 个节点,返回 man 节点的值。
5. 将两个有序链表合并为⼀个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。21. 合并两个有序链表 - 力扣(LeetCode)
/*** 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 mergeTwoLists(ListNode list1, ListNode list2) {ListNode newH=new ListNode(-1);ListNode tmp=newH;if(list1==null){return list2;}if(list2==null){return list1;}while(list1!=null&&list2!=null){if(list1.val>list2.val){tmp.next=list2;tmp=list2;list2=list2.next;}else{tmp.next = list1;tmp = list1;list1 = list1.next;}}if(list1==null){tmp.next=list2;}if(list2==null){tmp.next=list1;}return newH.next;}
}
- 初始化:创建虚拟头节点
newH
及指针tmp
指向它。 - 特殊情况处理:若
list1
为空,返回list2
;若list2
为空,返回list1
。 - 合并主体逻辑:
- 当
list1
和list2
都不为空,比较当前节点值,小值节点接到tmp
后,tmp
及对应链表指针后移。
- 当
- 处理剩余节点:若
list1
先遍历完,将list2
剩余部分接tmp
后;反之,将list1
剩余部分接tmp
后。 - 返回结果:返回
newH.next
,即合并后链表的真正头节点 。
6. 编写代码,以给定值x为基准将链表分割成两部分,所有⼩于x的结点排在⼤于或等于x的结点之前。链表分割_牛客题霸_牛客网
7. 链表的回⽂结构。 链表的回文结构_牛客题霸_牛客网
import java.util.*;/*
public class ListNode {int val;ListNode next = null;ListNode(int val) {this.val = val;}
}*/public class PalindromeList {public boolean chkPalindrome(ListNode A) {// write code hereif (A == null) {return true;}ListNode kuai = A;ListNode man = A;while (kuai != null && kuai.next != null) {kuai = kuai.next.next;man = man.next;}
ListNode cur = man.next;while (cur!= null) {ListNode curN = cur.next;cur.next = man;man =cur;cur = curN;}while (A != man) {if (A.val != man.val) {return false;}if (A.next == man) {return true;}A = A.next;man = man.next;}return true;}}
空链表处理:若链表头节点为 null,直接返回 true。
找中间节点:用快慢指针,快指针每次走两步,慢指针每次走一步,快指针到头时,慢指针指向中间节点。
反转链表后半部分:
定义指针 cur 指向中间节点的下一个节点,即链表后半部分的起始节点。
在 while 循环中,通过保存当前节点的下一个节点 curN,将当前节点 cur 的 next 指针指向前一个节点 man,然后更新 man 和 cur,实现链表后半部分的反转。
判断是否为回文链表:
使用 while 循环比较链表前半部分和反转后的后半部分。
若对应节点的值不相等,返回 false。
若 A 的下一个节点就是 man,说明链表长度为奇数且是回文链表,返回 true。
循环结束未返回 false,则返回 true
8. 输⼊两个链表,找出它们的第⼀个公共结点。 160. 相交链表 - 力扣(LeetCode)