def next_node(first,second,cur):
'''
对比两个指针当前所在节点的大小:小的节点加入到新链中,并向后移动指针
Args:
first:
second:
cur:新链到当前节点
Returns:
'''
if first.val <= second.val:
cur.next = first
cur = cur.next
first = first.next
else:
cur.next = second
cur = cur.next
second = second.next
return first,second,cur
def mergeTwoList(l1:ListNode,l2:ListNode)->ListNode:
# 如果l1或l2不存在返回另一个
if not l1:
return l2
if not l2:
return l1
if not l1 and not l2:
return None
first = l1
second = l2
head = None
# 确定新链到头节点
if first.val <= second.val:
head = first
first = first.next
else:
head = second
second = second.next
cur = head
# 只要两个指针没有走到链条到终点,就一直将较小到放入新链
while first and second:
first,second,cur = next_node(first,second,cur)
# 当一个链中所有节点都加入到新链后,另一个链就将剩下到节点一下加入新链
# if first:
# cur.next= first
#
# if second:
# cur.next = second
# 换成三目运算
cur.next = first if first else second
return head
共以下 87 个回答
堆排序写法
时间复杂度O(m+n)
空间复杂度O(1)
归并思想
代码如下
时间o(m+n),空间o(1)
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode p = dummy;
while(l1!=null&&l2!=null){
if(l1.val>l2.val){
p.next=l2;
p=p.next;
l2=l2.next;
}
else if(l1.val<=l2.val){
p.next=l1;
p=p.next;
l1=l1.next;
}
}
//给边界情况打个补丁或者说循环跳出后的操作
if(l1!=null){
p.next=l1;
}
if(l2!=null){
p.next=l2;
}
//当然是返回哑节点后的所有部分啦
return dummy.next;
}
}
复杂度:
时间复杂度:O(m+n),空间复杂度O(1)
时间复杂度O(m+n)
空间复杂度O(1)
1、构造一个空节点temp,当两个输入节点均不为nullptr时,可比较,temp指向的下一节点为值较小的那个,temp++,同时那个节点也++
2、当有一个输入节点为nullptr时,temp下一节点为另一个输入节点。
3、时间复杂度O(n),空间复杂度O(n)
时间复杂度 n+m
空间复杂度 1
//典型的双指针
class Solution {val){
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* dummy=new ListNode(0,nullptr);
ListNode* a=l1, * b=l2,* pre=dummy;
while(a && b){
if(a->val
pre->next=a;
a=a->next;
}
else{
pre->next=b;
b=b->next;
}
pre=pre->next;
pre->next=nullptr;
}
if(a) pre->next=a;
if(b) pre->next=b;
return dummy->next;
};
时间复杂度O(m+n)
空间复杂度O(1)
刚在题解中看到递归解法,还不太理解,原理相同
迭代,但写的还是有的冗余了,有待优化
当你觉得自己的代码有点冗余的时候,最好的学习方法就是看一下别人怎么写的哦,然后模仿他们优雅的写法。
解法一:递归
时间复杂度:O(m+n),其中m为l1长度,n为l2长度
空间复杂度:O(m+n)
解法二:非递归
时间复杂度:O(m+n)
空间复杂度:O(1)
时间复杂度:O(m+n)
空间复杂度:O(1)
时间复杂度O(n+m)
空间复杂度O(1)
时间复杂度o(n+m)
空间复杂度o(1)
时间复杂度O(n+m)
空间复杂度为O(1)
时间复杂度O(n+m)
空间复杂度为O(1)
● 时间复杂度: O(n)
● 空间复杂度: O(1)
1,我先判断,如果有一个链表为空,就输出另一个链表。然后再开始逐个比对,最后若有剩余的链表,新的链表就指向剩余的链表。
2,时间 复杂度为O(n+m)空间复杂度为O(1)
“`java
class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
ListNode merge = new ListNode(0);
ListNode temp = merge;
if(l1 == null && l2==null){
return null;
}
if(l1==null && l2!=null){
return l2;
}
if(l2 ==null&& l1 !=null){
return l1;
}
while(l1 !=null && l2!=null){
if(l1.val < l2.val){ temp.next = l1; l1 = l1.next; }else{ temp.next = l2; l2 = l2.next; } temp = temp.next; } temp.next = l1==null? l2:l1; return merge.next; } } //时间复杂度O(L1.length+l2.length) 空间复杂度O(1)
比较麻烦,麻烦在了一堆特殊情况处理,也是后期要继续学习提高的点,怎么在算法实现中考虑到这些边界情况。由于链表都是有序的,那么就顺序遍历逐对比较就可以了。
存在两个变量,时间复杂度O(m+n)
空间复杂度O(1)
思路:
l1 l2都是从小到大递增的,直接按顺序依次比较,小的放到结果链表
思路:通过比较 l1.val ? l2.val ,谁更小就先放进merge中
时间复杂度 :O(M+N)
空间复杂度 :O(1)
思路:
创建一个虚拟头结点 dummy 来拼接合并后的链表,cur 代表当前合并链表的末尾位置,同时复用入参的 l1 和 l2 来进行构造。
当 l1 和 l2 均不为空的时候,找到两者中的较小值拼接到 cur 的后面,若 l1 和 l2 任一为空,则将另外的链表拼接完。
时间:O(n+m)
空间:O(1)
此题码住,不想新增额外的节点,但是逻辑变得异常混乱,有时候想太多反而达不到目的。
解题思路:
1.比较两个链表的头节点的值,其中头节点较小的值是合并后链表的头节点,,重复步骤。
2.要考虑到两个链表是否为空的情况,这样在相应的判刑下返回出相应的值
时间复杂度O(m+n)
空间复杂度O(m+n)
时间复杂度:O(N+M) 取决于两个链表长度
空间复杂度:O(1)
“cpp
class Solution {
public:
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
ListNode *head = nullptr;
auto **cur = &head;
if (!l1) return l2;
if (!l2) return l1;
while (true) {
if (l1->val < l2->val) {
*cur = l1;
cur = &l1->next;
l1 = l1->next;
if (!l1) {
*cur = l2;
return head;
}
} else {
*cur = l2;
cur = &l2->next;
l2 = l2->next;
if (!l2) {
*cur = l1;
return head;
}
}
}
}
}
“`
时间复杂度 O(n)
空间复杂度 O(1)
思路:就是顺序比较两个链表中节点的大小,按递增的顺序返回一个链表。
递归实现(原链表上进行操作,时间复杂度O(N),空间复杂度O(N)(递归会有栈开销),N是两个链表的总长度)
迭代实现(创建一个新的虚拟头节点,直接在l1、l2上排序,时间复杂度O(N)(N为两个链表的长度),空间复杂度O(1)(用到了一个虚拟头节点))
“`
时间复杂度O(n)
空间复杂度O(n+m)
// 创建虚拟头结点,遍历l1,l2比较节点值大小,检查没遍历完的链表,返回
时间复杂度:O(n)
空间复杂度:O(1)
时间 O(n)
因为复用原链表的结点,所以空间 O(1)
思路:
1. 先判断这两个链表有没有空链表
2. 创建一个虚拟头节点(dummyHead), 并用指针cur指向这个虚拟头节点
3. 遍历list1和list2 并比较节点值的大小
4.跳出循环后指针cur下一个指向的节点是list1和list2当中在上一次循环遍历后还没到链表尾部的链表。
5. 返回虚拟头节点的next指针。
时间复杂度 O(n)
空间复杂度O(1)
额
遍历两个链表,比较结点大小合并
以上为虚头结点+非递归
以上为虚节点+递归
定义两个新的头结点,l1不大于l2,就把l1插入,或者插入l2
最后肯定有个为空,就把另一个不为空的插入新链表的尾结点
时间复杂度:O(n+m) 两链表之长
空间复杂度:O(1)
首先输出特殊情况,当l1为空时,返回l2。当l2为空时,返回l1
1、定义一个值为-1的头节点,在定义一个指针指向头节点
2、当l1和l2同时不为空时,判断l1和l2的值大小,进行链表的构造
3、由于两个链表不一样长,可能会出现l1和l2其中一个为空的情况,此时将剩下的节点拼到构造的链表后边就行
时间复杂度O(n) 空间复杂度O(1)
代码
时间复杂度:O(n)
空间复杂度:O(1)
双指针+给l1创建一个虚拟头节点
时间复杂度O(n+m) nm为两个链表的长度,空间复杂度O(1)
时间复杂度 O(n)
时间复杂度O(l1+l2)
空间复杂度O(1)
时间O(n+m), 空间O(1)
思路:穿针引线
递归
还是要创建好虚拟节点,然后注意判断循环的结束与开始
下面是代码
对python语法不熟悉,and和or在while的关系
第一天第三题
/*
* 双指针法
*/
while循环判断刚开始写的l1.next!=null,l2.next!=null,错了
该题可以用两种办法,一种新建个表头轮流插入
另一种采用递归的思想,面试官应该比较喜欢这种解法
“`
递归
迭代
类似归并排序的merge操作
以前也写过链表上的归并排序,在链表上不用申请额外空间去进行merge操作,使用自下而上的归并排序可以只用O(1)的空间复杂度
来烧脑
归并
注意虚拟头结点的使用,方便返回结果
构造一个虚拟头节点,注意最后剩余节点的处理方式