← Nooks Interview Insights

Nooks·Software Engineer·Technical Phone Screen·Intermediate

IntermediatePrefer not to say
Jul 2026Remote

Summary

Interviewed for a software engineering role at Nooks and got a web crawler problem. Pretty classic BFS question but the follow-up discussion about complexity and revisiting pages is where it got interesting.

Questions Asked (3)

Q1

Given a start URL and a function that returns all hyperlinks on a page, write a function that crawls and returns all reachable pages on the same hostname using breadth-first traversal, visiting each URL at most once.

Algorithms & Data StructuresSystem Design
Author's notes

I went straight to BFS with a queue and a visited set, which was the right call.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Model the problem as a graph traversal where each URL is a node and hyperlinks are directed edges, then apply BFS starting from the seed URL using a queue. Use a visited set to prevent revisiting URLs and filter discovered links to only include those matching the starting hostname. Clearly communicate your data structures and walk through the algorithm before coding.

Pro tip: Proactively mention real-world concerns like handling relative vs. absolute URLs, URL normalization (trailing slashes, query params), and the fact that the link-fetching function likely involves I/O — signaling awareness of concurrency and rate-limiting opportunities that matter in production crawlers like those at Nooks.

1. Clarify Requirements & Constraints

Ask about edge cases: should query parameters be treated as distinct URLs, how should relative URLs be handled, and is the provided function synchronous or asynchronous? Confirm that 'same hostname' means exact host match (e.g., excluding subdomains).

2. Define Data Structures

Choose a queue (deque) for BFS traversal and a hash set for tracking visited URLs. Explain that the set gives O(1) lookup to ensure each URL is visited at most once.

3. Implement BFS Traversal

Initialize the queue and visited set with the start URL, then loop: dequeue a URL, call the provided link function, filter results to the same hostname and unvisited URLs, add them to the visited set, and enqueue them.

4. Handle URL Normalization & Hostname Filtering

Parse each discovered URL to extract its hostname (using urllib.parse or equivalent) and compare it to the start URL's hostname. Normalize URLs by stripping trailing slashes or fragments to avoid duplicate visits.

5. Discuss Complexity & Scalability

State that time complexity is O(V + E) where V is reachable pages and E is total hyperlinks, and space is O(V) for the visited set. Then discuss how this could be extended with concurrent workers, a distributed queue, or politeness delays for a production crawler.

Key Points to Mention

  • BFS guarantees level-order traversal and is preferred over DFS for crawling since it discovers nearby pages first and avoids deep recursion stack issues
  • A visited set (hash set) is essential to ensure each URL is processed at most once, preventing infinite loops on cyclic links
  • Hostname extraction and filtering using URL parsing (e.g., urllib.parse.urlparse) to correctly scope the crawl to the same domain
  • URL normalization considerations: handling relative URLs, trailing slashes, fragments (#), and query strings to avoid redundant visits
  • The link-fetching function abstracts I/O — in practice this is a network call, making it a natural candidate for async/concurrent execution (e.g., asyncio, thread pool) to improve throughput
  • Real-world extensions: robots.txt compliance, rate limiting, politeness delays, and distributed crawling with a shared frontier queue

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q2

How do you prevent the crawler from visiting the same URL more than once?

Algorithms & Data Structures
Author's notes

Answered with a visited set, nothing fancy.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by explaining the need for a visited set to track URLs and avoid duplicates. Then describe the data structure choices (e.g., hash set, Bloom filter) and how they integrate with the crawler's frontier. Finally, discuss trade-offs and scalability considerations.

Pro tip: Mention that using a Bloom filter can save memory but may introduce false positives, so you might combine it with a hash set for exact tracking of important URLs. Also, consider normalization of URLs to avoid duplicates that differ only by query parameters or fragments.

1. Identify the problem

Explain that without deduplication, the crawler may waste resources and overload servers by revisiting the same URL. This can lead to infinite loops and inefficient crawling.

2. Choose a data structure

Discuss using a hash set for exact tracking, which offers O(1) lookups but uses more memory. For large-scale crawls, consider a Bloom filter for probabilistic checking with lower memory footprint.

3. Integrate with crawler

Describe how to check the visited set before adding a URL to the frontier. If the URL is already visited, skip it; otherwise, add it to the set and enqueue it.

4. Handle URL normalization

Mention that URLs should be normalized (e.g., removing fragments, sorting query parameters, lowercasing host) to ensure that semantically identical URLs are treated as the same.

5. Consider scalability and persistence

For distributed crawlers, discuss using a distributed cache like Redis or a database to share the visited set across nodes. Also, consider persistence for resuming crawls.

Key Points to Mention

  • Hash set for exact deduplication with O(1) lookup
  • Bloom filter for memory-efficient probabilistic deduplication
  • URL normalization to avoid duplicates from different representations
  • Distributed visited set using Redis or similar for scalability
  • Trade-offs between memory usage and accuracy
  • Handling of dynamic content and session IDs that can cause duplicate URLs

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.

Q3

What are the time and space complexities of your crawler solution?

Algorithms & Data StructuresTechnical Trade-offs
Author's notes

Blanked for a moment on how to frame the variables cleanly.

Create a free account to read the full note

AI HintsAI Generated

Suggested Approach

Start by clearly defining the parameters of your crawler solution, such as the number of pages (N), average links per page (L), and the data structures used. Then, break down the time and space complexity for each major component (e.g., fetching, parsing, storing) and express them in Big-O notation. Finally, discuss any trade-offs or optimizations that affect these complexities.

Pro tip: Always relate the complexity to real-world constraints like network latency, memory limits, and concurrency, and mention how you might optimize for the common case. This shows you understand that theoretical complexity must be balanced with practical engineering.

1. Define Variables and Assumptions

Clearly state the input size variables (e.g., N = number of pages, L = average links per page) and any assumptions about the crawler's behavior (e.g., single-threaded, BFS order).

2. Analyze Time Complexity

Break down the time complexity for each phase: fetching (network I/O), parsing (HTML processing), and URL management (deduplication, queue operations). Sum them up and simplify to Big-O.

3. Analyze Space Complexity

Consider the space needed for the frontier queue, visited set, and any in-memory storage of page content. Express as a function of N and L.

4. Discuss Trade-offs and Optimizations

Mention how concurrency, distributed crawling, or using external storage (e.g., databases) can change the complexity, and the trade-offs involved.

5. Summarize and Conclude

Provide a concise summary of the overall time and space complexity, and reiterate any key assumptions or optimizations.

Key Points to Mention

  • Time complexity: O(N * (L + P)) where P is parsing time per page, often simplified to O(N * L) if parsing is constant.
  • Space complexity: O(N) for visited set and queue, plus O(N * S) if storing page content, where S is average page size.
  • Use of data structures: hash set for visited URLs (O(1) lookup), queue for BFS (O(1) enqueue/dequeue).
  • Impact of concurrency: with C threads, time can be reduced to O(N * (L + P) / C) but space may increase due to buffers.
  • Trade-offs: in-memory vs. disk-based storage, politeness delays, and handling of dynamic content.
  • Real-world constraints: network latency, rate limiting, and memory limits often dominate over theoretical complexity.

AI-generated suggestions, not part of the candidate's original notes. May be inaccurate — verify before relying on them.