Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 合并两个排序列表将返回运行时错误_Python_List_Sorting_Merge - Fatal编程技术网

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