Haskell常見排序演算法的實現
這篇文章嘗試使用 Haskhell 來重寫常見的排序演算法。這裡不考慮效率,比如時間和空間上的,所以不會刻意去寫成尾遞迴。
插入排序
插入排序是一種簡單易懂的排序。這裡分為兩個步驟:
- 將一個元素插入一個已被排序的數列
- 對一個未排序的數列不停施以步驟 1
首先步驟 1,要插入數 x,當前序列中第一個數為 y。將 x, y 較小的數放在前面,然後對去除第一個數之後的子序列不停重複上述過程。
insert :: Ord a => a -> [a] -> [a] insert x [] = [x] insert x (y:ys) | x < y = x:y:ys | otherwise = y : insert x ys
接下來,只要施以步驟 2 即可,即將亂序的元素一個個地使用 insert 函式到另一個有序列表裡就可以了。
insertSort :: Ord a => [a] -> [a] insertSort [] = [] insertSort (x:xs) = insert x (insertSort xs)
也可以寫成尾遞迴的形式,用一個列表來儲存中間結果:
insertSort :: Ord a => [a] -> [a] -> [a] insertSort xs [] = xs insertSort xs (y:ys) = insertSort (insert y xs) ys
氣泡排序
氣泡排序也分為兩個步驟:
- 比較相鄰元素的大小,然後交換較小的元素,將最大的數通過這個方式交換到最後
- 重複步驟 1
第一步是交換
swaps :: Ord a => [a] -> [a] swaps [] = [] swaps [x] = [x] swaps (x1:x2:xs) | x1 > x2 = x2 : swaps(x1:xs) | otherwise = x1 : swaps(x2:xs)
然後就是不停 swaps,直到列表不再發生變化
bubbleSort :: Ord a => [a] -> [a] bubbleSort xs | swaps xs == xs = xs -- 沒發生變化,就停止 | otherwise = bubbleSort $ swaps xs
可以看到,第二步的效率不高,因為第一輪的 swaps 之後,最後一個數已經是最大的數了,第二步就沒有必要來遍歷到最後一個數。所以,可以將前一步 swaps 之後的序列分為前 n-1 項和最後一項,當前步下,最後一項可以不動,只需 bubbleSort 前 n-1 項。
bubbleSort' :: Ord a=> [a] -> [a] bubbleSort' [] = [] bubbleSort' xs = bubbleSort' initElem ++ [lastElem] where swappedElem = swaps xs initElem = init swappedElem lastElem = last swappedElem
選擇排序
首先找到最小的元素,將其從序列中取出,放入另一個序列中(初始為空),然後依次類推,直到所有元素從元序列被取出。
- 尋找序列中最小數,Haskell 有現成的函式 minimum
- 將最小數從原序列中刪除
這裡只要寫一個將序列中指定元素刪除的程式
deleteFromOri :: Eq a => a -> [a] -> [a] deleteFromOri _ [] = [] deleteFromOri x (y:ys) | x == y = ys | otherwise = y:deleteFromOri x ys
然後只要將每次 minimum 得到的數從原序列刪除放入新序列
selectSort :: Ord a => [a] -> [a] selectSort [] = [] selectSort xs = mini : selectSort xs' where mini = minimum xs xs' = deleteFromOri mini xs
快速排序
快排的定義其實非常簡單,但在 c 語言中卻不好理解,不像 Haskell 這樣寫起來就像在定義一個數學定理一樣。
- 取出序列中的一個數(簡單的取法,直接取第一個元素),將所有小於該數的數作為一組放於該數左邊,將所有該數的數作為另一組放於該數右邊
- 對左右兩組數分別施以步驟 1
程式碼為
quickSort :: Ord a => [a] -> [a] quickSort [] = [] quickSort [x:xs] = quickSort mini ++ [x] quickSort maxi where mini = filter (<x) xs maxi = filter (>=x) xs
當然這裡效果不高,會運算過程中會產生許多 []。
歸併排序
歸併排序這裡仍然是兩個步驟。
- 將兩個有序數列合為一個有序數列
- 將原序列不停劃分兩部分,直至每部分只有一個元素,然後不停呼叫步驟 1,將其合併成一個有序數列
步驟 1 的實現,只要將兩個序列 xs 和 ys 的第一個元素作比較即可。步驟 2 採用對半劃分。
merge :: Ord a => [a] -> [a] -> [a] merge xs [] = xs merge [] ys = ys merge (x:xs) (y:ys) | x > y = y:merge (x:xs) ys | otherwise = x:merge xs (y:ys) mergeSort :: Ord a => [a] -> [a] mergeSort xs = merge (mergeSort x1) (mergeSort x2) where (x1, x2) = split xs split xs = (take mid xs, drop mid xs) mid = (length xs) `div` 2
相關文章
- 常見演算法 PHP 實現 -- 堆排序演算法PHP排序
- 常見排序演算法-Python實現排序演算法Python
- python實現常見的五種排序演算法Python排序演算法
- 常見排序演算法原理及JS程式碼實現排序演算法JS
- 常見排序演算法排序演算法
- python3實現幾種常見的排序演算法Python排序演算法
- 用 Java 實現常見的 8 種內部排序演算法Java排序演算法
- 常見的排序演算法 (下)排序演算法
- 常見排序原理及 python 實現排序Python
- 常見的排序演算法分析(一)排序演算法
- 常見排序演算法總結排序演算法
- 常見排序演算法小結排序演算法
- Javascript常見排序演算法的筆記JavaScript排序演算法筆記
- Java常見排序演算法之插入排序Java排序演算法
- 演算法之常見排序演算法-氣泡排序、歸併排序、快速排序演算法排序
- 通過leetcode學習常見排序演算法及其Go實現LeetCode排序演算法Go
- PHP常見排序演算法學習PHP排序演算法
- 35.幾種常見的排序演算法排序演算法
- 10種python常見的排序演算法!Python排序演算法
- 幾種常見的排序演算法總結排序演算法
- JavaScript實現常見查詢演算法JavaScript演算法
- 棧的模擬實現及常見演算法演算法
- 常見排序演算法及複雜度排序演算法複雜度
- 幾種常見排序演算法總結排序演算法
- 常見的排序演算法:冒泡、快排、歸併排序演算法
- Python實現常見機器學習演算法(上)Python機器學習演算法
- 說說常見的排序演算法有哪些?區別?排序演算法
- 常見排序演算法及其實現(Binary,Insert、Select、Quick、Bubble.etc.Sort)排序演算法UI
- 排序演算法之快速排序的實現排序演算法
- 常見快取演算法和LRU的c++實現快取演算法C++
- 淺談常見的七種加密演算法及實現加密演算法
- 常見的三種排序演算法(選擇,冒泡,計數)排序演算法
- Java常見排序演算法之插入排序-簡單的效能優化技巧Java排序演算法優化
- python排序演算法的實現-快速排序Python排序演算法
- 【演算法】希爾排序的實現演算法排序
- 排序演算法:Java實現希爾排序排序演算法Java
- 排序演算法-Java實現快速排序演算法排序演算法Java
- 常見的js演算法面試題收集,es6實現JS演算法面試題