简体   繁体   中英

Complexity of appending lists in racket

>(define (f l) l)   ;;;consider l to be a list 

What should be complexity of this function. According to me it should be O(length l) because a new list should be created on heap and a new list is created and returned.

So if it is O(length l) then complexity of (append l1 l2) function must be O(length l1 + length l2) because

(define (append l1 l2)
    (if (null? l1) l2 [cons (car l1) (append (cdr l1) l2)]))

At the base case a new list is created on heap so it would take a time O(l2) and the recursion would take time O(l1) so total complexity O(l1 + l2)

But i was taught in class that append is O(l1) in class, so which is correct?

Screenshot to prove that an entire new list is created on heap 1 (otherwise on changing l1 or l2 l3 must have changed!!

(define (fl) l) simply returns its argument, does not copy it , and so its complexity is O(1), while the append definition that you have given copies only the first list, and so its complexity is O(length l1).

Consider the example that you have given: (set! l2 '(4 5)) does not modify any list, it modifies the global variable l2 , make it pointing to a new list. So l3 is unchanged. You can modify a list by using set-cdr! or set-car! , and if you try this (assuming using a dialect in which you can modify lists) you will see that also l3 is modified.

Renzo assumes arguments are already in a list and some interpreters that might be right. Most imlpementations of eval does it like this and then the actual lambda implementation does an evlis before apply .

The most efficient Scheme implementations executes the code as a stack machine and thus every variable is just an offset pointer to the stack just as in native programs. For (lambda ll) to work l needs to be consed from all the arguments such that at the beginning of the function it has performed a O((length n)) task and it has one stack argument with the address to that freshly created list. Then it returns that address, perhaps by leaving it on the stack.

append gets lists as two arguments. Thus it doesn't need to create them from the stack since the stack has two addresses. append makes a copy of l1 and when l1 is the empty list it uses the l2 without doing anything with it as the cdr` of the last pair. As an example:

(define test1 '(1 2 3))
(define test2 '(4 5 6))
(define test3 (append test1 test2))
test3 ; ==> (1 2 3 4 5 6)

(eq? (cdddr test3) test2) ; ==> #t (they are the same)

(define test4 (append test1 '()))
test4 ; ==> (1 2 3)

(equal? test1 test4) ; ==> #t
(eq? test1 test4)    ; ==> #f (they just look the same)

Here is the steps involved with the first append :

(append '(1 2 3) '(4 5 6))                        ; ==
(cons '1 (append '(2 3) '(4 5 6))                 ; ==
(cons '1 (cons '2 (append '(3) '(4 5 6)))         ; ==
(cons '1 (cons '2 (cons 3 (append '() '(4 5 6)))) ; ==
(cons '1 (cons '2 (cons 3 '(4 5 6)))              ; ==

As you can see it is O((+ 1 (length l1)))

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM