最近在研究一些算法为蓝桥杯和毕业时的面试做准备,以及增加自己在数据结构方面的理解
合并两个有序链表
首先是创建一个链表类
class ListNode():
def __init__(self,val,next=None):
self.val=val
self.next=next
1.非递归的写法
将两个链表的头部进行比较从小到大相连
#非递归写法
def fun(l1,l2):
# 将p1,p2的第一个元素进行比较
# 循环结束的条件是,p1或p2没有元素了
duixiang = ListNode(0)
tail=duixiang
while l1 and l2: # 等价于p1!=None and p2!=None
if l1.val > l2.val:
# 能进来说明p2的值小
t = ListNode(l2.val)
tail.next=t
tail=t
l2=l2.next
else:
t = ListNode(l1.val)
tail.next=t
tail=t
l1=l1.next
# 出来的时候可能p1或p2可能有剩余
if l1:
tail.next=l1
else:
tail.next=l2
duixiang = duixiang.next
return duixiang
可以看到上述代码,我额外创建了一个临时变量t进行数据的链接,这方便了我当时写代码时的理解,但无疑会增加内存的消耗,下面时改进后的
只是少了一个t的变量,空间复杂度为o(1)
def fun(l1,l2):
# 将p1,p2的第一个元素进行比较
# 循环结束的条件是,p1或p2没有元素了
duixiang = ListNode(0)
tail=duixiang
while l1 and l2: # 等价于p1!=None and p2!=None
if l1.val > l2.val:
# 能进来说明p2的值小
tail.next=l2
tail=l2
l2=l2.next
else:
tail.next=l1
tail=l1
l1=l1.next
# 出来的时候可能p1或p2可能有剩余
if l1:
tail.next=l1
else:
tail.next=l2
duixiang = duixiang.next
return duixiang
p=ListNode(0)
l1=p
print(l1)
p=ListNode(1) l1是没有改变的
print(l1)
<__main__.ListNode object at 0x000001B38B5DBFD0>
<__main__.ListNode object at 0x000001B38B5DBFD0>
p=ListNode(0)
l1=p
print(l1.next)
p.next=ListNode(1) 但对于p的操作是会影响到l1的
print(l1.next)
这样我们就可以看到,在python代码中的赋值是的到了一个名字不同,本质相同的’人’,对任意一个操作是会影响到所有与他相同的变量的,但当p被赋值进行了改变之后,虽然他仍然叫p但与之前是两个人
tail.next=l2
tail=l2
l2=l2.next 所以tail不是以前的tail l2不是以前的l2
2.递归写法
记住递归三要素:
1.基本结束条件
2.向基本结束条件演进
3.不断的调用自身
****一定要记住
如果有函数与函数之间的相加,或者其他的一些东西总而言之就是如果你写的递归报错了首先想一下这个递归代码需不需要return
#首先有个基本结束条件
if l1==None:
#如果l1为None,则l2一定有剩余,返回l2的值
return l2
elif l2==None:
#如果l2为None,则l1一定有剩余,返回l1的值
return l1
else:
if l1.val>l2.val:
l2.next =self.mergeTwoLists(l1, l2.next)
#如果我第一次是进来这个,而我不retrun,我怎么会有返回值呢
return l2
else:
l1.next=self.mergeTwoLists(l1.next,l2)
return l1
现在做一个小小的总结,可以看到递归和迭代的时间复杂的是o(m+n)
m,n分别为l1,l2的长度,这是不可避免地,因为你不能说不比较就直接连起来
而递归有一个栈的开销,空间复杂度会变为o(m+n),而迭代就是o(1)会好一些