TL;DR
一次遍历取两个排序链表的交集,系列目次见 前言和目次 。
需求
完成函数 sortedIntersect()
取两个已排序的链表的交集,交集指两个链表都有的节点,节点不一定一连。每一个链表应当只遍历一次。效果链表中不能包括反复的节点。
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 的下一个节点反复该步骤。但这题有 每一个链表只能遍历一次 的限定,那末如何做呢?
我们先假象有两个指针 p1
和 p2
,离别指向两个链表的首节点。当我们对照 p1
和 p2
的值时,有这几种状况:
p1.data === p2.data
,这时候节点一定交集,到场效果链表中。由于两个节点都用过了,我们能够同时后移p1
和p2
比较下一对节点。p1.data < p2.data
,我们应当今后挪动p1
,不动p2
,由于链表是升序分列的,p1
的后续节点有可能会跟p2
一样大。p1.data > p2.data
,跟上面相反,挪动p2
。p1
或p2
为空,背面一定没有交集了,遍历完毕。
基本思绪就是如许,递归和轮回都是云云。
递归解法
代码以下:
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
}