用 JavaScript 實現鏈表操做 - 10 Move Node In-place

TL;DR

用 in-place 的方式把一個鏈表的首節點移到另外一個鏈表(不改變鏈表的引用),系列目錄見 前言和目錄javascript

需求

實現一個 moveNode() 函數,把源鏈表的頭結點移到目標鏈表的開頭。要求是不能修改兩個鏈表的引用。java

var source = 1 -> 2 -> 3 -> null
var dest = 4 -> 5 -> 6 -> null
moveNode(source, dest)
source === 2 -> 3 -> null
dest === 1 -> 4 -> 5 -> 6 -> null

當碰到如下的狀況應該拋出異常:node

  • 源鏈表爲 nullgit

  • 目標鏈表爲 nullgithub

  • 源鏈表是空節點,data 屬性爲 null 的節點定義爲空節點。算法

前一個 kata 不一樣的是,這個 kata 是在不改變引用的狀況下修改兩個鏈表自身。所以 moveNode() 函數不須要返回值。同時這個 kata 也提出了 空節點 的概念。空節點會用於目標鏈表爲空的狀況(爲了保持引用),在函數執行以後,目標鏈表會由空節點變成一個包含一個節點的鏈表。segmentfault

你可使用 第一個 katapush 方法。數據結構

最優的方案

這個算法考的是對鏈表節點的插入和刪除。基本只對 source 和 dest 分別作一次操做,因此不用區分遞歸和循環。大體思路爲:函數

  1. source 作刪除一個節點的操做。若是隻有一個節點就直接置空。若是有多個節點,就把第二個節點的值賦給頭節點,而後讓頭結點指向第三個節點。測試

  2. dest 作插入一個節點的操做。若是頭結點爲空就直接賦值,不然把頭結點複製一份,做爲第二個節點插入到鏈表中,再把新值賦給頭結點。

代碼以下:

function moveNode(source, dest) {
  if (!source || !dest || source.data === null) throw new Error("invalid arguments")

  const data = source.data

  if (source.next) {
    source.data = source.next.data
    source.next = source.next.next
  } else {
    source.data = null
  }

  if (dest.data === null) {
    dest.data = data
  } else {
    dest.next = new Node(dest.data, dest.next)
    dest.data = data
  }
}

遞歸方案

這是我最開始思考的方案,差異在於對 dest 如何插入新節點的處理上用了遞歸。思路是把全部節點的 data 日後移一位,即把新值賦給第一個節點,第一個節點的值賦給第二個節點,第二個節點的值賦給第三個節點,以此類推。但實際操做中的順序必須是反的,就是把倒數第二個節點的值賦給最後一個節點,倒數第三個節點的值賦給倒數第二個節點…… 這個思路對 dest 操做了 N 次,不如上一個解法的 1 次操做高效。不過也算是個有意思的遞歸用例,因此我仍然把它放了上來。

代碼以下,主要看 pushInPlaceV2

function moveNodeV2(source, dest) {
  if (source === null || dest === null || source.isEmpty()) {
    throw new Error('invalid arguments')
  }

  pushInPlaceV2(dest, source.data)

  if (source.next) {
    source.data = source.next.data
    source.next = source.next.next
  } else {
    source.data = null
  }
}

function pushInPlaceV2(head, data) {
  if (!head) return new Node(data)

  if (!head.isEmpty()) head.next = pushInPlaceV2(head.next, head.data)
  head.data = data
  return head
}

總結

老是使用遞歸會產生慣性,致使忽略了數據結構的基本特性。鏈表的特性就是插入和刪除的便利,改改引用就成了。

算法相關的代碼和測試我都放在 GitHub 上,若是對你有幫助請幫我點個贊!

參考資料

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

相關文章
相關標籤/搜索