Scala函數式編程實現排序算法

  記得《Function Thinking》這本書中提到,如今的編程範式有兩類,一類是「命令式編程」,另外一類是「函數式編程」,如今咱們最常使用的許多語言像c、c++、java都是命令式的,但其中c++和java也都有一些函數式的類庫,可見函數式特性仍是受一些程序員的青睞的。還有一些純函數式的語言如 clojure、haskell則徹底是純函數式的。像python、scala則是混合型的,包含兩種範式,給程序員提供了巨大的靈活性,使解決問題的方式更多,可謂是程序員的一大利器。
如今就以scala語言的"pattern matching"來實現一些經典的排序算法,來展現一下函數式編程思惟方式上帶給咱們的驚喜和享受。java

1. 冒泡排序

原理:

經過相鄰元素比較交換的方式,將最大的元素依次移動到列表中未排好序部分的尾部,重複操做,直到列表中未排好序的部分爲空,從而使整個列表有序python

scala實現思路:

經過相鄰元素比較交換的方式,將最大的元素依次移動到列表中未排好序部分的尾部,重複操做,直到列表中未排好序的部分爲空,從而使整個列表有序c++

  1. 新建一冒泡函數bubble(unSorteds: List[A]): A,實現一趟冒泡功能,即從輸入列表中冒泡出一個最大元素A
  2. 給bubble函數添加兩個參數remains: List[A], accOrdereds: List[A],添加後函數以下:bubble(unSorteds: List[A],remains: List[A], accOrdereds: List[A]): A
    其中remains用於記錄每次冒泡後去掉冒出去的元素後剩餘元素列表,
    accOrdereds用於累積每趟冒泡冒出來的元素,而後將返回值A改成List[A],即返回累積排好序的列表。
    函數bubble使用「模式匹配」匹配爲排序的列表,分兩種狀況
  3. 列表中至少有兩種元素的狀況
  4. 列表中只剩餘一個元素
    這種狀況下,當剩餘元素列表remains爲空時,說明整個排序完成。不然繼續遞歸bubble

具體scala代碼以下

object BubbleSort { /** * @param list 待排序列表 * @tparam A 列表元素類型 * @return */ def bubbleSort[A <% Ordered[A]](list: List[A]): List[A] = { /** * @param unSorteds 每一趟冒泡時待排序列表 * @param remains 已遍歷且未冒出的元素列表 * @param accOrdereds 已冒出的元素組成的有序列表(是累積的) * @return 每一趟冒泡後排好序的列表 */ @tailrec def bubble(unSorteds: List[A], remains: List[A], accOrdereds: List[A]): List[A] = unSorteds match { case h1 :: h2 :: t => if (h1 > h2) bubble(h1 :: t, h2 :: remains, accOrdereds) else bubble(h2 :: t, h1 :: remains, accOrdereds) case h1 :: Nil => if (remains.isEmpty) return h1 :: accOrdereds else bubble(remains, Nil,h1 :: accOrdereds) } bubble(list, Nil, Nil) } def main(args: Array[String]): Unit = { val list = List(1,13,7,5,8,9,20,43,11,8) println(bubbleSort(list)) } }

2. 快速排序

原理:

使用分治思想,將數列用選好的基準點劃分爲兩個子序列(也就是將比基準點小的元素放左邊,比基準點大的元素放右邊),遞歸對子序列使用此方法進行此操做,遞歸到最底部時,數列的大小是零或一,也就是已排好序。程序員

使用scala的實現思路:

利用scala的模式匹配對序列進行匹配,分兩種狀況:算法

  1. 序列爲空
    爲空時返回一個空的List()
  2. 序列由head和tail組成,head不爲空,這時候以head爲基準點將序列劃分爲left和right兩個子序列,而後而後對left和right進行一樣操做並將結果quickSort(left)和quickSort(right)與基準元素head鏈接起來,如此遞歸操做,直到全部子序列都爲空,便已排好序。

scala代碼實現:

object QuickSort extends App { /** * 快速排序 * * @param list 待排序列表 * @tparam A 列表元素類型 * @return */ def quickSort[A <% Ordered[A]](list: List[A]): List[A] = list match { case Nil => List() case head :: tail => val (left, right) = tail.partition(_ < head) quickSort(left) ::: head :: quickSort(right) } val list = List(1, 13, 7, 5, 8, 9, 20, 43, 11, 8) println(quickSort(list)) }

3. 插入排序

原理:

經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入,直到將全部未排序數據都插入到已排序序列中,排序便完成編程

scala實現思路:

  1. 新建一個insert函數實現將一個元素插入到已排序序列的功能,函數簽名以下 def insert(a: A, accOrdereds: List[A]): List[A],其中accOrdereds爲已經排序序列,且是累積的,即每次insert時傳入的都是當前最新的已排序序列。此函數實現思路也是使用模式匹配來實現。
    這種狀況下
  2. 新建一sort函數,函數簽名以下:
    def sort(unSorteds: List[A], accOrdereds: List[A]): List[A]
    其中unSorteds是以模式匹配的方式匹配頭和尾,將頭元素使用insert函數插入到累積的已排序的序列。而後再使用sort進行下一輪插入。如此遞歸執行,直到爲排序序列unSorteds爲空,返回累積已經排好序的序列

注意
scala中List的頭(head)是List中第一個元素,List的尾(tail)是去掉頭元素(head)後的List函數式編程

scala代碼實現:

object InsertionSort extends App { /** * @param list 待排列表 * @tparam A 列表元素類型 * @return */ def insertionSort[A <% Ordered[A]](list: List[A]): List[A] = { /** * @param unSorteds 待排列表 * @param accOrdereds 累積有序列表 * @return 有序列表 */ @tailrec def sort(unSorteds: List[A], accOrdereds: List[A]): List[A] = unSorteds match { case ha :: ta => sort(ta, insert(ha, accOrdereds)) case Nil => accOrdereds } /** * @param a 待插入元素 * @param accOrdereds 累積有序列表 * @return */ def insert(a: A, accOrdereds: List[A]): List[A] = accOrdereds match { case h :: t if (a > h) => h :: insert(a, t) case _ => a :: accOrdereds } sort(list, Nil) } val list = List(1,13,7,5,8,9,20,43,11,8) println(insertionSort(list).mkString(",")) }

4. 歸併排序

原理

使用分治思想,將序列劃分爲若干個只有一個元素的子序列,重複進行merge排序操做,將子序列兩兩合併,直到最後只剩下一個子序列,這個子序列就是已排好的序列函數

scala實現思路:

  1. 建立一個merge函數用於合併兩個排好序的子序列
    def merge(as: List[A], bs: List[A]): List[A]
    實現方式經過內建一個loop函數,實現對兩個序列的遍歷和排序,loop函數簽名以下:
    def loop(cs: List[A], ds: List[A], accSorteds: List[A]): List[A]
    cs和ds是兩個已排序序列,accSorteds是累積排序序列,cs和ds合併過程當中產生的新的有序列序列
  2. 新建一個劃分序列並將劃分序列合併排序的函數:
    splitIn2AndSort(as: List[A]): List[A]

scala代碼實現

object MergeSort extends App { def mergeSort[A <% Ordered[A]](list: List[A]): List[A] = { /** * @param p 待排序的包含兩個列表的元組 * @return */ def sort(p: (List[A], List[A])): List[A] = { p match { case (Nil, Nil) => Nil case (a :: Nil, Nil) => a :: Nil case (Nil, a :: Nil) => a :: Nil case (as, bs) => merge(splitIn2AndSort(as), splitIn2AndSort(bs)) } } /** * 將給定列表劃分爲兩個列表,並歸併排序返回一個有序列表 * @param as 待劃分列表 * @return */ def splitIn2AndSort(as: List[A]): List[A] = sort(splitIn2(as)) /** * 合併兩個有序列表 * @param as 有序列表 * @param bs 有序列表 * @return 合併後的有序列表 */ def merge(as: List[A], bs: List[A]): List[A] = { def loop(cs: List[A], ds: List[A], accSorteds: List[A]): List[A] = (cs, ds) match { case (Nil, Nil) => accSorteds case (hc :: tc, hd :: td) => if (hc < hd) loop(tc, ds, hc :: accSorteds) else loop(td, cs, hd :: accSorteds) case (hc :: tc, Nil) => loop(tc, Nil, hc :: accSorteds) case (Nil, hd :: td) => loop(Nil, td, hd :: accSorteds) } loop(as, bs, Nil).reverse } def splitIn2(as: List[A]): (List[A], List[A]) = { val mid = as.length / 2 (as.slice(0, mid), as.slice(mid, as.length)) } splitIn2AndSort(list) } val list = List(1, 13, 7, 5, 8, 9, 20, 43, 11, 8) println(mergeSort(list).mkString(",")) }

5. 選擇排序

原理

從原序列中依次移出符合條件(最大或最小)的元素,放入到有序序列中,直到原序列吳待排序元素oop

scala實現思路:

  1. 新建一函數:
    def select(remains: List[A], sorteds: List[A], accSorteds: List[A]): List[A]
    實現從剩餘的未排序序列remains中選出符合條件的元素,將它追加到已排序序列sorteds和累積已排序序列accSorteds中
  2. 新建函數:
    def sort(remains: List[A], accSorteds: List[A]): List[A]
    用來執行一趟選擇排序過程,將排序結果累積在accSorteds中,當remains爲空時,排序結束,返回accSorteds

scala代碼實現:

object SelectionSort extends App { def selectionSort[A <% Ordered[A]](list: List[A]): List[A] = { /** * @param unSorteds 未排序列表 * @param accSorteds 累積最終的有序列表 * @return */ def sort(unSorteds: List[A], accSorteds: List[A]): List[A] = unSorteds match { case h :: t => select(unSorteds, Nil, accSorteds) case Nil => accSorteds } /** * * @param unSorteds 未排序列表 * @param sorteds 選擇出的元素組成的有序列表 * @param accSorteds 累積最終的有序列表 * @return */ @tailrec def select(unSorteds: List[A], sorteds: List[A], accSorteds: List[A]): List[A] = unSorteds match { case h1 :: h2 :: t => if (h1 < h2) select(h2 :: t, h1 :: sorteds, accSorteds) else select(h1 :: t, h2 :: sorteds, accSorteds) case h :: Nil => sort(sorteds, h :: accSorteds) case Nil => sort(sorteds, accSorteds) } sort(list, Nil) } val list = List(1, 13, 7, 5, 8, 9, 20, 43, 11, 8) println(selectionSort(list)) } 

以上五種排序算均採用scala函數式方式實現,實現過程多采用遞歸思惟和模式匹配,這也是函數式編程一般使用的方式。ui

相關文章
相關標籤/搜索