用 JavaScript 實現鏈表操做 - 16 Sorted Intersect

TL;DR

一次遍歷取兩個排序鏈表的交集,系列目錄見 前言和目錄node

需求

實現函數 sortedIntersect() 取兩個已排序的鏈表的交集,交集指兩個鏈表都有的節點,節點不必定連續。每一個鏈表應該只遍歷一次。結果鏈表中不能包含重複的節點。git

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

分析

最容易想到的解法多是從鏈表 A 中取一個節點,而後遍歷鏈表 B 找到相同的節點加入結果鏈表,最後取鏈表 A 的下一個節點重複該步驟。但這題有 每一個鏈表只能遍歷一次 的限制,那麼如何作呢?github

咱們先假象有兩個指針 p1p2,分別指向兩個鏈表的首節點。當咱們對比 p1p2 的值時,有這幾種狀況:segmentfault

  1. p1.data === p2.data ,這時節點確定交集,加入結果鏈表中。由於兩個節點都用過了,咱們能夠同時後移 p1p2 比較下一對節點。函數

  2. p1.data < p2.data ,咱們應該日後移動 p1 ,不動 p2 ,由於鏈表是升序排列的,p1 的後續節點有可能會跟 p2 同樣大。測試

  3. p1.data > p2.data ,跟上面相反,移動 p2指針

  4. p1p2 爲空,後面確定沒有交集了,遍歷結束。code

基本思路就是這樣,遞歸和循環都是如此。排序

遞歸解法

代碼以下:遞歸

function sortedIntersect(first, second) {
  if (!first || !second) return null

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

function nextDifferent(node) {
  let nextNode = node.next
  while (nextNode && nextNode.data === node.data) nextNode = nextNode.next
  return nextNode
}

須要注意的是不能加入重複節點的判斷。我是在第 5 行兩個鏈表的節點相等後,日後遍歷到下一個值不一樣的節點,爲此單獨寫了個 nextDifferent 函數。這個作法比較符合個人思路,但其實也能夠寫進循環體中,各位能夠自行思考。

循環解法

代碼以下,不贅述了:

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

  while (p1 || p2) {
    if (!p1 || !p2) break

    if (p1.data === p2.data) {
      pr = pr.next = new Node(p1.data)
      p1 = nextDifferent(p1)
      p2 = nextDifferent(p2)
    } else if (p1.data < p2.data) {
      p1 = p1.next
    } else {
      p2 = p2.next
    }
  }

  return result.next
}

參考資料

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

相關文章
相關標籤/搜索