用 JavaScript 實現鏈表操做 - 14 Sorted Merge

TL;DR

把兩個升序排列的鏈表合併成一個,系列目錄見 前言和目錄node

需求

實現函數 sortedMerge() 把兩個升序排列的鏈表合併成一個新鏈表,新鏈表也必須是升序排列的。這個函數應該對每一個輸入的鏈表都只遍歷一次。git

var first = 2 -> 4 -> 6 -> 7 -> null
var second = 1 -> 3 -> 5 -> 6 -> 8 -> null
sortedMerge(first, second) === 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 6 -> 7 -> 8 -> null

有一些邊界狀況要考慮:firstsecond 可能爲 null ,在合併過程當中 firstsecond 的數據有可能先取完。若是一個鏈表爲空,就返回另外一個鏈表(即便它也爲空),不須要拋出異常。github

在作這個 kata 以前,建議先完成 Shuffle Mergesegmentfault

遞歸解法

代碼以下:app

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

  if (first.data <= second.data) {
    return new Node(first.data, sortedMerge(first.next, second))
  } else {
    return new Node(second.data, sortedMerge(first, second.next))
  }
}

跟上個 kata 相似的思路。不過爲了保證最後的結果是升序排列的,咱們要取兩個鏈表中值更小的首節點,添加到結果鏈表的末尾。思路就不贅述了 。函數

循環解法

循環是這個 kata 有意思的一點,不少邊界狀況的判斷也發生在這裏。很容易寫出這樣的 if/else測試

let [p1, p2] = [first, second]
while (p1 || p2) {
  if (p1 && p2) {
    if (p1.data <= p2.data) {
      // append p1 data to result
    } else {
      // append p2 data to result
    }
  } else if (p1) {
    // append p1 to result
  } else {
    // append p2 to result
  }
}

上面例子裏 p1p2 是指向兩個鏈表節點的指針,在循環中它們隨時可能變成空,所以要比較數據大小首先就要判斷兩個都不爲空。並且註釋中的 append 代碼也會有必定重複。指針

爲了解決這個問題,咱們能夠上個 kata 裏調換指針的方法。完整代碼以下:code

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

  while (p1 || p2) {
    // if either list is null, append the other one to the result list
    if (!p1 || !p2) {
      pr.next = (p1 || p2)
      break
    }

    if (p1.data <= p2.data) {
      pr = pr.next = new Node(p1.data)
      p1 = p1.next
    } else {
      // switch 2 lists to make sure it's always p1 <= p2
      [p1, p2] = [p2, p1]
    }
  }

  return result.next
}

第 7 行判斷 p1p2 爲空,而且把非空的鏈表直接添加到 result 末尾,省去了繼續循環每一個節點。第 17 行的指針調換讓 p1 始終小於等於 p2 ,從而避免了重複的 append 代碼 。其餘技巧如 dummy node 在以前的 kata 都有講,就很少說了。遞歸

參考資料

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

相關文章
相關標籤/搜索