Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/357.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/10.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 反向打印小于O(n)空间的不可变链表_Python_Algorithm_Python 2.7_Linked List - Fatal编程技术网

Python 反向打印小于O(n)空间的不可变链表

Python 反向打印小于O(n)空间的不可变链表,python,algorithm,python-2.7,linked-list,Python,Algorithm,Python 2.7,Linked List,解决这个问题,我的想法是进行递归,在每次递归过程中,反向打印linkedlist的下半部分,然后反向打印linkedlist的上半部分。因此,额外的空间是O(logn)——这是用于递归堆栈的额外空间,但对于时间(O(nlogn)它大于O(n)-递归的每个(logn)级别上的组合调用迭代整个列表,将每个部分切成两半) 是否有算法可以实现相同的目标——反向打印一个不可变的单链表,其空间小于O(n),时间最多为O(n) 源代码(Python 2.7): 就这个问题的空间/时间要求而言,有两个极端: O

解决这个问题,我的想法是进行递归,在每次递归过程中,反向打印linkedlist的下半部分,然后反向打印linkedlist的上半部分。因此,额外的空间是
O(logn)
——这是用于递归堆栈的额外空间,但对于时间(O(nlogn)它大于O(n)-递归的每个(logn)级别上的组合调用迭代整个列表,将每个部分切成两半)

是否有算法可以实现相同的目标——反向打印一个不可变的单链表,其空间小于O(n),时间最多为O(n)

源代码(Python 2.7):


就这个问题的空间/时间要求而言,有两个极端:

  • O(n)空间,O(n)时间
  • O(1)空间,O(n^2)时间
  • 既然您不关心O(n)空间解决方案,那么让我们看看另一个:

    def reverse_print(LL):
        length = 0
        curr = LL
        while curr:
            length += 1
            curr = curr.next
    
        for i in range(length, 0, -1):
            curr = LL
            for _ in range(i):
                curr = curr.next
            print(curr.value)
    
    当然,如果您选择将其转换为一个双链接列表来渴望评论,您可以在O(n)时间和0空间内完成此操作:

    OP中算法的运行时间不是O(n)。它是O(n log(n))。作为运行时间,我们定义了一个节点的下一个节点的运行次数。这是在方法体的3个位置明确完成的。实际上它是在5个位置完成的:while子句中的2个位置和while looop的主体中的3个位置,但是如果一个临时保存值,它可以减少到3个位置。while循环重复约n/2次。因此,reverse_print方法明确地获取下一个节点3/2*2次。它在while循环之后的两个reverse_print调用中隐式地获取它们。在这些调用中要处理的列表的长度是用于反向打印原始调用的列表长度的一半,因此为n/2。因此,对于运行时间,我们有以下近似值:

    t(n) = 1.5n+2t(n/2)
    
    这种复发的解决方案是

    t(n) = 1.5n log(n) + n
    
    如果将解决方案插入reccurence,则可以验证这一点

    您还可以运行该问题,计算提取节点的频率。为此,我在程序中添加了一个next_node()方法。我使用cProfiler来计算函数调用。我还添加了一个类方法来创建测试列表。最后以这个节目结束

    import cProfile
    import math
    
    class LinkedListNode:
        def __init__(self, value, next_node):
            self.value = value
            self._next_node = next_node
    
        def next_node(self):
            ''' fetch the next node'''
            return(self._next_node)
    
        def reverse_print(self, list_tail):
            list_head=self
            if not self:
                return
            if not self.next_node():
                print (self.value)
                return
            if self == list_tail:
                print (self.value)
                return
            p0 = self
            p1 = self
            #assert(p1.next_node != list_tail)
            p1_next=p1.next_node()
            p1_next_next=p1_next.next_node()
            while p1_next != list_tail and p1_next_next != list_tail:
                p1 = p1_next_next
                p0 = p0.next_node()
                p1_next=p1.next_node()
                if p1_next != list_tail:
                    p1_next_next=p1_next.next_node()          
            p0.next_node().reverse_print(list_tail)
            self.reverse_print(p0)
    
        @classmethod
        def create_testlist(nodeclass, n):
            ''' creates a list of n elements with values 1,...,n'''
            first_node=nodeclass(n,None)
            for i in range(n-1,0,-1):
                second_node=first_node
                first_node=nodeclass(i,second_node)
            return(first_node)
    
    if __name__ == "__main__":
        n=1000
        cProfile.run('LinkedListNode.create_testlist(n).reverse_print(None)')
        print('estimated number of calls of next_node',1.5*n*math.log(n,2)+n)
    
    我得到了以下输出(最后是显示函数调用数量的探查器的输出):

    >
    重新启动:打印\u反转\u列表2.py
    1000
    999
    998
    ...
    2.
    1.
    2.539秒内完成116221个函数调用(114223个基本调用)
    订购人:标准名称
    ncalls tottime percall cumtime percall文件名:lineno(函数)
    1    0.000    0.000    2.539    2.539 :1()
    2000 0.015 0.000 2.524 0.001 PyShell.py:1335(写入)
    1999/1 0.008 0.000 2.538 2.538打印/反向打印/列表2.py:12(反向打印)
    1 0.000 0.000 0.001 0.001打印反向列表2.py:36(创建测试列表)
    1000 0.000 0.000 0.000 0.000打印\u反转\u列表2.py:5(\u初始\u)
    16410 0.002 0.000 0.002 0.000打印\u反转\u列表2.py:9(下一个\u节点)
    ...
    下一个_节点的估计呼叫数15948.67642699313
    
    因此,公式估计的下一个_node()调用数约为15949。下一个_node()调用的实际数目是16410。后一个数字包括行
    p0.next\u node().reverse\u print(list\u tail)
    中的next\u node()的2000次调用,我在公式中没有计算这些调用


    因此,
    1.5*n*log(n)+n
    似乎是对程序运行时间的合理估计。

    免责声明:我错过了在本次讨论中无法修改列表的内容


    想法:我们按向前的顺序迭代列表,在执行时将其反转。当我们到达末尾时,我们会向后迭代,打印元素并再次反转列表。
    核心观察结果是,你可以在适当的位置反转列表:你所需要的只是记住你处理的最后一个元素

    未经测试、丑陋的伪代码:

    def打印反转(列表){
    上一个=零
    cur=list.head
    如果cur==nil{
    返回
    }
    而cur!=零{
    下一个=当前下一个
    //[上一页]当前->下一页
    cur.next=prev
    //这是一个O(n)时间和O(sqrt(n))空间算法。
    在后文的第二部分中,它将扩展到任意正整数t的线性时间和O(n^(1/t))空间算法

    高层次的想法:将列表拆分为sqrt(n)多个(几乎)大小相等的部分。 使用简单的线性时间、线性空间方法,从最后一个到第一个,以相反的顺序依次打印零件

    为了存储部件的开始节点,我们需要一个大小为O(sqrt(n))的数组。 为了将一个部分还原为大约sqrt(n),naive算法需要一个数组来存储对该部分节点的引用,因此该数组的大小为O(sqrt(n)


    一个使用两个数组(
    lsa
    ssa
    ),大小
    k=[sqrt(n)]+1=O(sqrt(n))
    (lsa…大步进阵列,ssa…小步进阵列)

    第1阶段:(如果链表的大小未知,请找出n,其长度): 从开始到结束遍历列表并计算列表中的元素,这需要n个步骤

    第二阶段: 将单链表的每个第k个节点存储在数组
    lsa
    中。这需要n个步骤

    第三阶段: 按相反顺序处理lsa列表。按相反顺序打印每个部分 这也需要n个步骤

    因此,该算法的运行时间为3n=O(n),其速度约为2*sqrt(n)=O(sqrt(n))

    这是一个Python 3.5实现:

    import cProfile
    import math
    
    class LinkedListNode:
        def __init__(self, value, next_node):
            self.value = value
            self._next_node = next_node
    
        def next_node(self):
            return(self._next_node)
    
        def reverse_print(self):
            # Phase 1
            n=0
            node=self
            while node:
                n+=1
                node=node.next_node()
            k=int(n**.5)+1
    
            # Phase 2
            i=0
            node=self
            lsa=[node]
            while node:
                i+=1
                if i==k:
                    lsa.append(node)
                    i=0
                last_node=node
                node=node.next_node()
            if i>0:
                lsa.append(last_node)
    
            # Phase 3
            start_node=lsa.pop()
            print(start_node.value)
            while lsa:
                last_printed_node=start_node
                start_node=lsa.pop()
                node=start_node
                ssa=[]
                while node!=last_printed_node:
                    ssa.append(node)
                    node=node.next_node()
    
                ssa.reverse()
                for node in ssa:
                    print(node.value)
    
    
        @classmethod
        def create_testlist(nodeclass, n):
            ''' creates a list of n elements with values 1,...,n'''
            first_node=nodeclass(n,None)
            for i in range(n-1,0,-1):
                second_node=first_node
                first_node=nodeclass(i,second_node)
            return(first_node)
    
    if __name__ == "__main__":
        n=1000
        cProfile.run('LinkedListNode.create_testlist(n).reverse_print()')
        print('estimated number of calls of next_node',3*n)
    
    它打印以下输出(最后是显示函数调用数的探查器的输出):


    如果你的代码运行得很好,但你只需要一些想法来改进它,那么最好问一下你为什么要这样做
    >>> 
     RESTART: print_reversed_list2.py 
    1000
    999
    998
    ...
    2
    1
             116221 function calls (114223 primitive calls) in 2.539 seconds
    
       Ordered by: standard name
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    2.539    2.539 <string>:1(<module>)
         2000    0.015    0.000    2.524    0.001 PyShell.py:1335(write)
       1999/1    0.008    0.000    2.538    2.538 print_reversed_list2.py:12(reverse_print)
            1    0.000    0.000    0.001    0.001 print_reversed_list2.py:36(create_testlist)
         1000    0.000    0.000    0.000    0.000 print_reversed_list2.py:5(__init__)
        16410    0.002    0.000    0.002    0.000 print_reversed_list2.py:9(next_node)
       ...
    
    estimated number of calls of next_node 15948.67642699313
    
    import cProfile
    import math
    
    class LinkedListNode:
        def __init__(self, value, next_node):
            self.value = value
            self._next_node = next_node
    
        def next_node(self):
            return(self._next_node)
    
        def reverse_print(self):
            # Phase 1
            n=0
            node=self
            while node:
                n+=1
                node=node.next_node()
            k=int(n**.5)+1
    
            # Phase 2
            i=0
            node=self
            lsa=[node]
            while node:
                i+=1
                if i==k:
                    lsa.append(node)
                    i=0
                last_node=node
                node=node.next_node()
            if i>0:
                lsa.append(last_node)
    
            # Phase 3
            start_node=lsa.pop()
            print(start_node.value)
            while lsa:
                last_printed_node=start_node
                start_node=lsa.pop()
                node=start_node
                ssa=[]
                while node!=last_printed_node:
                    ssa.append(node)
                    node=node.next_node()
    
                ssa.reverse()
                for node in ssa:
                    print(node.value)
    
    
        @classmethod
        def create_testlist(nodeclass, n):
            ''' creates a list of n elements with values 1,...,n'''
            first_node=nodeclass(n,None)
            for i in range(n-1,0,-1):
                second_node=first_node
                first_node=nodeclass(i,second_node)
            return(first_node)
    
    if __name__ == "__main__":
        n=1000
        cProfile.run('LinkedListNode.create_testlist(n).reverse_print()')
        print('estimated number of calls of next_node',3*n)
    
    >>> 
     RESTART: print_reversed_list3.py 
    1000
    999
    998
    ...
    4
    3
    2
    1
             101996 function calls in 2.939 seconds
    
       Ordered by: standard name
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
            1    0.000    0.000    2.939    2.939 <string>:1(<module>)
         2000    0.018    0.000    2.929    0.001 PyShell.py:1335(write)
            1    0.003    0.003    2.938    2.938 print_reversed_list3.py:12(reverse_print)
            1    0.000    0.000    0.001    0.001 print_reversed_list3.py:49(create_testlist)
         1000    0.000    0.000    0.000    0.000 print_reversed_list3.py:5(__init__)
         2999    0.000    0.000    0.000    0.000 print_reversed_list3.py:9(next_node)    
       ...
    
    estimated number of calls of next_node 3000
    >>>     
    
    import cProfile
    import math
    import time
    
    class LinkedListNode:
        '''
        single linked list node
        a node has a value and a successor node
        '''
        stat_counter=0
        stat_curr_space=0
        stat_max_space=0
        stat_max_array_length=0
        stat_algorithm=0
        stat_array_length=0
        stat_list_length=0
        stat_start_time=0
    
        do_print=True
        def __init__(self, value, next_node):
            self.value = value
            self._next_node = next_node
    
    
        def next_node(self):
            self.stat_called_next_node()
            return(self._next_node)
    
        def print(self):
            if type(self).do_print:
                print(self.value)
    
        def print_tail(self):
            node=self
            while node:
                node.print()
                node=node.next_node()
    
        def tail_info(self):
            list_length=0
            node=self
            while node:
                list_length+=1
                last_node=node
                node=node.next_node()
            return((last_node,list_length))
    
    
        def retrieve_every_n_th_node(self,step_size,list_length):
            ''' for a list a of size list_length retrieve a pair there the first component 
            is an array with the nodes 
            [a[0],a[k],a[2*k],...,a[r*k],a[list_length-1]]]
            and the second component is list_length-1-r*k
            and 
            '''
            node=self
            arr=[]
            s=step_size
            index=0
            while index<list_length:
                if s==step_size:
                    arr.append(node)
                    s=1
                else:
                    s+=1
                last_node=node
                node=node.next_node()
                index+=1
            if s!=1:
                last_s=s-1
                arr.append(last_node)
            else:
                last_s=step_size
            return(arr,last_s)
    
    
        def reverse_print(self,algorithm=0):
            (last_node,list_length)=self.tail_info()
            assert(type(algorithm)==int)
            if algorithm==1:
                array_length=list_length
            elif algorithm==0:
                array_length=2
            elif algorithm>1:
                array_length=math.ceil(list_length**(1/algorithm))
                if array_length<2:
                    array_length=2
            else:
                assert(False)
            assert(array_length>=2)
            last_node.print()
            self.stat_init(list_length=list_length,algorithm=algorithm,array_length=array_length)
            self._reverse_print(list_length,array_length)
            assert(LinkedListNode.stat_curr_space==0)
            self.print_statistic()
    
    
    
        def _reverse_print(self,list_length,array_length):
            '''
            this is the core procedure  of the algorithm
                if the list fits into the array
                    store it in te array an print the array in reverse order
                else
                    split the list in 'array_length' sublists and store
                        the startnodes of the sublists in he array
                    _reverse_print array in reverse order
            '''
            if list_length==3 and array_length==2: # to avoid infinite loop
                array_length=3
            step_size=math.ceil(list_length/array_length)
            if step_size>1: # list_length>array_length:
                (supporting_nodes,last_step_size)=self.retrieve_every_n_th_node(step_size,list_length)
                self.stat_created_array(supporting_nodes)
                supporting_nodes.reverse()
                supporting_nodes[1]._reverse_print(last_step_size+1,array_length)
                for node in supporting_nodes[2:]:
                    node._reverse_print(step_size+1,array_length)
                self.stat_removed_array(supporting_nodes)
            else:
                assert(step_size>0)
                (adjacent_nodes,last_step_size)=self.retrieve_every_n_th_node(1,list_length)
                self.stat_created_array(adjacent_nodes)
                adjacent_nodes.reverse()
                for node in adjacent_nodes[1:]:
                    node.print()
                self.stat_removed_array(adjacent_nodes)
    
        # statistics functions
    
        def stat_init(self,list_length,algorithm,array_length):
            '''
            initializes the counters
            and starts the stop watch
            '''
            type(self)._stat_init(list_length,algorithm,array_length)
    
        @classmethod
        def _stat_init(cls,list_length,algorithm,array_length):
            cls.stat_curr_space=0
            cls.stat_max_space=0
            cls.stat_counter=0
            cls.stat_max_array_length=0
            cls.stat_array_length=array_length
            cls.stat_algorithm=algorithm
            cls.stat_list_length=list_length
            cls.stat_start_time=time.time()
    
        def print_title(self):
            '''
            prints the legend and the caption for the statistics values
            '''
            type(self).print_title()
    
        @classmethod
        def print_title(cls):
            print('   {0:10s} {1:s}'.format('space','maximal number of array space for'))
            print('   {0:10s} {1:s}'.format('',     'pointers to the list nodes, that'))
            print('   {0:10s} {1:s}'.format('',     'is needed'))
            print('   {0:10s} {1:s}'.format('time', 'number of times the method next_node,'))
            print('   {0:10s} {1:s}'.format('',     'that retrievs the successor of a node,'))
            print('   {0:10s} {1:s}'.format('',     'was called'))
            print('   {0:10s} {1:s}'.format('alg',  'algorithm that was selected:'))
            print('   {0:10s} {1:s}'.format('',     '0:   array size is 2'))
            print('   {0:10s} {1:s}'.format('',     '1:   array size is n, naive algorithm'))
            print('   {0:10s} {1:s}'.format('',     't>1: array size is n^(1/t)'))
            print('   {0:10s} {1:s}'.format('arr',  'dimension of the arrays'))
            print('   {0:10s} {1:s}'.format('sz',  'actual maximal dimension of the arrays'))
            print('   {0:10s} {1:s}'.format('n',    'list length'))
            print('   {0:10s} {1:s}'.format('log',    'the logarithm to base 2 of n'))
            print('   {0:10s} {1:s}'.format('n log n',    'n times the logarithm to base 2 of n'))               
            print('   {0:10s} {1:s}'.format('seconds',    'the runtime of the program in seconds'))               
    
            print()
            print('{0:>10s} {1:>10s} {2:>4s} {3:>10s} {4:>10s} {5:>10s} {6:>5s} {7:>10s} {8:>10s}'
                  .format('space','time','alg','arr','sz','n','log', 'n log n','seconds'))
    
        @classmethod
        def print_statistic(cls):
            '''
            stops the stop watch and prints the statistics for the gathered counters
            '''
            run_time=time.time()-cls.stat_start_time
            print('{0:10d} {1:10d} {2:4d} {3:10d} {4:10d} {5:10d} {6:5d} {7:10d} {8:10.2f}'.format(
                cls.stat_max_space,cls.stat_counter,cls.stat_algorithm,
                cls.stat_array_length,cls.stat_max_array_length,cls.stat_list_length,
                int(math.log2(cls.stat_list_length)),int(cls.stat_list_length*math.log2(cls.stat_list_length)),
                run_time
                ))
    
        def stat_called_next_node(self):
            '''
            counter: should be called
            if the next node funtion is called
            '''
            type(self)._stat_called_next_node()
    
        @classmethod
        def _stat_called_next_node(cls):
            cls.stat_counter+=1
    
        def stat_created_array(self,array):
            '''
            counter: should be called
            after an array was created and filled
            '''
            type(self)._stat_created_array(array)
    
        @classmethod
        def _stat_created_array(cls,array):
            cls.stat_curr_space+=len(array)
            if cls.stat_curr_space> cls.stat_max_space:
                cls.stat_max_space=cls.stat_curr_space
            if (len(array)>cls.stat_max_array_length):
                cls.stat_max_array_length=len(array)
    
        def stat_removed_array(self,array):
            '''
            counter: should be called
            before an array can be removed
            '''
            type(self)._stat_removed_array(array)
    
        @classmethod
        def _stat_removed_array(cls,array):
            cls.stat_curr_space-=len(array)
    
        @classmethod
        def create_testlist(nodeclass, n):
            '''
            creates a single linked list of
            n elements with values 1,...,n
            '''
            first_node=nodeclass(n,None)
            for i in range(n-1,0,-1):
                second_node=first_node
                first_node=nodeclass(i,second_node)
            return(first_node)
    
    if __name__ == "__main__":
        #cProfile.run('LinkedListNode.create_testlist(n).reverse_print()')
        n=100000
        ll=LinkedListNode.create_testlist(n)
        LinkedListNode.do_print=False
        ll.print_title()
        ll.reverse_print(1)
        ll.reverse_print(2)
        ll.reverse_print(3)
        ll.reverse_print(4)
        ll.reverse_print(5)
        ll.reverse_print(6)
        ll.reverse_print(7)
        ll.reverse_print(0)
    
       space      maximal number of array space for
                  pointers to the list nodes, that
                  is needed
       time       number of times the method next_node,
                  that retrievs the successor of a node,
                  was called
       alg        algorithm that was selected:
                  0:   array size is 2
                  1:   array size is n, naive algorithm
                  t>1: array size is n^(1/t)
       arr        dimension of the arrays
       sz         actual maximal dimension of the arrays
       n          list length
       log        the logarithm to base 2 of n
       n log n    n times the logarithm to base 2 of n
       seconds    the runtime of the program in seconds
    
         space       time  alg        arr         sz          n   log    n log n    seconds
        100000     100000    1     100000     100000     100000    16    1660964       0.17
           635     200316    2        317        318     100000    16    1660964       0.30
           143     302254    3         47         48     100000    16    1660964       0.44
            75     546625    4         18         19     100000    16    1660964       0.99
            56     515989    5         11         12     100000    16    1660964       0.78
            47     752976    6          7          8     100000    16    1660964       1.33
            45     747059    7          6          7     100000    16    1660964       1.23
            54    1847062    0          2          3     100000    16    1660964       3.02
    
       space      maximal number of array space for
                  pointers to the list nodes, that
                  is needed
       time       number of times the method next_node,
                  that retrievs the successor of a node,
                  was called
       alg        algorithm that was selected:
                  0:   array size is 2
                  1:   array size is n, naive algorithm
                  t>1: array size is n^(1/t)
       arr        dimension of the arrays
       sz         actual maximal dimension of the arrays
       n          list length
       log        the logarithm to base 2 of n
       n log n    n times the logarithm to base 2 of n
       seconds    the runtime of the program in seconds
    
         space       time  alg        arr         sz          n   log    n log n    seconds
       1000000    1000000    1    1000000    1000000    1000000    19   19931568       1.73
          2001    3499499    2       1000       1001    1000000    19   19931568       7.30
           302    4514700    3        100        101    1000000    19   19931568       8.58
           131    4033821    4         32         33    1000000    19   19931568       5.69
            84    6452300    5         16         17    1000000    19   19931568      11.04
            65    7623105    6         10         11    1000000    19   19931568      13.26
            59    7295952    7          8          9    1000000    19   19931568      11.07
            63   21776637    0          2          3    1000000    19   19931568      34.39