这是一道leetcode伤的题目,在答完题后觉得有必要整理一下算是记录也算是分享。
合并两个有序列表_leetcode
这里主要有两种方法,一种是迭代,另一种是递归。
首先区分一下二者
迭代:每次输出的结果作为下一次的输入,思路在于逐渐逼近,使用新值来覆盖旧值,直到满足条件后结束,不保存中间值,空间利用率高。常用for while 循环结构
递归:自己调用自己(比如斐波纳切数列这种),将一个分解为若干相对小一点的问题,遇到递归出口再原路返回,因此必须保存相关的中间值,这些中间值压入栈中保存,问题规模较大时会占用大量内存。常用if来判断
递归方法
时间复杂度:O(m+n)
原因:和算法调用次数有关,不会超过两个链表的长度之和。
空间复杂度:O(m+n)
原因:递归调用时需要消耗栈空间(Java会为每个函数构建一个栈用于回溯),栈空间的大小取决于递归调用的深度,结束递归调用时,函数最多调用(m+n)次,因此空间复杂度为O(m+n)。
//递归方法
//调用自身的方法
public static ListNode mergeTwoLists(ListNode l1, ListNode l2) {
//首先判断 l1 和 l2 是否为null
if (l1 == null) {
return l2;
} else if (l2 == null) {
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(m+n)
原因:这里while的循环次数不会超过两个链表的节点次数之和m+n,故时间复杂度为O(m+n)。
空间复杂度:O(1)
原因:这里只是使用了常数的空间存放若干变量
//迭代方法
//利用已知来推未知
public static ListNode mergeTwoLists2(ListNode l1, ListNode l2) {
//构建哑节点 用于存储最后的结果
ListNode preHead = new ListNode(-1);
//同时引入动态节点用来动态添加每次比较厚的结果
ListNode prev = preHead;
//迭代停止的条件是其中一个链表遍历完
while (l1 != null && l2 != null) {
//这里的if语句用于判断每个链表中最小的的那个头节点 并将该头节点作为动态节点
//的next
if (l1.val <= l2.val) {
prev.next = l1;
l1 = l1.next;
} else {
prev.next = l2;
l2 = l2.next;
}
//每次比较后,更新一下动态节点的位置
prev = prev.next;
}
//合并后 l1 和 l2最多只有一个还未被合并完 将链表末尾指向未合并完的链表即可
prev.next = l1 == null ? l2 : l1;
//返回 哑节点的下一个节点 作为最终结果
return preHead.next;
}
因此使用迭代的方法可以在空间利用率上有更好的效果