我有这段代码:
(define tree `(A (B (C)) (D (E)) (C (E))))
(define (prog1 graph)
(let ([seen `()])
(define (sub g)
(cond
[(member (car g) seen) `()]
[else
(set! seen (cons (car g) seen))
(cond
[(null? (cdr g)) (list (car g))]
[else
(cons (car g) (map sub (cdr g)))])]))
(delete `() (sub graph))))
(define delete
(lambda (x y)
(if (null? y )
`()
(if (eqv? (car y) x)
(delete x (cdr y))
(cons (car y) (delete x (cdr y)))))))
它打印一个连接图,其中所有节点都出现一次。
正在运行(prog1 tree)
打印:(A (B (C)) (D (E)))
我已经研究了lisp中的各种深度优先搜索(类似于我正在尝试做的事情),并且它们看起来更加优雅,有些使用迭代方法。我知道程序效率不高(在大树上它运行得很慢)所以我该如何提高这段代码的效率呢?
谢谢,詹姆斯
答案 0 :(得分:1)
在大多数情况下,此代码中的瓶颈不是树遍历,而是member
查找。函数的复杂性似乎大致为O(M * N),其中M是不同节点的数量,N是总节点的数量。 M作为一个因素进入这个因素的原因是因为你在线性列表中查找节点,这需要时间与列表的长度成比例(在你的情况下,它与不同节点的数量成正比)。 p>
摆脱M的方法是使用更有效的数据结构进行查找。例如,R6RS定义了哈希表。
答案 1 :(得分:1)
member
过程每次调用时都会对列表执行O(n)
查找。这不是我们想要快速测试集合成员资格的原因,因为你应该使用一个数据结构,为集合中添加元素和测试元素成员资格提供O(1)
复杂性,理想情况下是一个Set数据结构或者它的位置是Hash表。例如,在Racket中尝试替换这些行(或使用Scheme解释器中的默认哈希表实现):
(let ([seen `()]) => (let ([seen (make-hash)])
[(member (car g) seen) `()] => [(hash-has-key? seen (car g)) '()]
(set! seen (cons (car g) seen)) => (hash-set! seen (car g) 'ok)
此外,通常您希望在代码中使用quotes:'()
而不是quasiquotes:`()
,请参阅链接以了解差异以及何时适当使用quasiquoting。
最后,您可以使用内置的remove
程序,无需实施自己的delete
。