这是我参与11月更文挑战的第 15 天,活动详情查看:2021最后一次更文挑战
前言
在数据结构和算法中,链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表有很多种不同的类型:单向链表,双向链表以及循环链表。链表可以在多种编程语言中实现。
链表由一系列结点(节点)组成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间。
下面我们通过一道 LeetCode 题目来了解链表的特点以及应用。
题目
将两个升序链表
合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
提示:
-
两个链表的节点数目范围是
[0, 50]
-
-100 <= Node.val <= 100
-
l1
和l2
均按 非递减顺序 排列
思考
对于这道题,首先可以想到的是我们逐个去比较两个链表的节点,每次取出较小的节点,然后将剩下的节点继续进行比较。思路不是很难,那么代码应该怎么写呢?我们可以借助递归去书写代码。
此外,要考虑边界情况。如果存在空链表的情况,那么我们只需要返回非空链表即可。如果两个链表中有一个为空,递归结束。
下面是具体的代码。
解答
递归
/**
* @author 觅迹
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
var mergeTwoLists = function(l1, l2) {
if (l1 === null) { // l1节点为空
return l2;
} else if (l2 === null) { // l2节点为空
return l1;
} else if (l1.val < l2.val) { // 取出较小的节点
l1.next = mergeTwoLists(l1.next, l2);
return l1;
} else { // 取出较小的节点
l2.next = mergeTwoLists(l1, l2.next);
return l2;
}
};
复杂度分析:
-
时间复杂度:O(n + m),其中 n 和 m 分别为两个链表的长度。
-
空间复杂度:O(n + m)。