AC207-将两个递增有序的单链表 B 合并到 A 中,仍保持非递减有序¶
代码实现¶
代码逻辑解析¶
- 初始化指针:
ra指向链表 A 的头节点,用于构建合并后的链表。p指向链表 A 的第一个实际节点。-
q指向链表 B 的第一个实际节点。 -
双指针遍历与合并:
- 使用
while循环同时遍历链表 A 和 B,直到其中一个链表被完全遍历完。 -
在每次循环中比较
p->data和q->data:- 如果
p->data <= q->data,将p接入合并链表,并移动p和ra; - 否则,将
q接入合并链表,并移动q和ra。
- 如果
-
处理剩余节点:
-
如果链表 A 或链表 B 还有未处理的节点,直接将其接到合并链表的末尾。
-
释放链表 B 的头节点:
- 因为链表 B 的头节点已经没有实际用途,且其内容可能被合并到链表 A 中,因此释放其内存。
时间复杂度分析¶
- 遍历链表 A 和 B:
- 假设链表 A 的长度为
m,链表 B 的长度为n。 - 每次循环都会处理一个节点(来自 A 或 B),因此总的循环次数为
m + n。 -
时间复杂度为 O(m + n)。
-
其他操作:
- 初始化、剩余节点处理和释放头节点的操作均为 O(1)。
综上,时间复杂度为 O(m + n)。
空间复杂度分析¶
- 该算法只使用了常量级别的额外空间(如变量
ra,p,q),没有使用与输入规模相关的额外空间。 - 因此,空间复杂度为 O(1)。
较难理解部分的说明¶
为什么需要 ra 指针?¶
ra是一个辅助指针,用于记录当前合并链表的最后一个节点。- 它的作用是确保每次都能正确地将新的节点接入合并链表,避免断链或错误连接。
图解说明¶
假设链表 A 和 B 如下:
初始状态:
-
第一次循环:
p->data = 1,q->data = 2,p->data < q->data,将p接入合并链表。 -
第二次循环:
p->data = 3,q->data = 2,p->data > q->data,将q接入合并链表。 -
第三次循环:
p->data = 3,q->data = 4,p->data < q->data,将p接入合并链表。 -
第四次循环:
p->data = 5,q->data = 4,p->data > q->data,将q接入合并链表。 -
第五次循环:
p->data = 5,q->data = 6,p->data < q->data,将p接入合并链表。 -
最后处理剩余节点:
q不为空,将q接入合并链表。Text Only
最终结果:
| Text Only | |
|---|---|
延伸知识点¶
1. 双指针法的应用¶
双指针法广泛应用于有序数组或链表的合并问题,例如: - 合并两个有序数组。 - 合并多个有序链表(可以扩展为优先队列)。
2. 链表头节点的作用¶
- 链表的头节点通常是一个空节点,用于简化插入和删除操作。
- 在本例中,
A的头节点充当了合并链表的起点。
3. 相关算法问题¶
变体问题:¶
-
如果要求合并后的链表是递减有序的,如何修改算法?
-
如果链表 A 和 B 中可能存在重复元素,是否需要去重?如何实现?
应用场景:¶
- 数据库中的有序数据合并。
- 文件系统的块合并操作。
- 实时数据流的排序与合并。
通过这种双指针方法,可以在原地完成链表的合并,既高效又节省空间。