Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Node, number>();
const mapCopy = new Map<Node, number>();

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);
});
});
49 changes: 49 additions & 0 deletions src/neetcode/linked-list/copy-list-with-random-pointer/solution.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
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 => {
const nodes = new Map<Node | null, Node | null>();
nodes.set(null, null);

let curr = head;
while (curr) {
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;
}

return nodes.get(head) ?? null;
};
Loading