用 JavaScript 實現鏈表操做 - 18 Recursive Reverse

TL;DR

用遞歸的方式反轉鏈表,系列目錄見 前言和目錄node

需求

實現函數 reverse() 用遞歸的方式反轉鏈表。例子以下:git

var list = 2 -> 1 -> 3 -> 6 -> 5 -> null
reverse(list) === 5 -> 6 -> 3 -> 1 -> 2 -> null

解法

讓咱們先思考一下遞歸的大概解法:github

function reverse(head) {
  const node = new Node(head.data)
  const rest = reverse(head.next)
  // 把 node 放到 rest 的末尾,並返回 rest
}

麻煩的地方就在最後,把節點加入鏈表的末尾須要首先遍歷整個鏈表,這無疑很是低效。咱們在上一個 kata 的循環裏是怎麼解決的呢?維護一個 result 變量表明反轉鏈表,而後每次把新節點放到 result 的頭部,同時把新節點當作新的 result ,大概這個樣子:segmentfault

let result
for (let node = list; node; node = node.next) {
  result = new Node(node.data, result)
}

爲了在遞歸裏達到一樣的效果,咱們也必須維護這麼一個變量。爲了在每次遞歸過程當中都能用到這個變量,咱們得把它當函數的參數傳遞下去,reverse 的函數簽名就變成這樣:函數

function reverse(head, acc) { ... }

這裏 acc 就是反轉的鏈表。整理一番後的代碼以下:測試

function reverse(head, acc = null) {
  return head ? reverse(head.next, new Node(head.data, acc)) : acc
}

上面這段代碼同時也是尾遞歸。在遞歸函數中開額外的參數非常常見的作法,也是尾遞歸優化的必要手段。優化

參考資料

Codewars Kata
GitHub 的代碼實現
GitHub 的測試rest

相關文章
相關標籤/搜索