【算法】遞歸應用_常見算法的遞歸實現

前面學習了遞歸,趁熱打鐵就把常見的一些算法用遞歸實現了一遍,找找遞歸的感受。算法

斐波那契數列

用遞歸函數定義以下:
(1) n = 0時,f(n) = 0
(2) n = 1時,f(n) = 1
(3) n > 1時,f(n) = f(n-1) + f(n-2)微信

--Haskell
fibonacci :: (Num a, Eq a) => a -> a
fibonacci 0 = 0
fibonacci 1 = 1
fibonacci n = fibonacci (n - 1) + fibonacci (n - 2)

這代碼幾乎就是上面的遞歸定義原封不動的「照搬」。固然這個版本性能並非過高,優化的空間很大,這裏不討論。函數

插入排序

大部分人在打牌的時候無心中就使用了這個算法。左手拿着排序好的排【一開始沒有牌,固然知足條件】,假設按升序排列,每次右手摸完牌都會插入到左手適當的位置來保持左手中的牌是有序的。人的話,可能並無意識到,實際上你在插入牌的時候,是經過將右手的牌或者從頭開始或從尾開始依次跟左手的牌對比,而後找到合適的位置。時間複雜度爲O(n2)。性能

咱們能夠遞歸定義以下:
(1)若是列表內沒有元素,咱們就返回空列表
(2)若是列表不爲空,對列表元素的排序,能夠認爲就是將列表首元素插入到已經排好序的其餘元素中,這些元素的排序經過插入排序實現。學習

代碼以下:優化

--Haskell
insertionSort :: Ord a => [a] -> [a]
insertionSort [] = []
insertionSort (x:xs) = insert x $ insertionSort xs

insert :: Ord a => a -> [a] -> [a]
insert x y = let (p,q) = span (<x) y in p ++ [x] ++ q

冒泡排序

這個算法也較爲直觀,假設升序排列,第一次遍歷會將最大的元素移動到列表的右側,怎麼移動的呢?只要從左到右,依次比較當前位置的元素和右側的元素,交換順序不對的兩個元素的位置。經過屢次遍歷,最終獲得全部元素有序的列表。時間複雜度爲O(n2)。spa

--Haskell
bubbleSort :: Ord a => [a] -> [a]
bubbleSort [] = []
bubbleSort x = bubbleSort initx ++ [lastx]
  where
    x' = swap' x
    initx = init x'
    lastx = last x'

swap' :: Ord a => [a] -> [a]
swap' [] = []
swap' [x] = [x]
swap' (x1:x2:xs)
  | x1 > x2 = x2 : swap' (x1 : xs)
  | otherwise = x1 : swap' (x2 : xs)

歸併排序

這個算法的效率較高,時間複雜度爲O(nlogn),採用了分治思想,即:將元素分爲兩部分,每一部分分別採用歸併排序算法排序,而後將這兩部分已排序的部分合併爲總體有序。code

--Haskell
mergeSort :: Ord a => [a] -> [a]
mergeSort [] = []
mergeSort [x] = [x]
mergeSort xs = merge (mergeSort x1) (mergeSort x2)
  where
    (x1, x2) = splitAt half xs
    half = (length xs) `div` 2

merge :: Ord a => [a] -> [a] -> [a]
merge [] ys = ys
merge xs [] = xs
merge x1@(x:xs) y1@(y:ys)
  | x > y = y : merge x1 ys
  | otherwise = x : merge xs y1

二分算法

在前面實現了一版,以下排序

--Haskell
import qualified Data.Vector as V

binarySearch :: (Ord a)=>  V.Vector a -> Int -> Int -> a -> Maybe Int
binarySearch vec low high e
          | low > high = Nothing
          | vec V.! mid < e = binarySearch vec (mid+1) high e
          | vec V.! mid > e = binarySearch vec low (mid-1) e
          | otherwise = Just mid
          where
              mid = low + ((high-low) `div` 2)

但只能返回一個元素,若是咱們想返回全部與e相等的元素位置該怎麼辦呢?一種非遞歸的方案多是:使用上面獲得的位置,而後使用循環向前和向後搜索,從而獲得與之相等的全部元素的索引。下面是遞歸的版本,與循環版本對應,只不過在找到相等元素的時候繼續往下遞歸便可。遞歸

--Haskell
import qualified Data.Vector as V

binarySearch' :: (Ord a) => V.Vector a -> Int -> Int -> a -> [Int]
binarySearch' vec low high e
  | low > high = []
  | vec V.! mid < e = binarySearch' vec (mid + 1) high e
  | vec V.! mid > e = binarySearch' vec low (mid - 1) e
  | otherwise = binarySearch' vec low (mid - 1) e ++ [mid] ++ (binarySearch' vec (mid + 1) high e)
  where
    mid = low + ((high - low) `div` 2)

下一章介紹的快速排序也是遞歸的範本,下一篇文章介紹。

微信公衆號
圖片描述

相關文章
相關標籤/搜索