|
1 | 1 | # [Problem 1334: Find the City With the Smallest Number of Neighbors at a Threshold Distance](https://leetcode.com/problems/find-the-city-with-the-smallest-number-of-neighbors-at-a-threshold-distance/description/?envType=daily-question)
|
2 | 2 |
|
3 | 3 | ## Initial thoughts (stream-of-consciousness)
|
| 4 | +- There's probably a very efficient way to do this, but what comes to mind initially is that we should just start by building the graph. I'll need to think of an appropriate representation for storing the nodes/edges. |
| 5 | +- Then I think we could have a hash table (keys: nodes; values: list of other nodes reachable in less than or equal to `distanceThreshold` steps). How can we build this? |
| 6 | +- Then we can loop through the nodes and return the one with the minimum number of neighbors (and the greatest ID number if there are multiple cities with that same minimum number of neighbors) |
4 | 7 |
|
5 | 8 | ## Refining the problem, round 2 thoughts
|
6 | 9 |
|
| 10 | +### Some implementation ideas |
| 11 | +- **Graph representation:** |
| 12 | + - Option 1: hash table |
| 13 | + - Let's store nodes in a `dict` (keys: node; values: direct connections...maybe as touples so that we can also store the edge weights?) |
| 14 | + - Note: if the weight of a given connection is more than `distanceThreshold`, we can just ignore it |
| 15 | + - We should also add edges bidirectionally |
| 16 | + - Option 2: adjacency matrix: |
| 17 | + - Rows and columns are nodes, and the entries tell us how far each node is from each other node (inf if there's no edge between the nodes) |
| 18 | + - I kind of like this approach, because I suspect we could use some sort of graph traversal algorithm to propagate the edges 🤔 |
| 19 | +- **Building up a table of cities within the threshold distance:** |
| 20 | + - Once we've added the edges, I think we'll need to do another loop through to see what's reachable within the threshold distance |
| 21 | + - We could do something like the following (for each node `i` in turn, in ascending order): |
| 22 | + - Set `d = distanceThreshold` |
| 23 | + - loop through everything within distance `d` of the current node. Suppose another node, `j` is in that list and is distance `x` from node `i`: |
| 24 | + - Increment node `i`'s counter |
| 25 | + - Now search for things that are within `d - x` of node `j` |
| 26 | + - Keep repeating this process until there are no more nodes within the threshold distance away |
| 27 | + - We could use either a stack or a queue to do the searching...there's probably a way to cache some of the computations so that we don't have to re-do them each time we visit a node |
| 28 | +- **Final loop:** |
| 29 | + - Initialize `minCity = [cities[0], reachable[0]]` |
| 30 | + - Then loop through each node in turn, replacing `minCity` if `reachable[i] < minCity[1] or (reachable[i] == minCity[1] and minCity[0] < cities[i])` |
| 31 | + - return `minCity[0]` |
| 32 | + - Note: since we know the cities range from `0...n-1`, we could also loop through in reverse order of the city numbers and just replace `minCity` if `reachable[i] < minCity[1]` |
| 33 | + |
| 34 | +### Other notes |
| 35 | +- I think solving this efficiently will require implementing a graph traversal algorithm. Essentially we need to know the shortest path between all pairs of nodes, and then count (for each node) the number of nodes within `distanceThreshold`. Then we return the node with the most reachable nodes (and if there's a tie, pick the one with the max ID value) |
| 36 | +- On [Wikipedia's entry on shortest path graph problems](https://en.wikipedia.org/wiki/Shortest_path_problem) we can see a few options: |
| 37 | + |
| 38 | + |
| 39 | +- Of these, either the Floyd-Warshall algorithm or Johnson's algorithm will give us the shortest paths between all pairs of nodes. The entry says Johnson's algorithm is faster on sparse graphs, but we don't know whether the graph is actually sparse. Let's just pick whatever looks easier to implement. |
| 40 | +- The [Floyd-Warshall algorithm](https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm) looks straightforward: |
| 41 | + |
| 42 | + |
| 43 | + |
| 44 | +- [Johnson's algorithm](https://en.wikipedia.org/wiki/Johnson%27s_algorithm) isn't quite as clearly described: |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +So...let's just go with the Floyd-Warshall algorithm. |
| 49 | +- I think we're good to implmeent this now |
| 50 | + |
| 51 | + |
7 | 52 | ## Attempted solution(s)
|
8 | 53 | ```python
|
9 |
| -class Solution: # paste your code here! |
10 |
| - ... |
| 54 | +class Solution: |
| 55 | + def findTheCity(self, n: int, edges: List[List[int]], distanceThreshold: int) -> int: |
| 56 | + # Implement the Floyd-Warshall algorithm following Wikipedia's pseudocode... |
| 57 | + # First: initialize the distances to infinity |
| 58 | + dists = [[float('inf')] * n for _ in range(n)] |
| 59 | + |
| 60 | + # The distance from each node to itself is zero |
| 61 | + for i in range(n): |
| 62 | + dists[i][i] = 0 |
| 63 | + |
| 64 | + # Add the edges (bidirectionally) |
| 65 | + for u, v, w in edges: |
| 66 | + dists[u][v] = w |
| 67 | + dists[v][u] = w |
| 68 | + |
| 69 | + # Fill in the shortest path between all pairs |
| 70 | + for k in range(n): |
| 71 | + for i in range(n): |
| 72 | + for j in range(n): |
| 73 | + if dists[i][j] > dists[i][k] + dists[k][j]: |
| 74 | + dists[i][j] = dists[i][k] + dists[k][j] |
| 75 | + |
| 76 | + # now loop through each node and keep track of the city wth the minimum number of neighbors (and max ID, of those) |
| 77 | + min_neighbors = float('inf') |
| 78 | + city = -1 |
| 79 | + |
| 80 | + for i in range(n): |
| 81 | + # count neighbors and update the minimum if needed |
| 82 | + neighbors = sum([d <= distanceThreshold for d in dists[i]]) |
| 83 | + |
| 84 | + if (neighbors < min_neighbors) or ((neighbors == min_neighbors) and (i > city)): |
| 85 | + min_neighbors = neighbors |
| 86 | + city = i |
| 87 | + |
| 88 | + return city |
11 | 89 | ```
|
| 90 | +- Given test cases pass |
| 91 | +- Submitting... |
| 92 | + |
| 93 | + |
| 94 | + |
| 95 | +Solved! |
0 commit comments