Python 合并两个排序列表将返回运行时错误
我正在尝试解决一个问题,该问题要求合并两个已排序的链表。虽然我的代码通过了大多数测试,但对于大输入、很长的链表,它做得并不好。我能改进什么?谢谢 这是我的代码:Python 合并两个排序列表将返回运行时错误,python,list,sorting,merge,Python,List,Sorting,Merge,我正在尝试解决一个问题,该问题要求合并两个已排序的链表。虽然我的代码通过了大多数测试,但对于大输入、很长的链表,它做得并不好。我能改进什么?谢谢 这是我的代码: # # For your reference: # # SinglyLinkedListNode: # int data # SinglyLinkedListNode next # # def mergeLists(head1, head2): if head1 is None: return h
#
# For your reference:
#
# SinglyLinkedListNode:
# int data
# SinglyLinkedListNode next
#
#
def mergeLists(head1, head2):
if head1 is None:
return head2
if head2 is None:
return head1
p1=head1
p2=head2
while head1.data >= p2.data:
nxt = p2.next
p2.next = head1
head1 = p2
p2 = nxt
p1 = head1
while p1 is not None and p2 is not None:
if p1.data <= p2.data and (p1.next is None or p1.next.data >= p2.data):
next2 = p2.next
p2.next = p1.next
p1.next = p2
p2 = next2
p1 = p1.next
return head1
#
#供参考:
#
#SingleLinkedListNode:
#整数数据
#SingleLinkedListNode下一步
#
#
def合并列表(标题1、标题2):
如果head1为无:
返回头2
如果head2为无:
返回头1
p1=头1
p2=头2
当head1.data>=p2.data时:
nxt=p2.next
p2.next=头1
头1=p2
p2=nxt
p1=头1
p1不是无,p2不是无:
如果p1.data=p2.data):
next2=p2.next
p2.next=p1.next
p1.next=p2
p2=next2
p1=p1.next
返回头1
以下是通过构建新列表而不是修改现有节点来执行合并操作的方法:
def merge(p1, p2):
while p1 is not None and p2 is not None:
if p1.data < p2.data:
yield p1.data
p1 = p1.next
else:
yield p2.data
p2 = p2.next
while p1 is not None:
yield p1.data
p1 = p1.next
while p2 is not None:
yield p2.data
p2 = p2.next
def mergeLists(p1, p2):
return list(merge(p1, p2))
def合并(p1,p2):
p1不是无,p2不是无:
如果p1.data
(您也可以将.append()
添加到临时列表中,而不是yield
。)
如果必须修改节点,则需要更加小心,不要修改任何不应该修改的内容。尝试使用我建议的方法作为模板。看起来你要做很多重复的作业,以及重复的检查。我试图用最少的两种语言编写代码。当然,如果目标是编写易于阅读的代码,那么递归算法将是最佳解决方案。但我们的情况并非如此。还要注意的是,同时检查两个列表上的哨兵是没有意义的,因为列表的长度是未知的,并且在任何时候都可能不同
class L(object):
__slots__ = 'data', 'next'
def __repr__(self): # just for tests
return f'{self.data!r}, {self.next!r}'
def __eq__(self, other): # just for tests
return (self.data == other.data) and (self.next == other.next)
def merge0(a: L, b: L, sentinel=None):
if a is sentinel:
return b
if b is sentinel:
return a
if a.data > b.data:
head = last = b
b = b.next
else:
head = last = a
a = a.next
while True:
if a.data > b.data:
last.next = last = b
if (b := b.next) is sentinel:
last.next = a
return head
else:
last.next = last = a
if (a := a.next) is sentinel:
last.next = b
return head
上述代码使用6个主要指针进行操作:
-第一个链表的标题<代码>a。下一个可以是a
李>哨兵
-第二个链表的标题<代码>b。下一个可以是b
李>哨兵
-新合并链接列表的标题。它是标题
或a
。在主循环期间不可用李>b
-新合并链表的当前最后一个对象。最初是last
李>头部
-此指针在主循环期间未定义。它可以是last.next
,a
,b
或a.next
李>b.next
-标记任何链接列表结尾的唯一单例对象sentinel
last.next
。然后我们创建新的last
,并用这个元素检查列表的可能结尾。然后循环重复
然而,这仍然不是一个最优算法。假设我们有ll1(0,2,3,5,5,7,8)
,ll2(6,7,9,10)
和合并ll(0,2,3,5,5,6,7,7,8,9,10)
。实际上,ll
的前5个元素是ll1
的五个元素。根据merge0
main循环,这些元素中的每一个(第一个除外)都已重新分配给其左邻居。当合并列表的值分布较大时,这种方法的低效性尤其明显
其次,最好是替换每个列表的哨兵的显式检查。sentinel
对象不必是None
,可以通过本机异常抛出机制实现为自定义对象
改进版:
@object.__new__
class sentinel(object):
__slots__ = ()
@property
def next(self):
raise self.Signal()
@property
def data(self):
raise self.Signal()
class Signal(Exception):
pass
def __eq__(self, other): # just for tests
return (other is self) or (other is None)
def merge1(a: L, b: L, sentinel=sentinel):
if a is sentinel:
return b
if b is sentinel:
return a
if a.data <= b.data:
head = x = a
y = b
else:
head = x = b
y = a
try:
while True:
while (x := (last := x).next).data <= y.data:
pass
last.next = y
while (y := (last := y).next).data <= x.data:
pass
last.next = x
except sentinel.Signal:
last.next = y if (x is sentinel) else x
return head
一些测试:
>>> j0 = random_sorted_ll(length=100, min_num=4, span=5)
>>> j1 = copy_ll(j0)
>>> j2 = copy_ll(j0, prev_sentinel=None, new_sentinel=sentinel)
>>> k0 = random_sorted_ll(length=111, min_num=4, span=5)
>>> k1 = copy_ll(k0)
>>> k2 = copy_ll(k0, prev_sentinel=None, new_sentinel=sentinel)
>>> (j0 == j1 == j2) and (k0 == k1 == k2)
True
>>> m0 = mergeLists(j0, k0)
>>> m1 = merge0(j1, k1)
>>> m2 = merge1(j2, k2)
>>> m0 == m1 == m2
True
>>> (m2 is j2) or (m2 is k2)
True
比较:
def compare_speed(len0, len1, mn0, mn1, span0, span1):
print(f'\nll in an approximate range ({mn0:_d}, {len0*span0//2+mn0:_d}) with {len0=:_d}')
j0 = random_sorted_ll(len0, mn0, span0)
j1 = copy_ll(j0)
j2 = copy_ll(j0, None, sentinel)
print(f'll in an approximate range ({mn1:_d}, {len1*span1//2+mn1:_d}) with {len1=:_d}')
k0 = random_sorted_ll(len1, mn1, span1)
k1 = copy_ll(k0)
k2 = copy_ll(k0, None, sentinel)
from timeit import timeit
print('mergeLists:', timeit((lambda: mergeLists(j0, k0)), number=1), 'sec')
print('merge0:', timeit((lambda: merge0(j1, k1)), number=1), 'sec')
print('merge1:', timeit((lambda: merge1(j2, k2)), number=1), 'sec')
compare_speed(1_111_111, 1_000_000, 0, 0, 5, 5)
compare_speed(1_111_111, 1_000_000, 0, -25_000_000, 5, 100)
compare_speed(9_000_000, 9_111_111, 0, 0, 5, 5)
我的结果:
ll in an approximate range (0, 2_777_777) with len0=1_111_111
ll in an approximate range (0, 2_500_000) with len1=1_000_000
mergeLists: 0.46664454099999997 sec
merge0: 0.2180013780000003 sec
merge1: 0.16215903499999996 sec
ll in an approximate range (0, 2_777_777) with len0=1_111_111
ll in an approximate range (-25_000_000, 25_000_000) with len1=1_000_000
mergeLists: 0.4489969129999736 sec
merge0: 0.16649408400007815 sec
merge1: 0.1128580580000289 sec
ll in an approximate range (0, 22_500_000) with len0=9_000_000
ll in an approximate range (0, 22_777_777) with len1=9_111_111
mergeLists: 4.245676940000003 sec
merge0: 1.9906823259999982 sec
merge1: 1.548923153000004 sec
确切的完全错误是什么?@zerkms我正在一个站点上解决这个问题,“运行时错误”是提供的唯一信息
ll in an approximate range (0, 2_777_777) with len0=1_111_111
ll in an approximate range (0, 2_500_000) with len1=1_000_000
mergeLists: 0.46664454099999997 sec
merge0: 0.2180013780000003 sec
merge1: 0.16215903499999996 sec
ll in an approximate range (0, 2_777_777) with len0=1_111_111
ll in an approximate range (-25_000_000, 25_000_000) with len1=1_000_000
mergeLists: 0.4489969129999736 sec
merge0: 0.16649408400007815 sec
merge1: 0.1128580580000289 sec
ll in an approximate range (0, 22_500_000) with len0=9_000_000
ll in an approximate range (0, 22_777_777) with len1=9_111_111
mergeLists: 4.245676940000003 sec
merge0: 1.9906823259999982 sec
merge1: 1.548923153000004 sec