我正在尝试使用方案实现快速排序,这里的一些家伙已经帮我修复了我的split
函数,现在我要求你帮助将所有内容组合成一个有效的算法。
到目前为止,这是我的代码:
(define quick-sort (lambda (lst)
(define pivot (lambda (lst)
(if (null? lst)
null
(car lst))))
(define split (lambda (lst pivot)
(define lst1 null)
(define lst2 null)
(define split-helper (lambda (lst pivot lst1 lst2)
(if (null? lst)
(list lst1 lst2)
(if (<= (car lst) pivot)
(split-helper (cdr lst) pivot (cons (car lst) lst1) lst2)
(split-helper (cdr lst) pivot lst1 (cons (car lst) lst2))))))
(split-helper lst pivot lst1 lst2)))
(if (null? lst)
null
(append (quick-sort (car (split lst (pivot lst)))) (quick-sort (cdr (split lst (pivot lst))))))))
正如你所看到的,我选择的枢轴只是列表中的第一个元素,我面临的问题是,当pivot是列表中的最小元素时,程序会遇到无限循环,因为它使程序一遍又一遍地选择相同的轴。
此外,它当前实现的方式使它真的无效率,因为split
函数在lst
的每个ineration中被调用两次并且具有相同的quick-sort
但我只是'对控制有足够的控制权以任何其他方式写它。
我在Scheme中看到了一些关于Quick-Sort的帖子,但它们的实现有点不同,我宁愿尝试纠正自己的实现而不是复制其他一些家伙的工作。
谢谢。
答案 0 :(得分:3)
对于快速排序而言,这是一个经典的错误。您的数据透视表不应该是分区的一部分。这样一个元素列表就会生成两个空分区,一个在枢轴之前,一个在枢轴之后。
两次做同样的操作。使用let
缓冲拆分结果并使用变量两次。
答案 1 :(得分:2)
删除了过多的lambdas,别名,绑定和重新格式化,但没有更改或注释语义(Sylwester已经指出了这个bug):
(define (quick-sort lst)
(define (pivot lst)
(if (null? lst)
'()
(car lst) ))
(define (split lst pivot)
(let split-helper ((lst lst) ; Named let instead of internal
(lst1 '()) ; definition
(lst2 '()) )
(if (null? lst)
(cons lst1 list2)
(if (<= (car lst) pivot)
(split-helper (cdr lst)
(cons (car lst) lst1)
lst2)
(split-helper (cdr lst)
lst1
(cons (car lst) lst2) )))))
(if (null? lst)
'()
(let ((spl (split lst (pivot lst)))) ; Memoization of the `split`
(append (quick-sort (car spl))
(quick-sort (cdr spl)) ))))
我认为您正在尝试实施partition
:
(define (partition pred xs)
(let part ((ps '()) (ns '()) ; Initial "positives" `ps`, and "negatives" `ns`
(xs' xs) )
(if (null? xs')
(cons ps ns) ; Returning pair of lists
(let ((x (car xs'))) ; Memoization of `(car lst)`
(if (pred x)
(part (cons x ps) ns (cdr xs'))
(part ps (cons x ns) (cdr xs')) )))))
(define (quicksort xs)
(if (null? xs) '()
(let* ((x (car xs))
(pn (partition ; Memoization of `partition`
(lambda (x')
(< x' x) )
(cdr xs) )))
(append (quicksort (car pn)) ; Extracting positives from pair
(list x) ; Pivot
(quicksort (cdr pn)) )))) ; Negatives
(display
(quicksort (list 4 2 3 5 1)) ) ; (1 2 3 4 5)
part
在如Scheme等严格语言中效率低下;它为每个递归步骤复制所有三个参数。通常,基本折叠(如filter
和map
的直接表述最有效。使用filter
实现更高效的实现:
(define (quicksort xs)
(if (null? xs) '()
(let ((x (car xs))
(xs' (cdr xs)) )
(append (quicksort
(filter (lambda (x')
(< x' x) )
xs'))
(list x)
(quicksort
(filter (lambda (x')
(>= x' x) )
xs'))))))
这种策略在函数式语言中恰好可以简单地表达出来。
在懒惰的Haskell中,单遍历partition
实际上比filter
两次效率更高。
select :: (a -> Bool) -> ([a], [a]) -> a -> ([a], [a])
select pred (ps, ns) x | pred x = (x : ps, ns)
| otherwise = (ps, x : ns)
partition :: (a -> Bool) -> [a] -> ([a], [a])
partition pred = foldl (select pred) ([], [])
quicksort :: Ord a => [a] -> [a]
quicksort [] = []
quicksort (x : xs) = let (lt, gt) = partition (< x) xs
in quicksort lt ++ [x] ++ quicksort gt