Graphs Flashcards

1
Q
  1. Number of Islands
    Solved
    Medium
    Topics
    Companies: Facebook Microsoft Amazon TikTok Google

Given an m x n 2D binary grid grid which represents a map of ‘1’s (land) and ‘0’s (water), return the number of islands.

An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.

Example 1:

Input: grid = [
[“1”,”1”,”1”,”1”,”0”],
[“1”,”1”,”0”,”1”,”0”],
[“1”,”1”,”0”,”0”,”0”],
[“0”,”0”,”0”,”0”,”0”]
]
Output: 1

Example 2:

Input: grid = [
[“1”,”1”,”0”,”0”,”0”],
[“1”,”1”,”0”,”0”,”0”],
[“0”,”0”,”1”,”0”,”0”],
[“0”,”0”,”0”,”1”,”1”]
]
Output: 3

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 300
grid[i][j] is '0' or '1'.

https://leetcode.com/problems/number-of-islands/description/

A

from collections import deque
class Solution(object):
def numIslands(self, grid):
“””
:type grid: List[List[str]]
:rtype: int
“””

    seen = set()
    q = deque()
    q.append((0,0))
    ROWS = len(grid)
    COLS = len(grid[0])
    directions = [[0,1], [0,-1], [1,0], [-1,0]]

    def bfs(ir, ic):
        q.append((ir, ic))
        while q:
            r,c = q.popleft()
            for rm, cm in directions:
                row, col = rm + r, cm + c
                if row in range(ROWS) and col in range(COLS) and (row, col) not in seen and grid[r][c] == "1":
                    seen.add((row, col))
                    q.append((row, col))
    islands = 0
    for r in range(ROWS):
        for c in range(COLS):
            if (r,c) not in seen and grid[r][c] == "1":
                bfs(r, c)
                islands += 1
    return islands

DFS

from collections import deque
class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
if not grid:
return 0
rows = len(grid)
cols = len(grid[0])
visited = set()
directions = [[0,1], [0,-1], [-1,0], [1,0]]
islands = 0

    def dfs(ir, ic):
        visitstack = []
        visitstack.append((ir, ic))
        visited.add((ir, ic))
        while visitstack:
            r,c = visitstack.pop()
            for rm, cm in directions:
                row, col = rm+r, cm+c
                if row in range(rows) and col in range(cols) and (row, col) not in visited and grid[row][col] == "1":
                    visited.add((row,col))
                    visitstack.append((row, col))

    for r in range(rows):
        for c in range(cols):
            if (r,c) not in visited and grid[r][c] == "1":
                dfs(r,c)
                islands += 1
    return islands
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q
  1. Max Area of Island
    Solved
    Medium
    Topics
    Companies

You are given an m x n binary matrix grid. An island is a group of 1’s (representing land) connected 4-directionally (horizontal or vertical.) You may assume all four edges of the grid are surrounded by water.

The area of an island is the number of cells with a value 1 in the island.

Return the maximum area of an island in grid. If there is no island, return 0.

Example 1:

Input: grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]
Output: 6
Explanation: The answer is not 11, because the island must be connected 4-directionally.

Example 2:

Input: grid = [[0,0,0,0,0,0,0,0]]
Output: 0

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j] is either 0 or 1.

https://leetcode.com/problems/max-area-of-island/

A

class UnionFind:
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
self.size = [0] * n

def find(self, node):
    if self.parent[node] != node:
        self.parent[node] = self.find(self.parent[node])
    return self.parent[node]

def union(self, a, b):
    pa, pb = self.find(a), self.find(b)
    if pa != pb:
        if self.rank[pa] > self.rank[pb]:
            self.parent[pb] = pa
            self.size[pa] += self.size[pb]
        elif self.rank[pa] < self.rank[pb]:
            self.parent[pa] = pb
            self.size[pb] += self.size[pa]
        else:
            self.parent[pb] = pa
            self.rank[pa] += 1
            self.size[pa] += self.size[pb]
        return True
    else:
        # They are already connected
        return False
def init_component(self, c):
    if self.size[c] == 0:
        self.size[c] = 1

class Solution:
def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
ROWS = len(grid)
COLS = len(grid[0])
total = ROWS * COLS
uf = UnionFind(total)

    def idx(r, c):
        return r * COLS + c
    directions = [[0, 1], [0,-1], [1,0], [-1,0]]

    for r in range(ROWS):
        for c in range(COLS):
            if grid[r][c] == 1:
                uf.init_component(idx(r,c))
                for rm, cm in directions:
                    row = rm + r
                    col = cm + c
                    if row in range(ROWS) and col in range(COLS) and grid[row][col] == 1:
                        uf.init_component(idx(row,col))
                        uf.union(idx(r,c), idx(row, col))
    area = max(uf.size)
    return area

BFS

from collections import deque
class Solution(object):
def maxAreaOfIsland(self, grid):
“””
:type grid: List[List[int]]
:rtype: int
“””
seen = set()
rows = len(grid)
cols = len(grid[0])
max_area = 0
def bfs(r, c):
q = deque()
q.append((r,c))
seen.add((r,c))
directions = [[0,1], [0,-1], [1,0], [-1,0]]
area = 0
while q:
row,col = q.popleft()
area += 1
for dr, dc in directions:
r = dr + row
c = dc + col
if ((r,c ) not in seen and r in range(rows) and c in range(cols) and grid[r][c] == 1):
q.append((r,c))
seen.add((r,c))
return area
for r in range(rows):
for c in range(cols):
if ((r,c) not in seen and grid[r][c] == 1):
max_area = max(max_area, bfs(r,c))
return max_area

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q
  1. Clone Graph
    Solved
    Medium
    Topics
    Companies: FB

Given a reference of a node in a connected undirected graph.

Return a deep copy (clone) of the graph.

Each node in the graph contains a value (int) and a list (List[Node]) of its neighbors.

class Node {
public int val;
public List<Node> neighbors;
}</Node>

Test case format:

For simplicity, each node’s value is the same as the node’s index (1-indexed). For example, the first node with val == 1, the second node with val == 2, and so on. The graph is represented in the test case using an adjacency list.

An adjacency list is a collection of unordered lists used to represent a finite graph. Each list describes the set of neighbors of a node in the graph.

The given node will always be the first node with val = 1. You must return the copy of the given node as a reference to the cloned graph.

Example 1:

Input: adjList = [[2,4],[1,3],[2,4],[1,3]]
Output: [[2,4],[1,3],[2,4],[1,3]]
Explanation: There are 4 nodes in the graph.
1st node (val = 1)’s neighbors are 2nd node (val = 2) and 4th node (val = 4).
2nd node (val = 2)’s neighbors are 1st node (val = 1) and 3rd node (val = 3).
3rd node (val = 3)’s neighbors are 2nd node (val = 2) and 4th node (val = 4).
4th node (val = 4)’s neighbors are 1st node (val = 1) and 3rd node (val = 3).

Example 2:

Input: adjList = [[]]
Output: [[]]
Explanation: Note that the input contains one empty list. The graph consists of only one node with val = 1 and it does not have any neighbors.

Example 3:

Input: adjList = []
Output: []
Explanation: This an empty graph, it does not have any nodes.

Constraints:

The number of nodes in the graph is in the range [0, 100].
1 <= Node.val <= 100
Node.val is unique for each node.
There are no repeated edges and no self-loops in the graph.
The Graph is connected and all nodes can be visited starting from the given node.

https://leetcode.com/problems/clone-graph/description/

A

Definition for a Node.

from collections import deque
“””
class Node(object):
def __init__(self, val = 0, neighbors = None):
self.val = val
self.neighbors = neighbors if neighbors is not None else []
“””

class Solution(object):
def cloneGraph(self, node):
“””
:type node: Node
:rtype: Node
“””
if not node:
return None
mappings = {}
q = deque()
q.append(node)
new_node = Node(node.val)
mappings[node] = new_node
while q:
old = q.popleft()
new = mappings.get(old)
for neighbor in old.neighbors:
if neighbor not in mappings:
q.append(neighbor)
nn = mappings.get(neighbor, Node(neighbor.val))
mappings[neighbor] = nn
new.neighbors.append(nn)
return new_node

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q
  1. Walls and Gates
    Solved
    Medium
    Topics
    Companies

You are given an m x n grid rooms initialized with these three possible values.

-1 A wall or an obstacle.
0 A gate.
INF Infinity means an empty room. We use the value 231 - 1 = 2147483647 to represent INF as you may assume that the distance to a gate is less than 2147483647.

Fill each empty room with the distance to its nearest gate. If it is impossible to reach a gate, it should be filled with INF.

Example 1:

Input: rooms = [[2147483647,-1,0,2147483647],[2147483647,2147483647,2147483647,-1],[2147483647,-1,2147483647,-1],[0,-1,2147483647,2147483647]]
Output: [[3,-1,0,1],[2,2,1,-1],[1,-1,2,-1],[0,-1,3,4]]

Example 2:

Input: rooms = [[-1]]
Output: [[-1]]

Constraints:

m == rooms.length
n == rooms[i].length
1 <= m, n <= 250
rooms[i][j] is -1, 0, or 231 - 1.

https://leetcode.com/problems/walls-and-gates/description/

A

from collections import deque
class Solution(object):
def wallsAndGates(self, rooms):
“””
:type rooms: List[List[int]]
:rtype: None Do not return anything, modify rooms in-place instead.
“””
rows = len(rooms)
cols = len(rooms[0])
q = deque()
movements = [[0,1], [1,0], [-1, 0], [0,-1]]
is_valid = lambda r,c: (r >= 0 and r < rows) and (c >= 0 and c < cols) and rooms[r][c] == 2147483647
def bfs(q):
dist = 0
while q:
qlen = len(q)
for _ in range(qlen):
r, c = q.popleft()
for rm, cm in movements:
row, col = r + rm, c + cm
if is_valid(row, col):
q.append((row, col))
# This is important here, as soon as you see the neighbor set its distance so the is_valid will act like
# seen set. The first time a cell is seen is from the closest gate.
rooms[row][col] = dist+1
dist += 1
for r in range(rows):
for c in range(cols):
if rooms[r][c] == 0:
q.append((r, c))
bfs(q)
return rooms

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q
  1. Rotting Oranges
    Solved
    Medium
    Topics
    Companies

You are given an m x n grid where each cell can have one of three values:

0 representing an empty cell,
1 representing a fresh orange, or
2 representing a rotten orange.

Every minute, any fresh orange that is 4-directionally adjacent to a rotten orange becomes rotten.

Return the minimum number of minutes that must elapse until no cell has a fresh orange. If this is impossible, return -1.

Example 1:

Input: grid = [[2,1,1],[1,1,0],[0,1,1]]
Output: 4

Example 2:

Input: grid = [[2,1,1],[0,1,1],[1,0,1]]
Output: -1
Explanation: The orange in the bottom left corner (row 2, column 0) is never rotten, because rotting only happens 4-directionally.

Example 3:

Input: grid = [[0,2]]
Output: 0
Explanation: Since there are already no fresh oranges at minute 0, the answer is just 0.

Constraints:

m == grid.length
n == grid[i].length
1 <= m, n <= 10
grid[i][j] is 0, 1, or 2.

https://leetcode.com/problems/rotting-oranges/description/

A

from collections import deque
class Solution(object):
def orangesRotting(self, grid):
“””
:type grid: List[List[int]]
:rtype: int
“””
fresh = 0
q = deque()
rows = len(grid)
cols = len(grid[0])
visited = set() # maybe not needed.
for r in range(rows):
for c in range(cols):
if grid[r][c] == 1:
fresh += 1
elif grid[r][c] == 2:
q.append((r,c))
time = -1
is_valid = lambda r,c: 0 <= r < rows and 0 <= c < cols and grid[r][c] == 1
directions = [[0,1], [0,-1], [1,0], [-1,0]]
while q:
qlen = len(q)
time += 1
for _ in range(qlen):
rv, cv = q.popleft()
for rm, cm in directions:
row, col = rm + rv, cm + cv
if is_valid(row, col):
grid[row][col] = 2
q.append((row, col))
fresh -= 1

    if fresh > 0:
        return -1
    else:
        return max(0, time)
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q
  1. Pacific Atlantic Water Flow
    Solved
    Medium
    Topics
    Companies

There is an m x n rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island’s left and top edges, and the Atlantic Ocean touches the island’s right and bottom edges.

The island is partitioned into a grid of square cells. You are given an m x n integer matrix heights where heights[r][c] represents the height above sea level of the cell at coordinate (r, c).

The island receives a lot of rain, and the rain water can flow to neighboring cells directly north, south, east, and west if the neighboring cell’s height is less than or equal to the current cell’s height. Water can flow from any cell adjacent to an ocean into the ocean.

Return a 2D list of grid coordinates result where result[i] = [ri, ci] denotes that rain water can flow from cell (ri, ci) to both the Pacific and Atlantic oceans.

Example 1:

Input: heights = [[1,2,2,3,5],[3,2,3,4,4],[2,4,5,3,1],[6,7,1,4,5],[5,1,1,2,4]]
Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
Explanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below:
[0,4]: [0,4] -> Pacific Ocean
[0,4] -> Atlantic Ocean
[1,3]: [1,3] -> [0,3] -> Pacific Ocean
[1,3] -> [1,4] -> Atlantic Ocean
[1,4]: [1,4] -> [1,3] -> [0,3] -> Pacific Ocean
[1,4] -> Atlantic Ocean
[2,2]: [2,2] -> [1,2] -> [0,2] -> Pacific Ocean
[2,2] -> [2,3] -> [2,4] -> Atlantic Ocean
[3,0]: [3,0] -> Pacific Ocean
[3,0] -> [4,0] -> Atlantic Ocean
[3,1]: [3,1] -> [3,0] -> Pacific Ocean
[3,1] -> [4,1] -> Atlantic Ocean
[4,0]: [4,0] -> Pacific Ocean
[4,0] -> Atlantic Ocean
Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.

Example 2:

Input: heights = [[1]]
Output: [[0,0]]
Explanation: The water can flow from the only cell to the Pacific and Atlantic oceans.

Constraints:

m == heights.length
n == heights[r].length
1 <= m, n <= 200
0 <= heights[r][c] <= 105

https://leetcode.com/problems/pacific-atlantic-water-flow/description/

A

Double BFS/DFS from each coast find intersection

from collections import defaultdict
class Solution(object):
def pacificAtlantic(self, heights):
“””
:type heights: List[List[int]]
:rtype: List[List[int]]
“””
rows = len(heights)
cols = len(heights[0])
p_starts = set()
a_starts = set()
for r in range(rows):
p_starts.add((r, 0))
a_starts.add((r, cols-1))
for c in range(cols):
p_starts.add((0, c))
a_starts.add((rows-1, c))

    p_seen = set()
    a_seen = set()
    directions = [[0,1], [1,0], [0, -1], [-1,0]]

    def dfs(r,c, seen):
        visit_stack = []
        visit_stack.append((r,c))
        seen.add((r, c))
        is_valid = lambda r, c: 0 <= r < rows and 0 <= c < cols and (r,c) not in seen
        while visit_stack:
            ir, ic = visit_stack.pop()
            for rm, cm in directions:
                row, col = ir + rm, ic + cm
                if is_valid(row, col) and heights[ir][ic] <= heights[row][col]:
                    seen.add((row, col))
                    visit_stack.append((row,col))

    for r,c in p_starts:
        dfs(r,c, p_seen)
    for r,c in a_starts:
        dfs(r,c, a_seen)
    output = p_seen.intersection(a_seen)
    
    return output
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q
  1. Surrounded Regions
    Solved
    Medium
    Topics
    Companies

Given an m x n matrix board containing ‘X’ and ‘O’, capture all regions that are 4-directionally surrounded by ‘X’.

A region is captured by flipping all ‘O’s into ‘X’s in that surrounded region.

Example 1:

Input: board = [[“X”,”X”,”X”,”X”],[“X”,”O”,”O”,”X”],[“X”,”X”,”O”,”X”],[“X”,”O”,”X”,”X”]]
Output: [[“X”,”X”,”X”,”X”],[“X”,”X”,”X”,”X”],[“X”,”X”,”X”,”X”],[“X”,”O”,”X”,”X”]]
Explanation: Notice that an ‘O’ should not be flipped if:
- It is on the border, or
- It is adjacent to an ‘O’ that should not be flipped.
The bottom ‘O’ is on the border, so it is not flipped.
The other three ‘O’ form a surrounded region, so they are flipped.

Example 2:

Input: board = [[“X”]]
Output: [[“X”]]

Constraints:

m == board.length
n == board[i].length
1 <= m, n <= 200
board[i][j] is 'X' or 'O'.

https://leetcode.com/problems/surrounded-regions/description/

A

Start from the border, mark all you can reach as “E” then go over board again and replace any E with O and any O with X.
# Similar to the atlantic ocean problem, start on the coast.

class Solution(object):
def solve(self, board):
“””
:type board: List[List[str]]
:rtype: None Do not return anything, modify board in-place instead.
“””
directions = [(0, 1), (1, 0), (-1,0), (0,-1)]
seen = set()
rows = len(board)
cols = len(board[0])
is_border = lambda r,c: (r == 0 or r == rows -1) or (c == 0 or c == cols -1)
is_valid = lambda r, c: 0 <= r < rows and 0 <= c < cols and (r,c) not in seen and board[r][c] == “O” and not is_border(r,c)

    def dfs(r,c):
        board[r][c] = "E"
        for rm, cm in directions:
            row, col = r + rm, c + cm
            if is_valid(row, col):
                seen.add((row, col))
                dfs(row, col)
    
    for r in range(rows):
        for c in range(cols):
            if is_border(r, c) and board[r][c] == "O":
                seen.add((r, c))
                dfs(r,c)
    
    for r in range(rows):
        for c in range(cols):
            if board[r][c] == "E":
                board[r][c] = "O"
            else:
                board[r][c] = "X"
    
    return board
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q
  1. Course Schedule
    Solved
    Medium
    Topics
    Companies
    Hint

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1.

Return true if you can finish all courses. Otherwise, return false.

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]
Output: true
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0. So it is possible.

Example 2:

Input: numCourses = 2, prerequisites = [[1,0],[0,1]]
Output: false
Explanation: There are a total of 2 courses to take.
To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. So it is impossible.

Constraints:

1 <= numCourses <= 2000
0 <= prerequisites.length <= 5000
prerequisites[i].length == 2
0 <= ai, bi < numCourses
All the pairs prerequisites[i] are unique.

https://leetcode.com/problems/course-schedule/description/

A

from collections import defaultdict

class Solution(object):
def canFinish(self, numCourses, prerequisites):
“””
:type numCourses: int
:type prerequisites: List[List[int]]
:rtype: bool
“””
adj = defaultdict(list)
for course, preq in prerequisites:
adj[course].append(preq)
ncourses = numCourses
# Global Seen, we do not need to re-visit courses that have been completed.
seen = set()
def dfs(c):
if not adj[c]:
## Course has no preq.
return True
# This represents the current dfs stack.
cur_seen = set()
stack = []
stack.append(c)
while stack:
course = stack[-1]
# We do not pop the course yet because we do not if it’s processed or not
# One way to see it is that it needs to be popped twice first to add it’s children and
# second when all nodes bellow it are processed.
if course not in seen:
# Seeing this node the first time.
cur_seen.add(course)
seen.add(course)
else:
course = stack.pop()
if course in cur_seen:
cur_seen.remove(course)
for preq in adj[course]:
if preq not in seen:
stack.append(preq)
elif preq in cur_seen:
# Cycle
return False

        return True
    
    for c in range(numCourses):
        canComplete = dfs(c)
        if not canComplete:
            return False
        else:
            ncourses -= 1
    if ncourses > 0:
        return False
    return True
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q
  1. Course Schedule II
    Solved
    Medium
    Topics
    Companies
    Hint

There are a total of numCourses courses you have to take, labeled from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] = [ai, bi] indicates that you must take course bi first if you want to take course ai.

For example, the pair [0, 1], indicates that to take course 0 you have to first take course 1.

Return the ordering of courses you should take to finish all courses. If there are many valid answers, return any of them. If it is impossible to finish all courses, return an empty array.

Example 1:

Input: numCourses = 2, prerequisites = [[1,0]]
Output: [0,1]
Explanation: There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1].

Example 2:

Input: numCourses = 4, prerequisites = [[1,0],[2,0],[3,1],[3,2]]
Output: [0,2,1,3]
Explanation: There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. Both courses 1 and 2 should be taken after you finished course 0.
So one correct course order is [0,1,2,3]. Another correct ordering is [0,2,1,3].

Example 3:

Input: numCourses = 1, prerequisites = []
Output: [0]

Constraints:

1 <= numCourses <= 2000
0 <= prerequisites.length <= numCourses * (numCourses - 1)
prerequisites[i].length == 2
0 <= ai, bi < numCourses
ai != bi
All the pairs [ai, bi] are distinct.

https://leetcode.com/problems/course-schedule-ii/description/

A

Topological Sort
from collections import defaultdict
class Solution(object):
def findOrder(self, numCourses, prerequisites):
“””
:type numCourses: int
:type prerequisites: List[List[int]]
:rtype: List[int]
“””
order = []
seen = set()

    m = defaultdict(list)
    ncourses = numCourses
    for course, preq in prerequisites:
        m[course].append(preq)

    def dfs(start):
        if start in seen:
            return True
        cur_seen = set()
        stack = [start]
        
        while stack:
            course = stack[-1]

            if course not in seen:
                seen.add(course)
                cur_seen.add(course)
            else:
                course = stack.pop()
                if course in cur_seen:
                    order.append(course)
                    cur_seen.remove(course)
                m[course] = []
            for preq in m[course]:
                if preq not in seen:
                    stack.append(preq)
                else:
                    if preq in cur_seen:
                        return False
        return True

    for c in range(numCourses):
        if not dfs(c):
            return []
        ncourses -= 1
    if ncourses > 0:
        return []
    return order

BFS Top Sort

from collections import deque, defaultdict

class Solution:
def findOrder(self, numCourses: int, prerequisites: List[List[int]]) -> List[int]:
# BFS
q = deque()
order = []
indegree = [0] * numCourses
adj = defaultdict(list)
for course, preq in prerequisites:
# Note this is preq -> courses it feeds into
# So we can reduce their indegrees.
adj[preq].append(course)
indegree[course] += 1
for course in range(numCourses):
if indegree[course] == 0:
q.append(course)
seen = set()
while q:
course = q.popleft()
seen.add(course)
for preq in adj[course]:
indegree[preq] -= 1
if indegree[preq] == 0:
q.append(preq)
order.append(course)
if len(order) != numCourses:
return []
return order

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q
  1. Graph Valid Tree
    Solved
    Medium
    Topics
    Companies
    Hint

You have a graph of n nodes labeled from 0 to n - 1. You are given an integer n and a list of edges where edges[i] = [ai, bi] indicates that there is an undirected edge between nodes ai and bi in the graph.

Return true if the edges of the given graph make up a valid tree, and false otherwise.

Example 1:

Input: n = 5, edges = [[0,1],[0,2],[0,3],[1,4]]
Output: true

Example 2:

Input: n = 5, edges = [[0,1],[1,2],[2,3],[1,3],[1,4]]
Output: false

Constraints:

1 <= n <= 2000
0 <= edges.length <= 5000
edges[i].length == 2
0 <= ai, bi < n
ai != bi
There are no self-loops or repeated edges.

https://leetcode.com/problems/graph-valid-tree/description/

A

from collections import defaultdict

class Solution(object):
def validTree(self, n, edges):
“””
:type n: int
:type edges: List[List[int]]
:rtype: bool
“””
stack = []
visited = set()
adj = defaultdict(list)
for a, b in edges:
adj[a].append(b)
adj[b].append(a)
stack.append((0, -1))

    while stack:
        node, parent = stack.pop()
        visited.add(node)
        for neighbor in adj[node]:
            if neighbor not in visited:
                stack.append((neighbor, node))
            elif neighbor != parent:
                return False
    return len(visited) == n
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q
  1. Number of Connected Components in an Undirected Graph
    Solved
    Medium
    Topics
    Companies

You have a graph of n nodes. You are given an integer n and an array edges where edges[i] = [ai, bi] indicates that there is an edge between ai and bi in the graph.

Return the number of connected components in the graph.

Example 1:

Input: n = 5, edges = [[0,1],[1,2],[3,4]]
Output: 2

Example 2:

Input: n = 5, edges = [[0,1],[1,2],[2,3],[3,4]]
Output: 1

Constraints:

1 <= n <= 2000
1 <= edges.length <= 5000
edges[i].length == 2
0 <= ai <= bi < n
ai != bi
There are no repeated edges.

https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/description/

A

Can use DFS or BFS or Union Find

DFS
class Solution(object):
def countComponents(self, n, edges):
“””
:type n: int
:type edges: List[List[int]]
:rtype: int
“””
seen = set()
adj_list = defaultdict(list)
for i, j in edges:
adj_list[i].append(j)
adj_list[j].append(i)

    def dfs(i):
        seen.add(i)
        neighbors = adj_list[i]
        for neighbor in neighbors:
            if neighbor not in seen:
                dfs(neighbor)

    cc = 0
    for v in range(n):
        if v not in adj_list:
            cc += 1
        elif v not in seen:
            dfs(v)
            cc += 1
    return cc 

Union Find
from collections import defaultdict

class Solution:
def countComponents(self, n: int, edges: List[List[int]]) -> int:

    parent = [i for i in range(n)]
    rank = [0] * n

    def find(node):
        if parent[node] != node:
            parent[node] = find(parent[node])
        return parent[node]
    
    def union(a, b):
        pa, pb = find(a), find(b)

        if pa != pb:
            if rank[pa] > rank[pb]:
                parent[pb] = pa
            elif rank[pb] > rank[pa]:
                parent[pa] = pb
            else:
                parent[pb] = pa
                rank[pa] += 1

    adj = defaultdict(list)  
    for a, b in edges:
        adj[a].append(b)
        adj[b].append(a)
    
    for a in range(n):
        for b in adj[a]:
            union(a,b)
    
    cc = sum(1 for i in range(n) if parent[i] == i)
    return cc
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q
  1. Redundant Connection
    Solved
    Medium
    Topics
    Companies

In this problem, a tree is an undirected graph that is connected and has no cycles.

You are given a graph that started as a tree with n nodes labeled from 1 to n, with one additional edge added. The added edge has two different vertices chosen from 1 to n, and was not an edge that already existed. The graph is represented as an array edges of length n where edges[i] = [ai, bi] indicates that there is an edge between nodes ai and bi in the graph.

Return an edge that can be removed so that the resulting graph is a tree of n nodes. If there are multiple answers, return the answer that occurs last in the input.

Example 1:

Input: edges = [[1,2],[1,3],[2,3]]
Output: [2,3]

Example 2:

Input: edges = [[1,2],[2,3],[3,4],[1,4],[1,5]]
Output: [1,4]

Constraints:

n == edges.length
3 <= n <= 1000
edges[i].length == 2
1 <= ai < bi <= edges.length
ai != bi
There are no repeated edges.
The given graph is connected.

https://leetcode.com/problems/redundant-connection/description/

A

DFS

Union find or DFS/BFS
# DFS BFS you need to run the search as you encounter each connection, and then build the graph.

from collections import defaultdict

class Solution(object):
def findRedundantConnection(self, edges):
“””
:type edges: List[List[int]]
:rtype: List[int]
“””
adj = defaultdict(list)

    output = []

    def dfs(start, target):
        stack = []
        seen = set()
        stack.append(start)

        while stack:
            node = stack.pop()
            if node == target:
                output.append([start, target])
                return True
            seen.add(node)

            for neighbor in adj[node]:
                if neighbor not in seen:
                    stack.append(neighbor)
        return False
    
    for a,b in edges:
        if dfs(a,b):
            return output[-1]
        adj[a].append(b)
        adj[b].append(a)   

Union Find, return if you find parent of both are same.
class Solution(object):
def findRedundantConnection(self, edges):
“””
:type edges: List[List[int]]
:rtype: List[int]
“””
par = {}
rank = {}
ret = []
n_edges = len(edges)
for i in range(1, n_edges + 1):
par[i] = i
rank[i] = 1

    def find(node):
        p = node
        while p != par[p]:
            par[p] = par[par[p]]
            p = par[p]
        return p
    
    def union(a, b):
        par_a, par_b = find(a), find(b)
        rank_a, rank_b = rank[par_a], rank[par_b]

        if par_a == par_b:
            return [a,b]
        if rank_a > rank_b:
            par[par_b] = par_a
            rank[par_a] += rank_b
        elif rank_b > rank_a:
            par[par_a] = par_b
            rank[par_b] += rank_a
        else:
            par[par_b] = par_a

    for a,b in edges:
        redundant = union(a,b)
        if redundant:
            ret = redundant
    return ret
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q
  1. Word Ladder
    Solved
    Hard
    Topics
    Companies

A transformation sequence from word beginWord to word endWord using a dictionary wordList is a sequence of words beginWord -> s1 -> s2 -> … -> sk such that:

Every adjacent pair of words differs by a single letter.
Every si for 1 <= i <= k is in wordList. Note that beginWord does not need to be in wordList.
sk == endWord

Given two words, beginWord and endWord, and a dictionary wordList, return the number of words in the shortest transformation sequence from beginWord to endWord, or 0 if no such sequence exists.

Example 1:

Input: beginWord = “hit”, endWord = “cog”, wordList = [“hot”,”dot”,”dog”,”lot”,”log”,”cog”]
Output: 5
Explanation: One shortest transformation sequence is “hit” -> “hot” -> “dot” -> “dog” -> cog”, which is 5 words long.

Example 2:

Input: beginWord = “hit”, endWord = “cog”, wordList = [“hot”,”dot”,”dog”,”lot”,”log”]
Output: 0
Explanation: The endWord “cog” is not in wordList, therefore there is no valid transformation sequence.

Constraints:

1 <= beginWord.length <= 10
endWord.length == beginWord.length
1 <= wordList.length <= 5000
wordList[i].length == beginWord.length
beginWord, endWord, and wordList[i] consist of lowercase English letters.
beginWord != endWord
All the words in wordList are unique.

https://leetcode.com/problems/word-ladder/description/

A

Create wildcard words from each word with one char replaced with * for each chars and use them to find neighbors. Then do a BFS

from collections import defaultdict, deque

class Solution(object):
def ladderLength(self, beginWord, endWord, wordList):
“””
:type beginWord: str
:type endWord: str
:type wordList: List[str]
:rtype: int
“””
adj = defaultdict(list)
wl = set(wordList)
wl.add(beginWord)
visited = set()
for word in wl:
for i in range(len(word)):
modified = word[:i] + “*” + word[i+1:]
adj[modified].append(word)
adj[word].append(modified)
q = deque()
q.append(beginWord)

    level = 0
    while q:
        level += 1
        looplen = len(q)
        for _ in range(looplen):
            w = q.popleft()
            if w == endWord:
                return level

            dup = set()
            dup.add(w)
            for alternative in adj[w]:
                for neighbor in adj[alternative]:
                    if neighbor not in dup:
                        if neighbor not in visited:
                            visited.add(neighbor)
                            q.append(neighbor)
                        dup.add(neighbor)
    return 0
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q
  1. Min Cost to Connect All Points
    Medium

You are given an array points representing integer coordinates of some points on a 2D-plane, where points[i] = [xi, yi].

The cost of connecting two points [xi, yi] and [xj, yj] is the manhattan distance between them: |xi - xj| + |yi - yj|, where |val| denotes the absolute value of val.

Return the minimum cost to make all points connected. All points are connected if there is exactly one simple path between any two points.

Example 1:

Input: points = [[0,0],[2,2],[3,10],[5,2],[7,0]]
Output: 20
Explanation:

We can connect the points as shown above to get the minimum cost of 20.
Notice that there is a unique path between every pair of points.

Example 2:

Input: points = [[3,12],[-2,5],[-4,1]]
Output: 18

Constraints:

1 <= points.length <= 1000
-106 <= xi, yi <= 106
All pairs (xi, yi) are distinct.

https://leetcode.com/problems/min-cost-to-connect-all-points/description/

A

select 0 as start node

Prims Algo,
# insert it in heap with dist 0
# while heap, pop the top most element,
# mark visited, else continue
# calculate its distance from all other points
# add them to min heap.
# This ensures we always pick the min distance from each point to the other and once we visited all of them, then we just finish through the loop.
# we track num vertices we have connected, so we can shortcut once we reach n-1

class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
root = 0

    heap = [(0, root)]
    max_vertices = len(points) - 1
    vertices = 0
    seen = set()
    cost = 0
    while heap and vertices <= (len(points) - 1):
        dist, dest = heapq.heappop(heap)
        if dest in seen:
            continue
        seen.add(dest)
        cost += dist
        vertices += 1
        for ip, point in enumerate(points):
            if ip not in seen:
                dist = abs(points[dest][0]-point[0]) + abs(points[dest][1]- point[1])
                heapq.heappush(heap, (dist, ip))
    return cost
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q
  1. Network Delay Time
    Solved
    Medium
    Topics
    Companies
    Hint

You are given a network of n nodes, labeled from 1 to n. You are also given times, a list of travel times as directed edges times[i] = (ui, vi, wi), where ui is the source node, vi is the target node, and wi is the time it takes for a signal to travel from source to target.

We will send a signal from a given node k. Return the minimum time it takes for all the n nodes to receive the signal. If it is impossible for all the n nodes to receive the signal, return -1.

Example 1:

Input: times = [[2,1,1],[2,3,1],[3,4,1]], n = 4, k = 2
Output: 2

Example 2:

Input: times = [[1,2,1]], n = 2, k = 1
Output: 1

Example 3:

Input: times = [[1,2,1]], n = 2, k = 2
Output: -1

Constraints:

1 <= k <= n <= 100
1 <= times.length <= 6000
times[i].length == 3
1 <= ui, vi <= n
ui != vi
0 <= wi <= 100
All the pairs (ui, vi) are unique. (i.e., no multiple edges.)

https://leetcode.com/problems/min-cost-to-connect-all-points/

A

DJikstra

class Solution:
def minCostConnectPoints(self, points: List[List[int]]) -> int:
root = 0

    heap = [(0, root)]
    max_vertices = len(points) - 1
    vertices = 0
    seen = set()
    cost = 0
    while heap and vertices <= (len(points) - 1):
        dist, dest = heapq.heappop(heap)
        if dest in seen:
            continue
        seen.add(dest)
        cost += dist
        vertices += 1
        for ip, point in enumerate(points):
            if ip not in seen:
                dist = abs(points[dest][0]-point[0]) + abs(points[dest][1]- point[1])
                heapq.heappush(heap, (dist, ip))
    return cost
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q
  1. Swim in Rising Water
    Solved
    Hard
    Topics
    Companies
    Hint

You are given an n x n integer matrix grid where each value grid[i][j] represents the elevation at that point (i, j).

The rain starts to fall. At time t, the depth of the water everywhere is t. You can swim from a square to another 4-directionally adjacent square if and only if the elevation of both squares individually are at most t. You can swim infinite distances in zero time. Of course, you must stay within the boundaries of the grid during your swim.

Return the least time until you can reach the bottom right square (n - 1, n - 1) if you start at the top left square (0, 0).

Example 1:

Input: grid = [[0,2],[1,3]]
Output: 3
Explanation:
At time 0, you are in grid location (0, 0).
You cannot go anywhere else because 4-directionally adjacent neighbors have a higher elevation than t = 0.
You cannot reach point (1, 1) until time 3.
When the depth of water is 3, we can swim anywhere inside the grid.

Example 2:

Input: grid = [[0,1,2,3,4],[24,23,22,21,5],[12,13,14,15,16],[11,17,18,19,20],[10,9,8,7,6]]
Output: 16
Explanation: The final route is shown.
We need to wait until time 16 so that (0, 0) and (4, 4) are connected.

Constraints:

n == grid.length
n == grid[i].length
1 <= n <= 50
0 <= grid[i][j] < n2
Each value grid[i][j] is unique.

https://leetcode.com/problems/swim-in-rising-water/description/

A

Minimum Spanning Tree
class Solution:
def swimInWater(self, grid: List[List[int]]) -> int:
movements = [[0,1], [0,-1], [-1,0], [1,0]]
rows = len(grid)
cols = len(grid[0])
is_valid = lambda r,c: 0 <= r < rows and 0 <= c < cols
heap = []
visited = set()
heap.append((grid[0][0], (0,0)))
target = (rows-1, cols-1)
time = 0
while heap:
t, (r,c) = heapq.heappop(heap)
if (r,c) in visited:
continue
visited.add((r,c))
time = max(t, time)
if (r,c) == target:
return time
for rm, cm in movements:
row, col = rm + r, cm + c
if is_valid(row, col) and (row, col) not in visited:
heapq.heappush(heap, (grid[row][col], (row, col)))
return -1

17
Q
  1. Cheapest Flights Within K Stops
    Solved
    Medium
    Topics
    Companies

There are n cities connected by some number of flights. You are given an array flights where flights[i] = [fromi, toi, pricei] indicates that there is a flight from city fromi to city toi with cost pricei.

You are also given three integers src, dst, and k, return the cheapest price from src to dst with at most k stops. If there is no such route, return -1.

Example 1:

Input: n = 4, flights = [[0,1,100],[1,2,100],[2,0,100],[1,3,600],[2,3,200]], src = 0, dst = 3, k = 1
Output: 700
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 3 is marked in red and has cost 100 + 600 = 700.
Note that the path through cities [0,1,2,3] is cheaper but is invalid because it uses 2 stops.

Example 2:

Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 1
Output: 200
Explanation:
The graph is shown above.
The optimal path with at most 1 stop from city 0 to 2 is marked in red and has cost 100 + 100 = 200.

Example 3:

Input: n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, k = 0
Output: 500
Explanation:
The graph is shown above.
The optimal path with no stops from city 0 to 2 is marked in red and has cost 500.

Constraints:

1 <= n <= 100
0 <= flights.length <= (n * (n - 1) / 2)
flights[i].length == 3
0 <= fromi, toi < n
fromi != toi
1 <= pricei <= 104
There will not be any multiple flights between two cities.
0 <= src, dst, k < n
src != dst

https://leetcode.com/problems/cheapest-flights-within-k-stops/description/

A

DFS without visited, becasue we need to visit same node multiple times as it might cost less each time we visit.

from collections import defaultdict, deque
class Solution(object):
def findCheapestPrice(self, n, flights, src, dst, k):
“””
:type n: int
:type flights: List[List[int]]
:type src: int
:type dst: int
:type k: int
:rtype: int
“””
adj = defaultdict(list)
for i, j, price in flights:
adj[i].append((j, price))
q = deque()
visited = set()
q.append((0, src))
level = 0
cost_dict = [float(‘inf’)] * n
while q and level <= k:
qlen = len(q)
for _ in range(qlen):
cost, node = q.popleft()
if node not in adj:
continue
for n, p in adj[node]:
if cost_dict[n] > cost+p:
q.append((cost + p, n))
cost_dict[n] = cost + p
level += 1

    if cost_dict[dst] == float('inf'):
        return -1
    return cost_dict[dst]

Bellman Ford (slower but easy to read), key here is to compare current cost with the previous level cost.
class Solution:
def findCheapestPrice(self, n: int, flights: List[List[int]], src: int, dst: int, k: int) -> int:

    prices = [float('inf')] * n
    prices[src] = 0
    level = 0
    while level < k+1:
        temp_prices = prices[:]
        for s,d,p in flights:
            if temp_prices[d] > prices[s] + p:
                temp_prices[d] = prices[s] + p
        prices = temp_prices[:]
        level += 1
    if prices[dst] == float('inf'):
        return -1
    else:
        return prices[dst]
18
Q
  1. Alien Dictionary
    Hard
    Topics
    Companies

There is a new alien language that uses the English alphabet. However, the order of the letters is unknown to you.

You are given a list of strings words from the alien language’s dictionary. Now it is claimed that the strings in words are
sorted lexicographically
by the rules of this new language.

If this claim is incorrect, and the given arrangement of string in words cannot correspond to any order of letters, return “”.

Otherwise, return a string of the unique letters in the new alien language sorted in lexicographically increasing order by the new language’s rules. If there are multiple solutions, return any of them.

Example 1:

Input: words = [“wrt”,”wrf”,”er”,”ett”,”rftt”]
Output: “wertf”

Example 2:

Input: words = [“z”,”x”]
Output: “zx”

Example 3:

Input: words = [“z”,”x”,”z”]
Output: “”
Explanation: The order is invalid, so return “”.

Constraints:

1 <= words.length <= 100
1 <= words[i].length <= 100
words[i] consists of only lowercase English letters.

https://leetcode.com/problems/alien-dictionary/description/

A

class Solution:
def alienOrder(self, words: List[str]) -> str:
adj = {c: set() for word in words for c in word}
for a,b in zip(words, words[1:]):
minlen = min(len(a), len(b))
if len(a) > len(b) and a[:minlen] == b[:minlen]:
return “”
# Once we find different letters in the words we just break
# because we have a relation a > b, any letter after that, does not matter because
# we can’t deduce a relation. eg. AX, BY we know A > B, thus AX is > BY but we can’t
# determine the relation between X and Y. So they can appear anywhere.
for ca, cb in zip(a,b):
if ca != cb:
adj[ca].add(cb)
break
stack = []
prime = words[0][0]
visited = set()
order = []
def dfs(start):
stack = []
cur_visited = set()
stack.append(start)
while stack:
c = stack[-1]
if c not in visited:
visited.add(c)
cur_visited.add(c)
else:
stack.pop()
if c in cur_visited:
cur_visited.remove(c)
order.append(c)
for n in adj[c]:
if n not in visited:
stack.append(n)
elif n in cur_visited:
## Cycle
return True
return False
starts = list(adj.keys())
for k in starts:
if k not in visited:
if dfs(k):
return “”
order.reverse()
return ““.join(order)

BFS

from collections import defaultdict, Counter, deque
class Solution(object):
def alienOrder(self, words):
indegree = defaultdict(int)
adj = {c: set() for word in words for c in word}
for a,b in zip(words, words[1:]):
minlen = min(len(a), len(b))
if len(a) > len(b) and a[:minlen] == b[:minlen]:
return “”
# Once we find different letters in the words we just break
# because we have a relation a > b, any letter after that, does not matter because
# we can’t deduce a relation. eg. AX, BY we know A > B, thus AX is > BY but we can’t
# determine the relation between X and Y. So they can appear anywhere.
for ca, cb in zip(a,b):
if ca != cb:
if cb not in adj[ca]:
adj[ca].add(cb)
indegree[cb] += 1
break

    q = deque()
    output = []
    for v in adj.keys():
        if v not in indegree or indegree[v] == 0:
            q.append(v)
    seen = set()
    while q:
        vertex = q.popleft()
        if vertex in seen:
            continue
        seen.add(vertex)
        for neighbor in adj[vertex]:
            if neighbor in indegree:
                indegree[neighbor] -= 1
            if indegree[neighbor] == 0:
                q.append(neighbor)
        output.append(vertex)
    
    if len(adj) > len(output):
        return ""
    return "".join(output)