一次遍歷取兩個排序鏈表的交集,系列目錄見 前言和目錄 。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
咱們先假象有兩個指針 p1
和 p2
,分別指向兩個鏈表的首節點。當咱們對比 p1
和 p2
的值時,有這幾種狀況:segmentfault
p1.data === p2.data
,這時節點確定交集,加入結果鏈表中。由於兩個節點都用過了,咱們能夠同時後移 p1
和 p2
比較下一對節點。函數
p1.data < p2.data
,咱們應該日後移動 p1
,不動 p2
,由於鏈表是升序排列的,p1
的後續節點有可能會跟 p2
同樣大。測試
p1.data > p2.data
,跟上面相反,移動 p2
。指針
p1
或 p2
爲空,後面確定沒有交集了,遍歷結束。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 }