questions C

This commit is contained in:
2025-05-25 10:16:13 +01:00
parent 1e0aebfbfd
commit e6a22f98f8
85 changed files with 16925 additions and 0 deletions

View File

@@ -0,0 +1,210 @@
title: Copy List with Random Pointer
slug: copy-list-with-random-pointer
difficulty: medium
leetcode_id: 138
leetcode_url: https://leetcode.com/problems/copy-list-with-random-pointer/
categories:
- linked-lists
- hash-tables
patterns:
- linkedlist-reversal
description: |
A linked list of length `n` is given such that each node contains an additional random pointer, which could point to any node in the list, or `null`.
Construct a **deep copy** of the list. The deep copy should consist of exactly `n` **brand new** nodes, where each new node has its value set to the value of its corresponding original node. Both the `next` and `random` pointer of the new nodes should point to new nodes in the copied list such that the pointers in the original list and copied list represent the same list state. **None of the pointers in the new list should point to nodes in the original list**.
For example, if there are two nodes `X` and `Y` in the original list, where `X.random --> Y`, then for the corresponding two nodes `x` and `y` in the copied list, `x.random --> y`.
Return *the head of the copied linked list*.
The linked list is represented in the input/output as a list of `n` nodes. Each node is represented as a pair of `[val, random_index]` where:
- `val`: an integer representing `Node.val`
- `random_index`: the index of the node (range from `0` to `n-1`) that the `random` pointer points to, or `null` if it does not point to any node.
Your code will **only** be given the `head` of the original linked list.
constraints: |
- `0 <= n <= 1000`
- `-10^4 <= Node.val <= 10^4`
- `Node.random` is `null` or is pointing to some node in the linked list.
examples:
- input: "head = [[7,null],[13,0],[11,4],[10,2],[1,0]]"
output: "[[7,null],[13,0],[11,4],[10,2],[1,0]]"
explanation: "A list with 5 nodes. Node 0 has value 7 and random points to null. Node 1 has value 13 and random points to node 0. Node 2 has value 11 and random points to node 4. And so on. The output is a deep copy with the same structure."
- input: "head = [[1,1],[2,1]]"
output: "[[1,1],[2,1]]"
explanation: "Two nodes where both random pointers point to node 1 (the second node with value 2)."
- input: "head = [[3,null],[3,0],[3,null]]"
output: "[[3,null],[3,0],[3,null]]"
explanation: "Three nodes all with value 3. The middle node's random pointer points to the first node."
explanation:
intuition: |
Imagine you're tasked with duplicating a spider web where each thread (the `next` pointer) connects to the next junction, but there are also random silk threads (`random` pointers) that can connect any junction to any other junction — or nowhere at all.
The challenge is that when you're building your copy, you need to connect the `random` pointer of a copied node to another *copied* node. But how do you find the copied version of the node that the original `random` points to?
Think of it like this: if you're copying a contact list where each person has a "best friend" field pointing to another person in the same list, you can't fill in the "best friend" until you've created entries for *everyone*. The core insight is that you need a way to **map original nodes to their copies**.
There are two elegant approaches:
1. **Hash Map**: Use a dictionary to store the mapping from each original node to its copy. This makes lookups instant but uses O(n) extra space.
2. **Interweaving**: Cleverly weave copied nodes directly into the original list (A → A' → B → B' → ...), so each copy sits right next to its original. The original's `random.next` gives you the copy's random target. Then unweave to separate the lists.
approach: |
We'll describe the **Hash Map Approach** as the primary solution due to its clarity:
**Step 1: Handle the edge case**
- If `head` is `null`, return `null` immediately
&nbsp;
**Step 2: First pass — create all copied nodes**
- Traverse the original list
- For each original node, create a new node with the same value
- Store the mapping `original_node → copied_node` in a hash map
- This ensures we have all copies created before wiring up pointers
&nbsp;
**Step 3: Second pass — wire up `next` and `random` pointers**
- Traverse the original list again
- For each original node:
- Set `copy.next = hash_map[original.next]` (or `null` if `original.next` is `null`)
- Set `copy.random = hash_map[original.random]` (or `null` if `original.random` is `null`)
- The hash map allows O(1) lookup of any copied node
&nbsp;
**Step 4: Return the copied head**
- Return `hash_map[head]` — the copy of the original head node
common_pitfalls:
- title: Copying Random Pointers to Original Nodes
description: |
A common mistake is to set `copy.random = original.random`. This makes the copy's random pointer point to a node in the *original* list, not the copied list.
The problem explicitly states: "None of the pointers in the new list should point to nodes in the original list."
You must translate every pointer to point to the corresponding *copy*.
wrong_approach: "copy.random = original.random"
correct_approach: "copy.random = hash_map[original.random]"
- title: Single Pass Without Pre-Creating Nodes
description: |
You might try to copy nodes and wire pointers in a single pass. But consider: when processing node A, its `random` might point to node Z which you haven't created yet.
Either use two passes (create all nodes first, then wire pointers), or create nodes on-demand and check the hash map before creating duplicates.
wrong_approach: "Single pass assuming nodes exist"
correct_approach: "Two-pass or create-on-demand with hash map"
- title: Not Handling Null Pointers
description: |
Both `next` and `random` can be `null`. When looking up in the hash map, ensure you handle the case where the key is `null`.
In Python, `hash_map.get(None)` returns `None`, which is correct. In other languages, you may need explicit null checks.
wrong_approach: "Unconditionally accessing hash_map[node]"
correct_approach: "Check if node is null before hash map lookup"
key_takeaways:
- "**Hash map for node mapping**: When cloning graph-like structures, a hash map from original to copy nodes enables O(1) pointer translation"
- "**Two-pass pattern**: Create all nodes first, then wire connections — this avoids forward reference problems"
- "**Space-time tradeoff**: The interweaving approach achieves O(1) space but is trickier to implement; hash map is clearer at O(n) space"
- "**Deep copy fundamentals**: This problem teaches the core concept of deep copying interconnected structures — applicable to graphs, trees with parent pointers, and more"
time_complexity: "O(n). We traverse the list twice — once to create copies, once to wire pointers. Each node is visited a constant number of times."
space_complexity: "O(n). The hash map stores a mapping for each of the `n` nodes. The output list itself also uses O(n) space, but that's required by the problem."
solutions:
- approach_name: Hash Map
is_optimal: true
code: |
class Node:
def __init__(self, x: int, next: 'Node' = None, random: 'Node' = None):
self.val = x
self.next = next
self.random = random
def copyRandomList(head: 'Node') -> 'Node':
if not head:
return None
# Map from original node to its copy
old_to_new = {}
# First pass: create all copied nodes
current = head
while current:
old_to_new[current] = Node(current.val)
current = current.next
# Second pass: wire up next and random pointers
current = head
while current:
copy = old_to_new[current]
# Wire next pointer (use .get() to handle None gracefully)
copy.next = old_to_new.get(current.next)
# Wire random pointer
copy.random = old_to_new.get(current.random)
current = current.next
# Return the copy of the head
return old_to_new[head]
explanation: |
**Time Complexity:** O(n) — Two linear passes through the list.
**Space Complexity:** O(n) — Hash map stores n mappings.
The hash map approach is intuitive: first create all copies, then use the map to translate every pointer from the original domain to the copy domain. The `.get()` method handles `None` keys gracefully by returning `None`.
- approach_name: Interweaving Nodes
is_optimal: false
code: |
def copyRandomList(head: 'Node') -> 'Node':
if not head:
return None
# Step 1: Interweave copied nodes into the original list
# Original: A -> B -> C
# After: A -> A' -> B -> B' -> C -> C'
current = head
while current:
copy = Node(current.val)
copy.next = current.next
current.next = copy
current = copy.next
# Step 2: Wire up random pointers for copied nodes
current = head
while current:
copy = current.next
if current.random:
# The copy of current.random is current.random.next
copy.random = current.random.next
current = copy.next
# Step 3: Unweave the lists to separate original and copy
current = head
copy_head = head.next
while current:
copy = current.next
# Restore original list
current.next = copy.next
# Wire copy list
if copy.next:
copy.next = copy.next.next
current = current.next
return copy_head
explanation: |
**Time Complexity:** O(n) — Three linear passes through the list.
**Space Complexity:** O(1) — No hash map; copies are woven into the original list temporarily.
This clever approach avoids extra space by placing each copy immediately after its original. The trick is that `original.random.next` gives us the copy of the random target. After wiring random pointers, we unweave to restore the original list and extract the copy. While space-efficient, it's more complex and modifies the original list temporarily.