删除O(nlogn)中列表的副本

时间:2013-07-02 18:52:13

标签: scheme racket

如何删除列表的副本? (运行时间为O(n log n)) 例如:'(4 6 1 1 2 3 3 5 6)=> '(4 6 1 2 3 5)

 (define (re-dup lst)
   (cond
      ((empty? lst) empty)
      (else
      (define el (first lst))
      (define el-free-lst (filter (lambda (x) (not (= el x))) (rest lst)))
      (cons el (re-dup el-free-lst)))))

这是对的吗?

2 个答案:

答案 0 :(得分:2)

您当前的解决方案是O(n^2),因为filter遍历列表一次为原始列表中的每个元素。可以使用具有常量时间插入和成员资格操作的辅助数据结构编写O(n)解决方案,以跟踪已经找到的元素。

在Racket中,我们有一个开箱即用的set数据结构,对于常量时间操作,“实际上需要一组O(log N)时间大小N“(请参阅documentation),因此set-member?set-add程序将为O(log n)。因此,使用Racket的set进行此实现并非最佳,但我们实现了O(n log n)目标:

(define (re-dup lst)
  (let loop ((seen (set))
             (lst lst)
             (acc '()))
    (cond ((null? lst)
           (reverse acc))
          ((set-member? seen (car lst))
           (loop seen (cdr lst) acc))
          (else
           (loop (set-add seen (car lst))
                 (cdr lst)
                 (cons (car lst) acc))))))

按预期工作,保留列表中的原始顺序(这是此问题的约束,如评论中所述),但需要额外执行O(n) reverse次操作:

(re-dup '(4 6 1 1 2 3 3 5 6))
=> '(4 6 1 2 3 5)

答案 1 :(得分:0)

您可以通过以下方式获得O(n log n)行为:

  1. 合并排序(O(n log n)
  2. 遍历并丢弃重复项(O(n)
  3. 因此整体O(n log n)

    (define (re-dup lst)
      (if (null? lst)
          lst
          (let ((lst (list-sort < lst)))
            (let thinning ((skip (car lst)) (rest (cdr lst)))
              (cond ((null? rest) (list skip))
                    ((equal? skip (car rest)) (thinning skip (cdr rest)))
                    (else (cons skip (thinning (car rest) (cdr rest)))))))))