用遞歸的方式反轉鏈表,系列目錄見 前言和目錄 。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 }
上面這段代碼同時也是尾遞歸。在遞歸函數中開額外的參數非常常見的作法,也是尾遞歸優化的必要手段。優化