剑指 Offer 25. 合并两个排序的链表
题目描述
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。
限制:
- 0 < = 链 表 长 度 < = 1000 0 <= 链表长度 <= 1000 0<=链表长度<=1000
双指针
思路:采用归并排序思想
- 设指针
p
、q
分别指向链表 l 1 l1 l1 和 l 2 l2 l2 当前待处理节点; - 每次比较
p.val
和q.val
,将较小者对应的节点添加到 新链表中; - 若经过一轮比较后(即,退出
while
时),l1
或l2
中仍然有剩余节点(二者只会有一个有剩余),则将剩余节点“接到”新链表 末尾。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode p = l1;
ListNode q = l2;
ListNode ansList = new ListNode(); // 虚拟头结点\
ListNode cnt = ansList; // 新链表的当前节点
while (p != null && q != null) {
if (p.val < q.val) {
cnt.next = p;
cnt = cnt.next;
p = p.next;
} else {
cnt.next = q;
cnt = cnt.next;
q = q.next;
}
}
// 剩余部分直接修改指针,即可
if (p != null) cnt.next = p;
if (q != null) cnt.next = q;
return ansList.next;
}
}
- 时间复杂度: O ( m + n ) O(m + n) O(m+n)
- 空间复杂度:
O
(
1
)
O(1)
O(1)
递归 🔥
参考 题解(注意动画演示)
思路 🤔
- 终止条件
- 如果 l 1 l1 l1 为空,则返回 l 2 l2 l2;
- 如果 l 2 l2 l2 为空,则返回 l 1 l1 l1;
- 递归体
比较两个节点,使用二者 v a l val val 较小者,继续递归。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
// 终止条件
if (l1 == null) return l2;
if (l2 == null) return l1;
// 递归体
if (l1.val < l2.val) {
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else {
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
}
}
剑指 Offer 52. 两个链表的第一个公共节点
题目描述
输入两个链表,找出它们的第一个公共节点。
注意:
- 如果两个链表没有交点,返回 n u l l null null.
- 在返回结果后,两个链表仍须保持原有的结构。
- 可假定整个链表结构中没有循环。
- 程序尽量满足 O ( n ) O(n) O(n) 时间复杂度,且仅用 O ( 1 ) O(1) O(1) 内存。
哈希
思路:
- 使用
set
保存headA
所有节点; - 遍历
headB
中所有节点 q:- 若 q 在
set
中,则说明 q 为为 两个链表的“交点”; - 否则,如果所有 q 都不在 s e t set set,说明二者无“交点”
- 若 q 在
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
Set<ListNode> set = new HashSet<>();
ListNode p = headA;
// 将 headA 所有节点添加到 set
while (p != null) {
set.add(p);
p = p.next;
}
ListNode q = headB;
while (q != null) {
if (set.contains(q)) {
return q;
}
q = q.next;
}
return null;
}
}
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度:
O
(
n
)
O(n)
O(n)
双指针 ⭐️
使用“哈希表”的解法可以进一步优化,将空间复杂度降为 O ( 1 ) O(1) O(1).
思路 🤔
- 分别求两个链表的长度;
- 保证
headA
是长度更长的那个; - “尾对齐”:将 headA 向后移动 l e n A − l e n B lenA - lenB lenA−lenB 个节点(保证两个链表“尾对齐”)
- 双指针
h
e
a
d
A
headA
headA、
h
e
a
d
B
headB
headB 同时后移
- 若
headA == headB
,说明找到“交点”; - 否则,无“交点”
- 若
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 1、求两个链表长度
int lenA = getLength(headA);
int lenB = getLength(headB);
// 2、保证headA是短链表
if (lenA > lenB) {
// 交换长度
lenA = lenA ^ lenB;
lenB = lenA ^ lenB;
lenA = lenA ^ lenB;
// 交换链表
ListNode temp = headA;
headA = headB;
headB = temp;
}
// 3、对齐尾节点:headB向后移动(lenB - lenA)个节点
int offset = lenB - lenA;
while (offset-- > 0) {
headB = headB.next;
}
// 4、同时遍历headA、headB
while (headB != null) {
if (headA == headB) {
return headB;
}
headA = headA.next; // 同时后移
headB = headB.next;
}
return null; // 无交点
}
int getLength(ListNode head) {
int len = 0;
while (head != null) {
len++;
head = head.next;
}
return len;
}
}
- 时间复杂度: O ( n ) O(n) O(n)
- 空间复杂度:
O
(
1
)
O(1)
O(1)