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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from typing import List, Union, Collection, Mapping, Optional
from abc import ABC, abstractmethod

# Definition for a Node.
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = int(x)
self.next = next
self.random = random

class Solution:
def copyRandomList(self, head: 'Optional[Node]') -> 'Optional[Node]':
"""
Approach 1: Using HashMap
Time Complexity: O(n)
Space Complexity: O(n)
"""
if not head:
return None

# Map old nodes to new nodes
old_to_new = {}

# First pass: create all new nodes
curr = head
while curr:
old_to_new[curr] = Node(curr.val)
curr = curr.next

# Second pass: assign next and random pointers
curr = head
while curr:
if curr.next:
old_to_new[curr].next = old_to_new[curr.next]
if curr.random:
old_to_new[curr].random = old_to_new[curr.random]
curr = curr.next

return old_to_new[head]
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Definition for a Node.
export class _Node {
val: number;
next: _Node | null;
random: _Node | null;

constructor(val?: number, next?: _Node | null, random?: _Node | null) {
this.val = val ?? 0;
this.next = next ?? null;
this.random = random ?? null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { _Node } from "./Node";

function copyRandomList(head: _Node | null): _Node | null {
/**
* Approach: Using HashMap
* Time Complexity: O(n)
* Space Complexity: O(n)
*/
if (head === null) {
return null;
}

// Map old nodes to new nodes
const oldToNew = new Map<_Node, _Node>();

// First pass: create all new nodes
let curr: _Node | null = head;
while (curr !== null) {
oldToNew.set(curr, new _Node(curr.val));
curr = curr.next;
}

// Second pass: assign next and random pointers
curr = head;
while (curr !== null) {
const newNode = oldToNew.get(curr)!;

if (curr.next !== null) {
newNode.next = oldToNew.get(curr.next)!;
}

if (curr.random !== null) {
newNode.random = oldToNew.get(curr.random)!;
}

curr = curr.next;
}

return oldToNew.get(head)!;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import unittest
from src.my_project.interviews.top_150_questions_round_22\
.ex_58_copy_list_with_random_pointer import Solution, Node

class CopyListWithRandomPointerTestCase(unittest.TestCase):

def create_linked_list_with_random(self, values):
"""
Helper function to create a linked list with random pointers.

:param values: List of [val, random_index] pairs
:return: Head of the linked list
"""
if not values:
return None

# Create all nodes first
nodes = []
for val, _ in values:
nodes.append(Node(val))

# Connect next pointers and random pointers
for i in range(len(nodes)):
if i < len(nodes) - 1:
nodes[i].next = nodes[i + 1]

random_index = values[i][1]
if random_index is not None:
nodes[i].random = nodes[random_index]

return nodes[0]

def linked_list_to_list(self, head):
"""
Helper function to convert linked list with random pointers to list format.

:param head: Head of the linked list
:return: List of [val, random_index] pairs
"""
if not head:
return []

# Create a map of nodes to their indices
node_to_index = {}
curr = head
index = 0
while curr:
node_to_index[curr] = index
curr = curr.next
index += 1

# Build the result
result = []
curr = head
while curr:
random_index = node_to_index.get(curr.random) if curr.random else None
result.append([curr.val, random_index])
curr = curr.next

return result

def test_example_1(self):
# Input: head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
# Output: [[7,null],[13,0],[11,4],[10,2],[1,0]]
solution = Solution()
head = self.create_linked_list_with_random([[7, None], [13, 0], [11, 4], [10, 2], [1, 0]])
copied_head = solution.copyRandomList(head)
result = self.linked_list_to_list(copied_head)
target = [[7, None], [13, 0], [11, 4], [10, 2], [1, 0]]
self.assertEqual(result, target)

# Verify deep copy - nodes should be different objects
self.assertIsNot(head, copied_head)

def test_example_2(self):
# Input: head = [[1,1],[2,1]]
# Output: [[1,1],[2,1]]
solution = Solution()
head = self.create_linked_list_with_random([[1, 1], [2, 1]])
copied_head = solution.copyRandomList(head)
result = self.linked_list_to_list(copied_head)
target = [[1, 1], [2, 1]]
self.assertEqual(result, target)

# Verify deep copy
self.assertIsNot(head, copied_head)

def test_example_3(self):
# Input: head = [[3,null],[3,0],[3,null]]
# Output: [[3,null],[3,0],[3,null]]
solution = Solution()
head = self.create_linked_list_with_random([[3, None], [3, 0], [3, None]])
copied_head = solution.copyRandomList(head)
result = self.linked_list_to_list(copied_head)
target = [[3, None], [3, 0], [3, None]]
self.assertEqual(result, target)

# Verify deep copy
self.assertIsNot(head, copied_head)

def test_empty_list(self):
# Input: head = []
# Output: []
solution = Solution()
head = None
copied_head = solution.copyRandomList(head)
self.assertIsNone(copied_head)

def test_single_node(self):
# Input: head = [[1,null]]
# Output: [[1,null]]
solution = Solution()
head = self.create_linked_list_with_random([[1, None]])
copied_head = solution.copyRandomList(head)
result = self.linked_list_to_list(copied_head)
target = [[1, None]]
self.assertEqual(result, target)

# Verify deep copy
self.assertIsNot(head, copied_head)