1、合并两个有序链表
2、合并 k 个有序链表
3、寻找单链表的倒数第 k 个节点
4、寻找单链表的中点
5、判断单链表是否包含环并找出环起点
6、判断两个单链表是否相交并找出交点
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/**
* 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) {
if (list1 == null){
return list2;
}else if (list2 == null){
return list1;
}else if (list1 == null && list2 == null){
return null;
}
ListNode head,p1,p2,p;
if (list1.val < list2.val){
head = list1;
p1 = head.next;
p2 = list2;
}else {
head = list2;
p2 = head.next;
p1 = list1;
}
p = head;
while (p1 != null && p2 != null){
if (p1.val < p2.val){
p.next = p1;
p1 = p1.next;
}else {
p.next = p2;
p2 = p2.next;
}
p = p.next;
}
if (p1 != null){
p.next = p1;
}
if (p2 != null){
p.next = p2;
}
return head;
}
}
23. 合并K个升序链表
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
1->4->5,
1->3->4,
2->6
]
将它们合并到一个有序链表中得到。
1->1->2->3->4->4->5->6
示例 2:
输入:lists = []
输出:[]
示例 3:
输入:lists = [[]]
输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i] 按 升序 排列
lists[i].length 的总和不超过 10^4
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-k-sorted-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/**
* 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 mergeKLists(ListNode[] lists) {
if (lists == null) return null;
// 1. 创建优先队列
PriorityQueue<ListNode> queue = new PriorityQueue<>((l1, l2) -> {
return l1.val - l2.val;
});
// 2. 遍历 所有链表数组节点 入 queue
ListNode p = null;
for (ListNode node:lists){
// node ,拿到每一个数组的头节点
p = node;
while (p!=null){
// 入队
queue.offer(p);
p = p.next;
}
}
// 3. 出队,拿到升序链表
ListNode head = new ListNode(-1); // 虚头节点 ,返回的升序链表的头节点是 head.next
p = head;
while (!queue.isEmpty()){
// 出队
p.next = queue.poll();
p = p.next;
}
p.next = null;
return head.next;
}
}
PriorityQueue,具有优先级别的队列,优先级队列的元素按照它们的自然顺序排序,或者由队列构造时提供的 Comparator 进行排序这取决于哪个构造器。
构造函数 | 描述 |
---|---|
PriorityQueue() | 使用默认的容量(11) 创建一个优先队列,元素的顺序规则采用的是自然顺序 |
PriorityQueue(int initialCapacity) | 使用默认指定的容量创建一个优先队列,元素的顺序规则采用的是自然顺序 |
PriorityQueue(Comparator<? super E> comparator) | 使用默认的容量(11) 创建一个优先队列,元素的顺序规则采用的是 comparator |
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
寻找倒数第 k 个节点:
注意对虚拟头结点的使用,会减少很多边界问题。
使⽤了虚拟头结点的技巧,也是为了防⽌出现空指针的情况,⽐如说链表总共有 5 个节点,
题⽬就让你删除倒数第 5 个节点,也就是第⼀个节点,那按照算法逻辑,应该⾸先找到倒数第 6 个节点。但
第⼀个节点前⾯已经没有节点了,这就会出错。
/**
* 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 removeNthFromEnd(ListNode head, int n) {
// 1. 创建 虚头节点
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 2. 寻找倒数 第 n+1 个节点
ListNode node = findFromEnd(dummy, n + 1);
// 3. 删除 第 n+1 个节点的下一个节点
node.next = node.next.next;
return dummy.next;
}
/**
* 返回链表的倒数第k个节点
* @param head
* @param k
* @return
*/
public ListNode findFromEnd(ListNode head, int k){
ListNode p1 = head,p2 = head;
// 1. p1 走 k 步
for (int i = 0;i < k;i++){
p1 = p1.next;
}
// 2. p2 和 p1 一起走,直到 p1 指向空
while (p1 != null){
p1 = p1.next;
p2 = p2.next;
}
// 此时 p2 就指向 倒数第k个数
return p2;
}
}
876. 链表的中间结点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/middle-of-the-linked-list
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
方法:使用快慢指针,快指针 fast 走两步,slow 慢指针 走一步。
如果链表⻓度为偶数,也就是说中点有两个的时候,返回的节点是靠后的那个节点。
/**
* 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 slow = head,fast = head;
while (fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
}
return slow;
}
}
判断链表是否包含环
方法:快慢指针
每当慢指针 slow 前进⼀步,快指针 fast 就前进两步。
如果 fast 最终遇到空指针,说明链表中没有环;如果 fast 最终和 slow 相遇,那肯定是 fast 超过了
slow ⼀圈,说明链表中含有环。
分析:假设链表是一个环,有 n 个节点,慢指针 一次走1步,快指针 一次走 2步,假设快慢指针 x 次后相遇,(2x-x)%n = 0, x % n = 0,x 最小值 取 n,如果有环快慢指针经过 x 次后一定会相遇。(有问题)
public ListNode middleNode(ListNode head) {
ListNode slow = head,fast = head;
while (fast != null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if (slow == fast) { // 快慢指针相遇,说明含有环
return true;
}
}
return false;
}
141. 环形链表
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。
注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
例1图:
例2图:
例3图:
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode slow = head,fast = head;
while (fast != null && fast.next != null){
// 慢指针走一步,快指针走两步
fast = fast.next.next;
slow = slow.next;
// 快慢指针 相遇,包含环
if (slow == fast){
return true;
}
}
return false;
}
}
已知链表中含有环,返回这个环的起始位置
前面一题 判断链表是否包含环,快慢指针相遇后,返回 true,在本题 求环的起点 问题中,快慢指针相遇 不需要返回,退出 while 循环,让 慢指针 重新指向 head,然后 快慢指针 同时一次走一步,再次相遇时的点就是 环的起点。
ListNode cycleStart(ListNode head){
ListNode slow = head,fast = head;
while (fast != null && fast.next != null){
slow = slow.next;
fast = fast.next.next;
if (fast == slow){
break;
}
}
slow = head;
while (slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
142. 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。
如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。
如果 pos 是 -1,则在该链表中没有环。
注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle-ii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
图:
1
2
3
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode slow = head,fast = head;
int k = 0;
while (fast != null && fast.next != null){
// 慢指针走一步,快指针走两步
fast = fast.next.next;
slow = slow.next;
// 快慢指针 相遇,包含环
if (slow == fast){
k = 1; // 有环的标志
break;
}
}
if (k == 0){
// 没有环
return null;
}else {
// 寻找环的起点 并返回
slow = head;
while (slow != fast){
slow = slow.next;
fast = fast.next;
}
return slow;
}
}
}
两个链表是否相交
解决办法:
用 p1,p2 分别遍历 A,B 。
p1 遍历完链表 A 之后开始遍历链表 B,让 p2 遍历完链表 B 之后开始遍历链表 A
p1,p2 相等时就到达相交节点c1。
如果A,B,没有交点,要返回 null,上述方法同样适用。
160. 相交链表
给你两个单链表的头节点 headA 和 headB ,
请你找出并返回两个单链表相交的起始节点。
如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/intersection-of-two-linked-lists
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
ListNode p1 = headA,p2 = headB;
while (p1 != p2){
if (p1 == null) p1 = headB;
else p1 = p1.next;
if (p2 == null) p2 = headA;
else p2 = p2.next;
}
return p1;
}
}