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,24 @@
from typing import List, Union, Collection, Mapping, Optional
from abc import ABC, abstractmethod

class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:

answer = dict()

for k, v in enumerate(nums):

if v in answer:
return [answer[v], k]
else:
answer[target - v] = k

return []








Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from typing import List, Union, Collection, Mapping, Optional
from abc import ABC, abstractmethod
import re

class Solution:
def isPalindrome(self, s: str) -> bool:

# To lowercase
s = s.lower()

# Remove non-alphanumeric characters
s = re.sub(pattern=r'[^a-zA-Z0-9]', repl='', string=s)

# Determine if s is palindrome or not
len_s = len(s)

for i in range(len_s//2):

if s[i] != s[len_s - 1 - i]:
return False

return True
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from typing import List, Union, Collection, Mapping, Optional
from abc import ABC, abstractmethod
from collections import deque


# Definition for a Node.
class Node:
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []


class Solution:
def cloneGraph(self, node: Optional['Node']) -> Optional['Node']:
if not node:
return None

# HashMap to store original node -> cloned node mapping
old_to_new = {}

# BFS approach
queue = deque([node])
old_to_new[node] = Node(node.val)

while queue:
curr = queue.popleft()

# Process all neighbors
for neighbor in curr.neighbors:
# If neighbor hasn't been cloned yet
if neighbor not in old_to_new:
# Clone the neighbor
old_to_new[neighbor] = Node(neighbor.val)
# Add to queue for processing
queue.append(neighbor)

# Add the cloned neighbor to the current cloned node's neighbors
old_to_new[curr].neighbors.append(old_to_new[neighbor])

return old_to_new[node]

Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
class _Node {
val: number
neighbors: _Node[]

constructor(val?: number, neighbors?: _Node[]) {
this.val = (val===undefined ? 0 : val)
this.neighbors = (neighbors===undefined ? [] : neighbors)
}
}

function cloneGraph(node: _Node | null): _Node | null {
if (!node) {
return null;
}

// HashMap to store original node -> cloned node mapping
const oldToNew = new Map<_Node, _Node>();

// BFS approach
const queue: _Node[] = [node];
oldToNew.set(node, new _Node(node.val));

while (queue.length > 0) {
const curr = queue.shift()!;

// Process all neighbors
for (const neighbor of curr.neighbors) {
// If neighbor hasn't been cloned yet
if (!oldToNew.has(neighbor)) {
// Clone the neighbor
oldToNew.set(neighbor, new _Node(neighbor.val));
// Add to queue for processing
queue.push(neighbor);
}

// Add the cloned neighbor to the current cloned node's neighbors
oldToNew.get(curr)!.neighbors.push(oldToNew.get(neighbor)!);
}
}

return oldToNew.get(node)!;
}

165 changes: 165 additions & 0 deletions tests/test_150_questions_round_22/test_86_clone_graph_round_22.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import unittest
from src.my_project.interviews.top_150_questions_round_22.ex_86_clone_graph import Solution, Node
from typing import Optional, List


class CloneGraphTestCase(unittest.TestCase):

def build_graph(self, adj_list: List[List[int]]) -> Optional[Node]:
"""Helper function to build a graph from adjacency list"""
if not adj_list:
return None

# Create all nodes first
nodes = [Node(i + 1) for i in range(len(adj_list))]

# Build connections
for i, neighbors in enumerate(adj_list):
for neighbor_val in neighbors:
nodes[i].neighbors.append(nodes[neighbor_val - 1])

return nodes[0] if nodes else None

def graph_to_adj_list(self, node: Optional[Node]) -> List[List[int]]:
"""Helper function to convert graph back to adjacency list for comparison"""
if not node:
return []

visited = {}
adj_list = []

def dfs(curr: Node):
if curr in visited:
return

visited[curr] = len(visited)
adj_list.append([])

for neighbor in curr.neighbors:
if neighbor not in visited:
dfs(neighbor)

# First pass: assign indices
dfs(node)

# Second pass: build adjacency list
visited.clear()
adj_list.clear()

def build_adj(curr: Node):
if curr in visited:
return

visited[curr] = len(visited)
neighbors_vals = [n.val for n in curr.neighbors]
adj_list.append(sorted(neighbors_vals))

for neighbor in curr.neighbors:
build_adj(neighbor)

build_adj(node)
return adj_list

def verify_deep_copy(self, original: Optional[Node], cloned: Optional[Node]) -> bool:
"""Verify that the clone is a deep copy (not sharing references)"""
if original is None and cloned is None:
return True
if original is None or cloned is None:
return False

visited_original = set()
visited_cloned = set()

def dfs(orig: Node, clone: Node) -> bool:
if orig is clone: # Same reference = not a deep copy
return False

if orig.val != clone.val:
return False

if len(orig.neighbors) != len(clone.neighbors):
return False

visited_original.add(orig)
visited_cloned.add(clone)

# Check neighbors
orig_neighbor_vals = sorted([n.val for n in orig.neighbors])
clone_neighbor_vals = sorted([n.val for n in clone.neighbors])

if orig_neighbor_vals != clone_neighbor_vals:
return False

# Recursively check unvisited neighbors
for orig_n, clone_n in zip(
sorted(orig.neighbors, key=lambda x: x.val),
sorted(clone.neighbors, key=lambda x: x.val)
):
if orig_n not in visited_original:
if not dfs(orig_n, clone_n):
return False

return True

return dfs(original, cloned)

def test_example_1(self):
"""
Example 1: Graph with 4 nodes
adjList = [[2,4],[1,3],[2,4],[1,3]]
"""
solution = Solution()
adj_list = [[2, 4], [1, 3], [2, 4], [1, 3]]
original = self.build_graph(adj_list)
cloned = solution.cloneGraph(original)

# Verify it's a deep copy
self.assertTrue(self.verify_deep_copy(original, cloned))

# Verify structure is the same
cloned_adj_list = self.graph_to_adj_list(cloned)
expected = [[2, 4], [1, 3], [2, 4], [1, 3]]
self.assertEqual(cloned_adj_list, expected)

def test_example_2(self):
"""
Example 2: Single node with no neighbors
adjList = [[]]
"""
solution = Solution()
adj_list = [[]]
original = self.build_graph(adj_list)
cloned = solution.cloneGraph(original)

# Verify it's a deep copy
self.assertTrue(self.verify_deep_copy(original, cloned))

# Verify structure
self.assertIsNotNone(cloned)
self.assertEqual(cloned.val, 1)
self.assertEqual(len(cloned.neighbors), 0)

def test_example_3(self):
"""
Example 3: Empty graph
adjList = []
"""
solution = Solution()
adj_list = []
original = self.build_graph(adj_list)
cloned = solution.cloneGraph(original)

# Both should be None
self.assertIsNone(original)
self.assertIsNone(cloned)

def test_two_nodes(self):
"""Test simple graph with two connected nodes"""
solution = Solution()
adj_list = [[2], [1]]
original = self.build_graph(adj_list)
cloned = solution.cloneGraph(original)

self.assertTrue(self.verify_deep_copy(original, cloned))
self.assertEqual(self.graph_to_adj_list(cloned), [[2], [1]])