TSPL學習筆記(3):排序算法練習

快速排序

快排的詳細介紹,簡單的說就是取輸入序列中的首元素m,而後將除首元素m之外的其它元素分紅兩組,小於等於m的一組和大於m的一組.將3組元素組合成輸入隊列:小於等於m + m + 大於m.app

下面看一個用haskell實現的快速排序代碼:less

quicksort :: (Ord a) => [a] -> [a]   
quicksort [] = []   
quicksort (x:xs) =   
  let smallerSorted = quicksort [a | a <- xs, a <= x]  
      biggerSorted = quicksort [a | a <- xs, a > x]   
  in smallerSorted ++ [x] ++ biggerSorted

haskell中有列表解析(List Comprehension)的支持,因此smallerSortedbiggerSorted兩個集合經過列表解析很方便的就生成了.而Scheme中沒有列表解析,因此首先要實現一個相似列表解析的功能將附後條件的列表元素篩選出來:函數

(define my-filter
    (lambda (f l)   
        (define iter
            (lambda (l a)
                (if (not (pair? l)) a
                    (let* ([h (car l)] [r (if (f h) (append a (list h)) a)])
                            (iter (cdr l) r)))))
        (iter l '())))

filter的功能是輸入一個條件判斷函數f和一個列表l,filter將l中全部知足f的元素組成一個列表並返回.上面代碼使用的是accumulator模式,也就是在迭代模式,經過傳進一個a參數在每次遞歸調用中保存結果.能夠注意到這個filter的定義是知足尾遞歸的.ui

下面是filter的遞歸版本:ssr

(define my-filter
    (lambda (f l)
        (if (not (pair? l)) '()
            (let ([h (car l)])
                (if (f h) (cons h (filter f (cdr l)))
    (filter f (cdr l)))))))

經過cps變換將上述函數轉換成尾遞歸的:code

(define my-filter
    (lambda (f l)
        (define cps
            (lambda (l k)
                (if (not (pair? l)) (k '())
                    (let ([h (car l)])
                        (if (f h) (cps (cdr l) (lambda (x) (k (cons h x))))
                            (cps (cdr l) (lambda (x) (k x))))))))
    (cps l (lambda (x) x))))

有了filter以後咱們就能夠實現qsort了:排序

(define qsort
    (lambda (l)
        (if (not (pair? l)) '()
            (let* ([m (car l)]
                   [large (my-filter (lambda (x) (if (> x m) #t #f)) (cdr l))]
                   [less (my-filter (lambda (x) (if (<= x m) #t #f)) (cdr l))])
            (append (qsort less) (cons m (qsort large)))))))
            
>(qsort `(5 1 4 2 3 3 7)) -> (1 2 3 3 4 5 7)

比較下與haskell版的區別,首先是沒有列表解析,其次是沒有模式匹配,須要用if表達式處理.遞歸

固然haskell中也是由filter的,下面就是haskell快排的filter版本:隊列

quicksort :: (Ord a) => [a] -> [a]     
quicksort [] = []     
quicksort (x:xs) =      
    let smallerSorted = quicksort (filter (<=x) xs) 
        biggerSorted = quicksort (filter (>x) xs)    
    in  smallerSorted ++ [x] ++ biggerSorted

在引haskell中fold後代碼能夠更加簡潔和高效:ip

(define (foldl f init xs)
    (define (iter xs acc)
        (if (null? xs) acc
            (iter (cdr xs) (f acc (car xs)))))
    (iter xs init))

(define (foldr f init xs)
    (define (iter xs acc)
        (if (null? xs) acc
            (iter (cdr xs) (f (car xs) acc))))
    (iter (reverse xs) init))

(define (qsort l greater)
    (if (not (pair? l)) '()
        (let ([m (car l)]
             [partition (foldr (lambda (x acc)
                         (let ([small (car acc)]
                           [large (cadr acc)])
                     (if (greater x m) (list small (cons x large))
                         (list (cons x small) large))))
                '(()()) (cdr l))])  
        (append (qsort (car partition) greater) 
                (cons m (qsort (cadr partition) greater))))))

冒泡排序

冒泡排序的詳細介紹

首先要定義一個交換函數:

;交換列表中的兩個元素       
(define (swap xs n1 n2)
    (let ([a (element-at xs n1)]
          [b (element-at xs n2)])
          (reverse (car (cdr (foldl (lambda (acc x)
            (let ([fst (car acc)]
                  [snd (car (cdr acc))])             
                 (cond [(= fst n1) (list (+ fst 1) (cons b snd))]
                       [(= fst n2) (list (+ fst 1) (cons a snd))]
                       [else (list (+ fst 1) (cons x snd))]))) '(1 ()) xs))))))

如下是冒泡排序的實現:

(define (bubble xs)
    (define (bubble-imp xs less)    
        (if (= (length xs) 1) (list (car xs) less);返回最大值和剩餘值組成的列表
            (let ([f (car xs)]
                  [s (cadr xs)])
                 (if (> f s) (bubble-imp (cdr (swap xs 1 2)) (reverse (cons s less)))
                             (bubble-imp (cdr xs) (reverse (cons f less)))))))
    (define (iter xs result)
        (if (null? xs) result
            (let ([r (bubble-imp xs '())])
                 (iter (cadr r) (cons (car r) result)))))
    (iter xs '()))

輔助過程bubble-imp用於篩選列表,它的返回值是輸入列表的(最大元素 其他剩餘元素的列表)
bubble-imp每輪執行都會比較列表的第一和第二個元素,若是第一個元素大於第二個元素則交換它們兩的位置,
而後將較小的元素插入less中,以後丟棄表頭(較小的元素)用剩餘元素執行第二輪迭代,直到輸入列表中只剩下1個元素,此時,那個剩餘元素就是初始
輸入列表中的最大元素,而less中則存放了其他的元素.

也就是每次執行bubble-imp都會生成一個最大值和其他元素的列表,爲了完成排序須要將最大值插入結果列表,而後繼續從剩餘元素中找出次大值.
'iter'過程完成的就是這個任務,執行bubble-imp將最大值插入結果列表而後判斷是否還有剩餘元素,若是有則繼續上述過程.

相應的haskell實現

-- 冒泡排序
bubble :: (Ord a) => [a] -> [a]
bubble []  = error "Can't call pass on an empty list, dummy!"
bubble (x:[]) = [x]
bubble xs = iter xs []
       where 
        pass xs left        
            | size == 2 = if first < second then (first:left,second)
                             else (second:left,first)
            | size > 2 =  let remain = tail (tail xs) in
                          if first < second then pass (second:remain) (first:left)
                             else pass (first:remain) (second:left)
            | size == 1 = ([],first)                                     
            where 
                  size  = length xs
                  first = head xs
                  second = head (tail xs)                          
        iter xs result =
            let 
                passret = (pass xs [])
                left = fst passret
                max = snd passret 
            in 
                if length left == 0 then (max:result)
            else iter left (max:result)
相關文章
相關標籤/搜索