用 JavaScript 實現鏈表操做 - 13 Shuffle Merge

TL;DR

把兩個鏈表洗牌合併成一個,系列目錄見 前言和目錄node

需求

實現函數 shuffleMerge() 把兩個鏈表合併成一個。新鏈表的節點是交叉從兩個鏈表中取的。這叫洗牌合併。舉個例子,當傳入的鏈表爲 1 -> 2 -> 3 -> null7 -> 13 -> 1 -> null 時,合併後的鏈表爲 1 -> 7 -> 2 -> 13 -> 3 -> 1 -> null 。若是合併過程當中一個鏈表的數據先取完了,就從另外一個鏈表中取剩下的數據。這個函數應該返回一個新鏈表。git

var first = 3 -> 2 -> 8 -> null
var second = 5 -> 6 -> 1 -> 9 -> 11 -> null
shuffleMerge(first, second) === 3 -> 5 -> 2 -> 6 -> 8 -> 1 -> 9 -> 11 -> null

若是參數之一爲空,應該直接返回另外一個鏈表(即便另外一個鏈表也爲空),不須要拋異常。github

遞歸解法 1

代碼以下:segmentfault

function shuffleMerge(first, second) {
  if (!first || !second) return first || second

  const list = new Node(first.data, new Node(second.data))
  list.next.next = shuffleMerge(first.next, second.next)
  return list
}

解題思路是,首先判斷是否有一個鏈表爲空,有就返回另外一個,結束遞歸。這個判斷過了,下面確定是兩個鏈表都不爲空的狀況。咱們依次從兩個鏈表中取第一個節點組合成新鏈表,而後遞歸 shuffleMerge 兩個鏈表的後續節點,並把結果銜接到 list 後面。這段代碼基本跟題目描述的意思一致。函數

遞歸解法 2

在上面的基礎上咱們還能作個更聰明的版本,代碼以下:測試

function shuffleMergeV2(first, second) {
  if (!first || !second) return first || second
  return new Node(first.data, shuffleMerge(second, first.next))
}

經過簡單的調換 firstsecond 的順序,咱們能把遞歸過程從 「先取 first 的首節點再取 second 的首節點」 變成 「總老是取 first 的首節點」 。解法 1 中的三行代碼簡化成了一行。指針

循環解法

循環其實才是本題的考點,由於這題主要是考指針(引用)操做。尤爲是把 「依次移動兩個鏈表的指針」 寫進一個循環裏。不過上個解法中調換兩個鏈表順序的方式也能夠用到這裏。代碼以下:code

function shuffleMergeV3(first, second) {
  const result = new Node()
  let pr = result
  let [p1, p2] = [first, second]

  while (p1 || p2) {
    if (p1) {
      pr.next = new Node(p1.data)
      pr = pr.next
      p1 = p1.next
    }
    [p1, p2] = [p2, p1]
  }

  return result.next
}

首先咱們生成一個 dummy node result ,同時創建一個 pr 表明 result 的尾節點(方便插入)。兩個鏈表的指針分別叫 p1p2 。在每次循環中咱們都把 p1 的節點數據寫到 result 鏈表的末尾,而後修改指針指向下一個節點。經過 12 行的調換指針,咱們能夠保證下一次循環就是對另外一個鏈表進行操做了。這樣一直遍歷到兩個鏈表末尾,返回 result.next 結束。遞歸

參考資料

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

相關文章
相關標籤/搜索