|
| 1 | +// Intersection: Given two (singly) linked lists, determine if the two lists intersect. Return the intersecting node. |
| 2 | +// Note that the intersection is defined based on reference, not value. That is, if the kth node of the first linked |
| 3 | +// list is the exact same node (by reference) as the jth node of the second linked list, then they are intersecting. |
| 4 | + |
| 5 | +const assert = require("assert"); |
| 6 | +const { LinkedListNode } = require("../../lib/avc278/linkedlist"); |
| 7 | + |
| 8 | +/** |
| 9 | + * |
| 10 | + * @param {LinkedListNode} A input linked list to check against |
| 11 | + * @param {LinkedListNode} B input linked list to check against |
| 12 | + * @return {LinkedListNode|null} whether the input linked lists intersect |
| 13 | + * |
| 14 | + * For this problem, we need to figure out the length of the two linked lists in order to know where the possible |
| 15 | + * intersection occurs. This is because once the two linked lists intersect, the length from the intersection to the end |
| 16 | + * of the linked list is the same. And so, when we compare the linked lists after finding the size, we can offset the |
| 17 | + * longer linked list by the difference in size. When we first reach the end in the first traversal, we can check |
| 18 | + * immediately if the two tail nodes are the same. If they are not, we can exit early and return false since we know |
| 19 | + * they don't intersect. |
| 20 | + * |
| 21 | + * Otherwise, we offset the longer of the two linked lists, and step one node at a time until we reach nodes that are |
| 22 | + * equal. At this point, we have found our intersecting node, and return the node. In terms of runtime analysis, since |
| 23 | + * we do traverse both linked lists A and B, we require O(A + B) time. Since we only store size counters, and pointers |
| 24 | + * to sections in the linked lists, we only use up O(1) additional space. |
| 25 | + * |
| 26 | + * Runtime: O(A+B) |
| 27 | + * Space: O(1) |
| 28 | + * |
| 29 | + */ |
| 30 | +const intersection = (A, B) => { |
| 31 | + if (!A || !B) return null; |
| 32 | + |
| 33 | + let aSize = 1; |
| 34 | + let currA = A; |
| 35 | + while (currA.next) { |
| 36 | + aSize += 1; |
| 37 | + currA = currA.next; |
| 38 | + } |
| 39 | + |
| 40 | + let bSize = 1; |
| 41 | + let currB = B; |
| 42 | + while (currB.next) { |
| 43 | + bSize += 1; |
| 44 | + currB = currB.next; |
| 45 | + } |
| 46 | + |
| 47 | + if (currA !== currB) return null; |
| 48 | + |
| 49 | + let smaller = aSize < bSize ? A : B; |
| 50 | + let longer = aSize < bSize ? B : A; |
| 51 | + let diff = Math.abs(aSize - bSize); |
| 52 | + |
| 53 | + while (diff > 0) { |
| 54 | + longer = longer.next; |
| 55 | + diff -= 1; |
| 56 | + } |
| 57 | + |
| 58 | + while (smaller !== longer) { |
| 59 | + smaller = smaller.next; |
| 60 | + longer = longer.next; |
| 61 | + } |
| 62 | + |
| 63 | + return smaller; |
| 64 | +}; |
| 65 | + |
| 66 | +describe(module.filename, () => { |
| 67 | + it("should return false when given an empty linked list", () => { |
| 68 | + const a1 = null; |
| 69 | + const b1 = new LinkedListNode(1, null); |
| 70 | + |
| 71 | + assert.ok(!intersection(a1, b1)); |
| 72 | + }); |
| 73 | + it("should return false when the linked lists do not intersect, even though the node values are the same.", () => { |
| 74 | + /* |
| 75 | + * a1 -> a2 -> a3 -> a4 -> a5 -> a6 -> a7 |
| 76 | + * |
| 77 | + * b1 -> b2 -> b3 -> b4 -> b5 -> b6 |
| 78 | + */ |
| 79 | + const a7 = new LinkedListNode(7, null); |
| 80 | + const a6 = new LinkedListNode(6, a7); |
| 81 | + const a5 = new LinkedListNode(5, a6); |
| 82 | + const a4 = new LinkedListNode(4, a5); |
| 83 | + const a3 = new LinkedListNode(3, a4); |
| 84 | + const a2 = new LinkedListNode(2, a3); |
| 85 | + const a1 = new LinkedListNode(1, a2); |
| 86 | + |
| 87 | + const b6 = new LinkedListNode(6, null); |
| 88 | + const b5 = new LinkedListNode(5, b6); |
| 89 | + const b4 = new LinkedListNode(4, b5); |
| 90 | + const b3 = new LinkedListNode(3, b4); |
| 91 | + const b2 = new LinkedListNode(2, b3); |
| 92 | + const b1 = new LinkedListNode(1, b2); |
| 93 | + |
| 94 | + assert.ok(!intersection(a1, b1)); |
| 95 | + }); |
| 96 | + it("should return the intersecting node when the two linked lists intersect.", () => { |
| 97 | + /* |
| 98 | + * a1 -> a2 -> a3 -> a4 |
| 99 | + * \ |
| 100 | + * -> c1 -> c2 -> c3 |
| 101 | + * / |
| 102 | + * b1 -> b2 -> b3 |
| 103 | + */ |
| 104 | + const c3 = new LinkedListNode(7, null); |
| 105 | + const c2 = new LinkedListNode(6, c3); |
| 106 | + const c1 = new LinkedListNode(5, c2); |
| 107 | + |
| 108 | + const a4 = new LinkedListNode(4, c1); |
| 109 | + const a3 = new LinkedListNode(3, a4); |
| 110 | + const a2 = new LinkedListNode(2, a3); |
| 111 | + const a1 = new LinkedListNode(1, a2); |
| 112 | + |
| 113 | + const b3 = new LinkedListNode(3, c1); |
| 114 | + const b2 = new LinkedListNode(3, b3); |
| 115 | + const b1 = new LinkedListNode(3, b2); |
| 116 | + |
| 117 | + assert.equal(intersection(a1, b1), c1); |
| 118 | + }); |
| 119 | +}); |
0 commit comments