From 6a3070f1d00599ef72747144035c820f0f189d30 Mon Sep 17 00:00:00 2001 From: TheGeniusOfEternity Date: Fri, 21 Aug 2026 12:02:10 +0300 Subject: [PATCH 1/4] test(neetcode|roadmap|copy-list-with-random-pointer): testcases --- .../solution.test.ts | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 src/neetcode/linked-list/copy-list-with-random-pointer/solution.test.ts diff --git a/src/neetcode/linked-list/copy-list-with-random-pointer/solution.test.ts b/src/neetcode/linked-list/copy-list-with-random-pointer/solution.test.ts new file mode 100644 index 0000000..1e8b964 --- /dev/null +++ b/src/neetcode/linked-list/copy-list-with-random-pointer/solution.test.ts @@ -0,0 +1,157 @@ +import { copyRandomList, Node } from './solution'; + +describe('Copy List With Random Pointer | NeetCode | RoadMap | Testcases', () => { + const buildList = ( + arr: { val: number; randomIndex: number }[], + ): Node | null => { + if (arr.length === 0) { + return null; + } + const nodes = arr.map((item) => new Node(item.val)); + for (let i = 0; i < arr.length; i++) { + if (i < arr.length - 1) { + nodes[i].next = nodes[i + 1]; + } + const ri = arr[i].randomIndex; + nodes[i].random = ri >= 0 ? nodes[ri] : null; + } + return nodes[0]; + }; + + const areListsEqual = (a: Node | null, b: Node | null): boolean => { + const mapOriginal = new Map(); + const mapCopy = new Map(); + + let curr = a; + let idx = 0; + while (curr) { + mapOriginal.set(curr, idx++); + curr = curr.next; + } + + curr = b; + idx = 0; + while (curr) { + mapCopy.set(curr, idx++); + curr = curr.next; + } + + if (mapOriginal.size !== mapCopy.size) { + return false; + } + + let nodeA = a; + let nodeB = b; + while (nodeA && nodeB) { + if (nodeA.val !== nodeB.val) { + return false; + } + + const randomA = nodeA.random; + const randomB = nodeB.random; + + if (randomA === null && randomB === null) { + // ok + } else if (randomA !== null && randomB !== null) { + const idxA = mapOriginal.get(randomA); + const idxB = mapCopy.get(randomB); + if (idxA !== idxB) { + return false; + } + } else { + return false; + } + + nodeA = nodeA.next; + nodeB = nodeB.next; + } + + return true; + }; + + test('#1 Should return null for empty list', () => { + expect(copyRandomList(null)).toBeNull(); + }); + + test('#2 Should copy single node with random null', () => { + const head = new Node(1); + const copy = copyRandomList(head); + expect(copy).not.toBeNull(); + expect(copy?.val).toBe(1); + expect(copy?.next).toBeNull(); + expect(copy?.random).toBeNull(); + + expect(head.next).toBeNull(); + expect(head.random).toBeNull(); + }); + + test('#3 Should copy single node with random pointing to itself', () => { + const head = new Node(1); + head.random = head; + const copy = copyRandomList(head); + expect(copy).not.toBeNull(); + expect(copy?.val).toBe(1); + expect(copy?.next).toBeNull(); + + expect(copy?.random).toEqual(copy); + + expect(head.random).toEqual(head); + }); + + test('#4 Should copy list with 2 nodes and cross random', () => { + const nodes = buildList([ + { val: 1, randomIndex: 1 }, // 1 -> 2 + { val: 2, randomIndex: 0 }, // 2 -> 1 + ]); + const copy = copyRandomList(nodes); + expect(areListsEqual(nodes, copy)).toBe(true); + }); + + test('#5 Should copy list with random pointing to previous nodes', () => { + const nodes = buildList([ + { val: 1, randomIndex: -1 }, + { val: 2, randomIndex: 0 }, + { val: 3, randomIndex: 1 }, + { val: 4, randomIndex: 2 }, + ]); + const copy = copyRandomList(nodes); + expect(areListsEqual(nodes, copy)).toBe(true); + }); + + test('#6 Should copy list with random pointing to next nodes', () => { + const nodes = buildList([ + { val: 1, randomIndex: 3 }, + { val: 2, randomIndex: 2 }, + { val: 3, randomIndex: 1 }, + { val: 4, randomIndex: 0 }, + ]); + const copy = copyRandomList(nodes); + expect(areListsEqual(nodes, copy)).toBe(true); + }); + + test('#7 Should copy list with complex random (self, null, cross)', () => { + const nodes = buildList([ + { val: 1, randomIndex: 0 }, // self + { val: 2, randomIndex: -1 }, // null + { val: 3, randomIndex: 1 }, // points to node 2 + { val: 4, randomIndex: 3 }, // self + ]); + const copy = copyRandomList(nodes); + expect(areListsEqual(nodes, copy)).toBe(true); + }); + + test('#8 Should not modify original list', () => { + const original = buildList([ + { val: 1, randomIndex: 2 }, + { val: 2, randomIndex: -1 }, + { val: 3, randomIndex: 0 }, + ]); + const originalSnapshot = buildList([ + { val: 1, randomIndex: 2 }, + { val: 2, randomIndex: -1 }, + { val: 3, randomIndex: 0 }, + ]); + copyRandomList(original); + expect(areListsEqual(original, originalSnapshot)).toBe(true); + }); +}); From e89893645601034ce272f5e9f1410b06d160ae70 Mon Sep 17 00:00:00 2001 From: TheGeniusOfEternity Date: Fri, 21 Aug 2026 12:02:18 +0300 Subject: [PATCH 2/4] feat(neetcode|roadmap|copy-list-with-random-pointer): two pass solution --- .../copy-list-with-random-pointer/solution.ts | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts diff --git a/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts b/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts new file mode 100644 index 0000000..29e5992 --- /dev/null +++ b/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts @@ -0,0 +1,47 @@ +export class Node { + val; + next; + random; + + constructor( + val: number, + next: Node | null = null, + random: Node | null = null, + ) { + this.val = val; + this.next = next; + this.random = random; + } +} + +/** + * Creates a deep copy of the list. + * @param {Node} head - the head of a linked list where each node contains an additional pointer `random`, + * which may point to any node in the list, or `null` + * @return the head of the copied linked list + */ +export const copyRandomList = (head: Node | null): Node | null => { + if (head === null) { + return null; + } + + const nodes = new Map(); + let curr: Node | null = head; + + while (curr) { + nodes.set(curr, new Node(curr.val)); + curr = curr.next; + } + + curr = head; + while (curr) { + const copy = nodes.get(curr); + if (copy) { + copy.next = nodes.get(curr.next) ?? null; + copy.random = nodes.get(curr.random) ?? null; + } + curr = curr.next; + } + + return nodes.get(head) ?? null; +}; From 8515a739d0a7738693a32e342d03fbd798ee7e16 Mon Sep 17 00:00:00 2001 From: TheGeniusOfEternity Date: Fri, 21 Aug 2026 12:19:15 +0300 Subject: [PATCH 3/4] feat(neetcode|roadmap|copy-list-with-random-pointer): one pass solution --- .../copy-list-with-random-pointer/solution.ts | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts b/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts index 29e5992..59801f2 100644 --- a/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts +++ b/src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts @@ -21,25 +21,27 @@ export class Node { * @return the head of the copied linked list */ export const copyRandomList = (head: Node | null): Node | null => { - if (head === null) { - return null; - } - const nodes = new Map(); - let curr: Node | null = head; - - while (curr) { - nodes.set(curr, new Node(curr.val)); - curr = curr.next; - } + nodes.set(null, null); - curr = head; + let curr = head; while (curr) { - const copy = nodes.get(curr); - if (copy) { - copy.next = nodes.get(curr.next) ?? null; - copy.random = nodes.get(curr.random) ?? null; + if (!nodes.has(curr)) { + nodes.set(curr, new Node(0)); + } + if (!nodes.has(curr.next)) { + nodes.set(curr.next, new Node(0)); + } + if (!nodes.has(curr.random)) { + nodes.set(curr.random, new Node(0)); } + const currCopy = nodes.get(curr); + if (currCopy) { + currCopy.val = curr.val; + currCopy.next = nodes.get(curr.next) ?? null; + currCopy.random = nodes.get(curr.random) ?? null; + } + curr = curr.next; } From f2d62211719a9632084d3db1b510a44adfe72fd1 Mon Sep 17 00:00:00 2001 From: TheGeniusOfEternity Date: Fri, 21 Aug 2026 12:19:33 +0300 Subject: [PATCH 4/4] feat(neetcode|roadmap|docs): update table of contents --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b20716f..cc2ce24 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,7 @@ - [Task #36](src/neetcode/linked-list/linked-list-cycle) – _Linked List Cycle_ - _[check the task](https://neetcode.io/problems/linked-list-cycle-detection)_ - [Task #37](src/neetcode/linked-list/reorder-list) – _Reorder List_ - _[check the task](https://neetcode.io/problems/reorder-linked-list)_ - [Task #38](src/neetcode/linked-list/remove-nth-node-from-end-of-list) – _Remove Nth Node From End Of List_ - _[check the task](https://neetcode.io/problems/reorder-linked-list)_ + - [Task #39](src/neetcode/linked-list/copy-list-with-random-pointer) – _Copy List With Random Pointer_ - _[check the task](https://neetcode.io/problems/copy-linked-list-with-random-pointer)_ [BigFrontEnd - JavaScript Coding Questions](https://bigfrontend.dev/problem) - [1. implement curry()](src/bigfrontend/javascript/curry) - _Curry Function_ - [check the task](https://bigfrontend.dev/problem/implement-curry)