LeetCode Graph
LeetCode Graph
LeetCode Graph
The graph problem covers many topics, for example, graph traverse, shortest distance, topology
sort, and even union find.
We will start from the definition of the graph. The graph is a set of vertexes and edges, and
there may be weight on every edge.
https://en.wikipedia.org/wiki/Graph_(abstract_data_type)
There are many ways to represent a graph, for example list (linked node), adjacency matrix,
indigence matrix.
Adjacency list
This undirected cyclic graph can be described by the three unordered lists {b, c}, {a, c}, {a, b}.
Indigence matrix
e1 e2 e3 e4
1 1 1 1 0
2 1 0 0 0
3 0 1 0 1
4 0 0 1 1
To handle the graph problem easily, I prefer the following two data structures:
1. A hashed table with vertex id as key and a set of neighbours where the vertex can reach
as the value. For weighted graph, the value should be a hash map.
2. A two dimension of matrix (in C++, it is vector<vector<int>> G), if vertex i can reach
vertex j then G[i][j] = 1, otherwise G[i][j] =0. For weighted graph the value is the weight.
The first data structure has a lot of advantage to calculate the following:
The second data structure is used when there is only limited functionality is required and fast
access is needed, consider vector access is almost 40 times faster than the hash table.
There are many other shortest path algorithms, for example Floyd–Warshall algorithm, O(V^3))
and Bellman Ford algorithm, O(E^3), but Dijkstra is the most popular one.
Between BFS and DFS, when the question such as shortest step is asked, then BFS should be
used. This is why BFS is far more popular in leet code solutions than DFS.
The common algorithm code pattern you may need to practice a lot are listed below:
1. Flood fill
2. BFS
3. DFS
4. Topology Sort
5. Dijkstra
6. Union Find
7. Balance Weight
8. Travelling Salesman
9. MinMax Problem
Travelling Salesman
https://simple.wikipedia.org/wiki/Travelling_salesman_problem
The travelling salesman is to say for a list of given cites, the distances between the cities, the
sales man need to travel all the cities at least once, what is the shortest way.
This an NP hard problem, so the time complexity is exponential. We should make our code
pattern clean, short and easy to understand.
Please think how you handle the following issues in this problem.
Return the length of the shortest path that visits every node. You may start and stop at any
node, you may revisit nodes multiple times, and you may reuse edges.
Example 1:
Input: [[1,2,3],[0],[0],[0]]
Output: 4
Explanation: One possible path is [1,0,2,0,3]
Example 2:
Input: [[1],[0,2,4],[1,3,4],[2],[1,2]]
Output: 4
Explanation: One possible path is [0,1,4,2,3]
Note:
MinMax Problem
https://en.wikipedia.org/wiki/Minimax
The MinMax problem is not a sorting problem, it is a gaming problem, which is to say assuming
two people are playing a game, for example chess, every one will choose the best strategy for
himself, then what will be the end result?
The key question to ask in a MinMax Problem is very similar to a saleman problem (may be to
every graph problem. 😊). This is to say how to represent the current state.
In most of the cases, it is where we are (each player) plus who should move next. So it can be
G(S(a), S(b), M), S(a) is where play A is and M is who should move next.
To solve the problem we can start from some sure win state for one party, assume it is play A,
then reversely search for all possible states which can directly transit to this state, if on those
state it is A will move next, then it is also a A sure win state, otherwise, if all possible B move will
end up with a A sure win state, then these states are also A sure win state. Same logic can be
applied on play B.
A game on an undirected graph is played by two players, Mouse and Cat, who alternate
turns.
The graph is given as follows: graph[a] is a list of all nodes b such that ab is an edge of the
graph.
Mouse starts at node 1 and goes first, Cat starts at node 2 and goes second, and there is a
Hole at node 0.
During each player's turn, they must travel along one edge of the graph that meets where
they are. For example, if the Mouse is at node 1, it must travel to any node in graph[1].
Additionally, it is not allowed for the Cat to travel to the Hole (node 0.)
If ever the Cat occupies the same node as the Mouse, the Cat wins.
If ever the Mouse reaches the Hole, the Mouse wins.
If ever a position is repeated (ie. the players are in the same position as a previous
turn, and it is the same player's turn to move), the game is a draw.
Given a graph, and assuming both players play optimally, return 1 if the game is won by
Mouse, 2 if the game is won by Cat, and 0 if the game is a draw.
Example 1:
Input: [[2,5],[3],[0,4,5],[1,4,5],[2,3],[0,2,3]]
Output: 0
Explanation:
4---3---1
| |
2---5
\ /
0
Note:
In this probkem, the state S can be represented as S(c,m,t), c and m are the current position of
cat and mouse and t is the who should move next.
Let’s starts with some sure win scenario. S(0, *, *) is a sure win state for mouse, this ie because
mouse enter the mouse hole, so regardless where cat is and who will move it is mouse win.
This is to say if the mouse is at 2 and 5 in the above graph and it is mouse move, mouse will also
win. S(2, *, ‘m’) = MOUSE and S(5, *, ‘m’) = MOUSE
On the other hand, if mouse and cat are in the same location, it is CAT win, regardless who will
move next. So S(x,x,*) = CAT. This means if it is the cat move, and cat can reach mouse place, it it
is cat win for example S(1,3,’c’) = CAT, S(3,5,’c’) = CAT, S(4,2,’c’) = CAT…
There is another situation not that obvious, assume the CAT is at 3 and mouse is at 1, and it is
mouse move. For all the possible mouse moves (in this case only one), it will end up in CAT’s
location, so CAT win. S(1,3,’m’) = CAT.
The algorithm to reach this conclusion is that start from S(1,3, ’m’), play all possible mouse
moves, if it is CAT win, take this path out from existing path, when no more path left it is your
rival, CAT win. If after such play, there are still some paths left, it is a DRAW scenario (no one
win).
// Initialize all the scenarios where mouse will win or cat will win
queue<vector<int>> search;
for (int i = 1; i < N; ++i)
{
// for all the move
for (int t = 0; t < 2; ++t)
{
// mark mouse win
color[0][i][t] = MOUSE;
search.push({ 0, i, t });
cout << 0 << i << t << MOUSE << endl;
// mark CAT win
if (i > 0)
{
color[i][i][t] = CAT;
search.push({i, i, t});
cout << i << i << t << CAT << endl;
}
}
}
// This is a BFS search, we initialize some sure win state for one party
// in the queue, and reversly search more sure win state and mark them.
// After the search complete, we may have some undetermined state, this
// means DRAW state.
while (!search.empty())
{
vector<int> node = search.front();
search.pop();
// for nodes that are colored :
int m = node[0], c = node[1], t = node[2];
int state = color[m][c][t];
// The children contains all the possible path coming to current state.
vector<vector<int>> children;
// mouse step, iterate all possible cat paths coming here
if (t == 0)
{
for (int c1 : graph[c])
{
if (c1 > 0)
{
children.push_back({ m, c1, 1 - t });
}
}
}
// cat step, iterate all possible cat paths coming here
else
{
for (int m1 : graph[m])
{
children.push_back({ m1, c, 1 - t });
}
}
return color[1][2][0];
}
Balance Weight
Assuming you have a graph, which is a series of line segments parallel to the X axis and each
edge has a weight. Can you find a point with weighted distance to left end and right end equal
or most close to equal?
There are many methods to solve the problem, one way is to have two pointers from left and
right moving to the center, each start one step, if total weighted distance on left is less than the
one on right, we move left pointer one step further, otherwise we move right pointer one step
further.
Please note one important fact, the weight sum from the total objects on one side, it has
nothing to do with distance. While the total cost is SUM(weight * distance).
In the following example the weight is number of people. You can see if right end has two
people but left side has one, we should keep on move the meeting point to right regardless how
far from it is from the left.
A group of two or more people wants to meet and minimize the total travel distance. You are
given a 2D grid of values 0 or 1, where each 1 marks the home of someone in the group.
The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x -
p1.x| + |p2.y - p1.y|.
Example:
Input:
1 - 0 - 0 - 0 - 1
| | | | |
0 - 0 - 0 - 0 - 0
| | | | |
0 - 0 - 1 - 0 - 0
Output: 6
To find out a best meeting point for multiple people, you need to use the method called
weighted distance. Basically, the best meeting point for two persons are the middle point
between them, but when you have multiple people and in a 2D grid you should use weighted
distance. The weight is the number of people, to find out the fairest meeting point, you can use
two pointers, and move from the light side to the heavy.
There is a catch in the weighted distance, in case there is any obstacle in the route, weighted
distance may fail. This is why #317 cannot use this method.
/// <summary>
/// Leet code #296. Best Meeting Point
/// </summary>
int LeetCode::minTotalDistance(vector<int> & nums)
{
if (nums.empty()) return 0;
int i = 0, j = nums.size() - 1;
// Here the left and right are the sum of people considered as weight,
// which has nothing to do with distance.
int left = nums[i], right = nums[j], sum = 0;
while (i < j)
{
if (left < right)
{
sum += left;
i++;
left += nums[i];
}
else
{
sum += right;
j--;
right += nums[j];
}
}
return sum;
}
/// <summary>
/// Leet code #296. Best Meeting Point
///
/// A group of two or more people wants to meet and minimize the total travel
/// distance. You are given a 2D grid of values 0 or 1, where each 1 marks
/// the home of someone in the group. The distance is calculated using
/// Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.
/// For example, given three people living at (0,0), (0,4), and (2,2):
/// 1 - 0 - 0 - 0 - 1
/// | | | | |
/// 0 - 0 - 0 - 0 - 0
/// | | | | |
/// 0 - 0 - 1 - 0 - 0
/// The point (0,2) is an ideal meeting point, as the total travel distance of
/// 2+2+2=6 is minimal. So return 6.
///
/// Hint:
/// 1.Try to solve it in one dimension first. How can this solution apply to
/// the two dimension case?
/// </summary>
int LeetCode::minTotalDistance(vector<vector<int>>& grid)
{
if (grid.empty() || grid[0].empty()) return 0;
vector<int> row(grid.size());
vector<int> col(grid[0].size());
for (size_t i = 0; i < grid.size(); i++)
{
for (size_t j = 0; j < grid[i].size(); j++)
{
if (grid[i][j] == 1)
{
row[i]++;
col[j]++;
}
}
}
return minTotalDistance(row) + minTotalDistance(col);
}
/// <summary>
/// Leet code #902. Truck delivery
///
/// There are a number of houses in a line, the distance between two houses
/// are given in an array.
/// for example the distance between house(0) and house(1) is in distance[0]
/// and the distance between house(n-2) and house(n-1) is given in
/// distance[n-2]. A delivery truck stops in front of the houses, and the
/// driver has a number of packages to deliver, each with a weight[i],
/// if no package for this house it is 0.
/// The truck can only stop in front of one house, the driver should minimize
/// the total cost of the delivery.
///
/// Note:
/// 1. The cost is defined by multiply the distance the driver should walk
/// with the weight of package in hand, i.e.
/// It is weight[i] * distance[i].
/// 2. If the driver walk back with empty hand the cost is zero.
/// 3. The driver is only allowed to carry one package at a time.
/// </summary>
int LeetCode::minDeliveryCost(vector<int> distances, vector<int> weight)
{
// at very beginning, we assume the truck stops at house0, so the
// weight of total packages on left is 0 and the total weight
// on right is from 1 to n-1.
int weight_left = 0, weight_right = 0;
int distance = 0, sum = 0, min_cost = INT_MAX;
for (size_t i = 1; i < weight.size(); i++)
{
weight_right += weight[i];
distance += distances[i];
sum += weight[i] * distance;
}
min_cost = sum;
for (size_t i = 1; i < weight.size(); i++)
{
// assume truck is at house i, house[i-1] is moved to left
weight_left += weight[i - 1];
// on the right side, the cost to each house deduct the cost of weight *
distance[i-1];
sum -= weight_right * distances[i];
// on the left side, the cost to each house add (weight * distance[i-1]
sum += weight_left * distances[i];
// house[i] is no longer on right, it is in front of track
weight_right -= weight[i];
// calculate min_cost
min_cost = min(sum, min_cost);
}
return min_cost;
}
Topology Sort
The topology sort is one of the most common problem in Graph Theory. The idea of the
topology sort is that we traverse the graph in an order of dependency. We start all the nodes
without dependencies, visit them, then remove the dependencies on them and select all the
unvisited node without dependency as the next iteration.
For example, we can visit the above graph in the order of 3 iterations.
5,7,3,11,8,2,9,10
The code pattern of the topology sort is that you need to maintain two data structures. The first
is the count of dependency on each vertex in the graph, which will help us to find the free
nodes. The second is the mapping table of dependency, helping us to update the dependency
count when a specific node is visited.
Here is an example:
207. Course Schedule
Some courses may have prerequisites, for example to take course 0 you have to first take
course 1, which is expressed as a pair: [0,1]
Given the total number of courses and a list of prerequisite pairs, is it possible for you to
finish all courses?
Example 1:
Input: 2, [[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: 2, [[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.
Note:
1. The input prerequisites is a graph represented by a list of edges, not adjacency
matrices. Read more about how a graph is represented.
2. You may assume that there are no duplicate edges in the input prerequisites.
/// <summary>
/// LeetCode #207. Course Schedule
/// There are a total of n courses you have to take, labeled from 0 to
/// n - 1.
/// Some courses may have prerequisites, for example to take course 0 you
/// have to first take course 1, which is expressed as a pair: [0,1]
/// Given the total number of courses and a list of prerequisite pairs,
/// is it possible for you to finish all courses?
///
/// For example:
/// 2, [[1,0]]
/// There are a total of 2 courses to take. To take course 1 you should
/// have finished course 0. So it is possible.
///
/// 2, [[1,0],[0,1]]
/// 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.
/// Note:
/// The input prerequisites is a graph represented by a list of edges,
/// not adjacency matrices. Read more about how a graph is represented.
/// </summary>
bool LeetCode::canFinishCourse(int numCourses, vector<pair<int, int>>&
prerequisites)
{
vector<int> courseDependency(numCourses);
vector<unordered_set<int>> whoDependOnMe(numCourses);
unordered_set<int> freeCourse;
queue<int> search_queue;
// Using queue to manage BFS and get every free course and clear the
// dependency with a free course, i.e. you depend on a free course,
// then such dependency
// does not matter. If all dependencies are clear, we got a new
// free course
while (!search_queue.empty())
{
int free_course = search_queue.front();
search_queue.pop();
unordered_set<int> course_depend_on_me = whoDependOnMe[free_course];
for (int dependent_course : course_depend_on_me)
{
courseDependency[dependent_course]--;
if (courseDependency[dependent_course] == 0)
{
freeCourse.insert(dependent_course);
search_queue.push(dependent_course);
}
}
}
// if number of free courses equals to the total course, we can finish
// all courses
if (freeCourse.size() == numCourses)
{
return true;
}
else
{
return false;
}
}
269. Alien Dictionary
There is a new alien language which uses the latin alphabet. However, the order among
letters are unknown to you. You receive a list of non-empty words from the dictionary,
where words are sorted lexicographically by the rules of this new language. Derive the
order of letters in this language.
Example 1:
Input:
[
"wrt",
"wrf",
"er",
"ett",
"rftt"
]
Output: "wertf"
Example 2:
Input:
[
"z",
"x"
]
Output: "zx"
Example 3:
Input:
[
"z",
"x",
"z"
]
Output: ""
Note:
/// <summary>
/// Leet code #269. Alien Dictionary
/// There is a new alien language which uses the latin alphabet. However, the
/// order among letters are unknown to you. You receive a list of words from
/// the dictionary, where words are sorted lexicographically by the rules of
/// this new language. Derive the order of letters in this language.
///
/// For example,
/// Given the following words in dictionary,
/// [
/// "wrt",
/// "wrf",
/// "er",
/// "ett",
/// "rftt"
/// ]
/// The correct order is: "wertf".
/// Note:
/// 1.You may assume all letters are in lowercase.
/// 2.If the order is invalid, return an empty string.
/// 3.There may be multiple valid order of letters, return any one of them is
/// fine.
/// </summary>
string LeetCode::alienOrder(vector<string>& words)
{
string result;
unordered_map<char, unordered_set<char>> next_set;
unordered_map<char, unordered_set<char>> prev_set;
unordered_set<char> root_set;
while (!root_set.empty())
{
char prev_ch = *root_set.begin();
for (char next_ch : next_set[prev_ch])
{
prev_set[next_ch].erase(prev_ch);
if (prev_set[next_ch].empty())
{
root_set.insert(next_ch);
prev_set.erase(next_ch);
}
}
root_set.erase(prev_ch);
result.push_back(prev_ch);
next_set.erase(prev_ch);
}
if (next_set.empty()) return result;
else return "";
}
BFS or DFS
When we traverse the graph, normally we have two options, one is the BFS another is DFS.
When it is required to find minimum steps for the solution, BFS will be the choice, because it will
always give you the minimum hop in the path.
815. Bus Routes
We have a list of bus routes. Each routes[i] is a bus route that the i-th bus repeats forever.
For example if routes[0] = [1, 5, 7], this means that the first bus (0-th indexed) travels in
the sequence 1->5->7->1->5->7->1->... forever.
We start at bus stop S (initially not on a bus), and we want to go to bus stop T. Travelling by
buses only, what is the least number of buses we must take to reach our destination? Return
-1 if it is not possible.
Example:
Input:
routes = [[1, 2, 7], [3, 6, 7]]
S = 1
T = 6
Output: 2
Explanation:
The best strategy is take the first bus to the bus stop 7, then take the
second bus to the bus stop 6.
Note:
/// <summary>
/// Leet code #815. Bus Routes
///
/// We have a list of bus routes. Each routes[i] is a bus route that the
/// i-th bus repeats forever. For example if routes[0] = [1, 5, 7], this
/// means that the first bus (0-th indexed) travels in the sequence
/// 1->5->7->1->5->7->1->... forever.
///
/// We start at bus stop S (initially not on a bus), and we want to go to
/// bus stop T. Travelling by buses only, what is the least number of
/// buses we must take to reach our destination? Return -1 if it is not
/// possible.
///
/// Example:
/// Input:
/// routes = [[1, 2, 7], [3, 6, 7]]
/// S = 1
/// T = 6
/// Output: 2
/// Explanation:
/// The best strategy is take the first bus to the bus stop 7, then take
/// the second bus to the bus stop 6.
/// Note:
///
/// 1. 1 <= routes.length <= 500.
/// 2. 1 <= routes[i].length <= 500.
/// 3. 0 <= routes[i][j] < 10 ^ 6.
/// </summary>
int LeetCode::numBusesToDestination(vector<vector<int>>& routes, int S, int T)
{
unordered_map<int, set<int>> stop_map;
unordered_map<int, int> dist_map;
Flood Fill
Flood Fill is a special BFS or say Union Find, which easily merge all the adjacent nodes in an area
with same property. In some algorithm book, this is also called as coloring. This algorithm is used
to calculate the number of points in an area.
The code pattern is also BFS based, with a 2 D array to record the color, and an array of count
which remember the total points in each area.
In a network of nodes, each node i is directly connected to another node j if and only
if graph[i][j] = 1.
Some nodes initial are initially infected by malware. Whenever two nodes are directly
connected and at least one of those two nodes is infected by malware, both nodes will be
infected by malware. This spread of malware will continue until no more nodes can be
infected in this manner.
Suppose M(initial) is the final number of nodes infected with malware in the entire
network, after the spread of malware stops.
We will remove one node from the initial list. Return the node that if removed, would
minimize M(initial). If multiple nodes could be removed to minimize M(initial), return
such a node with the smallest index.
Note that if a node was removed from the initial list of infected nodes, it may still be
infected later as a result of the malware spread.
Example 1:
Example 3:
Note:
/// <summary>
/// Leet code #924. Minimize Malware Spread
///
/// In a network of nodes, each node i is directly connected to another node j
/// if and only if graph[i][j] = 1.
///
/// Some nodes initial are initially infected by malware. Whenever two nodes
/// are directly connected and at least one of those two nodes is infected by
/// malware, both nodes will be infected by malware. This spread of malware
/// will continue until no more nodes can be infected in this manner.
///
/// Suppose M(initial) is the final number of nodes infected with malware in
/// the entire network, after the spread of malware stops.
///
/// We will remove one node from the initial list. Return the node that if
/// removed, would minimize M(initial). If multiple nodes could be removed to
/// minimize M(initial), return such a node with the smallest index.
///
/// Note that if a node was removed from the initial list of infected nodes,
/// it may still be infected later as a result of the malware spread.
///
/// Example 1:
/// Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
/// Output: 0
///
/// Example 2:
/// Input: graph = [[1,0,0],[0,1,0],[0,0,1]], initial = [0,2]
/// Output: 0
///
/// Example 3:
/// Input: graph = [[1,1,1],[1,1,1],[1,1,1]], initial = [1,2]
/// Output: 1
///
/// Note:
///
/// 1. 1 < graph.length = graph[0].length <= 300
/// 2. 0 <= graph[i][j] == graph[j][i] <= 1
/// 3. graph[i][i] = 1
/// 4. 1 <= initial.length < graph.length
/// 5. 0 <= initial[i] < graph.length
/// </summary>
int LeetCode::minMalwareSpread(vector<vector<int>>& graph, vector<int>& initial)
{
vector<int> flag(graph.size(), -1);
vector<int> count(graph.size());
for (size_t i = 0; i < initial.size(); i++)
{
queue<int> search;
search.push(initial[i]);
if (flag[initial[i]] != -1)
{
count[flag[initial[i]]] = 0;
continue;
}
while (!search.empty())
{
int node = search.front();
search.pop();
(This problem is the same as Minimize Malware Spread, with the differences bolded.)
In a network of nodes, each node i is directly connected to another node j if and only
if graph[i][j] = 1.
Some nodes initial are initially infected by malware. Whenever two nodes are directly
connected and at least one of those two nodes is infected by malware, both nodes will be
infected by malware. This spread of malware will continue until no more nodes can be
infected in this manner.
Suppose M(initial) is the final number of nodes infected with malware in the entire
network, after the spread of malware stops.
We will remove one node from the initial list, completely removing it and any connections
from this node to any other node. Return the node that if removed, would
minimize M(initial). If multiple nodes could be removed to minimize M(initial), return
such a node with the smallest index.
Example 1:
Example 2:
Example 3:
Note:
/// <summary>
/// Leet code #928. Minimize Malware Spread II
///
/// In a network of nodes, each node i is directly connected to another node j
/// if and only if graph[i][j] = 1.
///
/// Some nodes initial are initially infected by malware. Whenever two nodes
/// are directly connected and at least one of those two nodes is infected by
/// malware, both nodes will be infected by malware. This spread of malware
/// will continue until no more nodes can be infected in this manner.
///
/// Suppose M(initial) is the final number of nodes infected with malware in
/// the entire network, after the spread of malware stops.
///
/// We will remove one node from the initial list, completely removing it and
/// any connections from this node to any other node. Return the node that if
/// removed, would minimize M(initial). If multiple nodes could be removed to
/// minimize M(initial), return such a node with the smallest index.
///
/// Note that if a node was removed from the initial list of infected nodes,
/// it may still be infected later as a result of the malware spread.
///
/// Example 1:
/// Input: graph = [[1,1,0],[1,1,0],[0,0,1]], initial = [0,1]
/// Output: 0
///
/// Example 2:
/// Input: graph = [[1,1,0],[1,1,1],[0,1,1]], initial = [0,1]
/// Output: 1
///
/// Example 3:
///
/// Input: graph = [[1,1,0,0],[1,1,1,0],[0,1,1,1],[0,0,1,1]], initial = [0,1]
/// Output: 1
///
/// Note:
///
/// 1. 1 < graph.length = graph[0].length <= 300
/// 2. 0 <= graph[i][j] == graph[j][i] <= 1
/// 3. graph[i][i] = 1
/// 4. 1 <= initial.length < graph.length
/// 5. 0 <= initial[i] < graph.length
/// </summary>
int LeetCode::minMalwareSpreadII(vector<vector<int>>& graph, vector<int>& initial)
{
unordered_set<int> virus;
for (auto v : initial) virus.insert(v);
if (flag[initial[i]] == -1)
{
flag[initial[i]] = initial[i];
count[initial[i]] += 1;
}
else if (flag[initial[i]] != -2)
{
count[flag[initial[i]]]--;
flag[initial[i]] = -2;
}
else
{
continue;
}
while (!search.empty())
{
int node = search.front();
search.pop();
Another common shortest distance algorithm is Floyd-Warshall Algorithm, which is O(n^3, but it
record shortest distance from any to any node.
The code pattern of Dijkstra is based on BFS and a priority queue, by default we assume the
distance from the source node to all the node are infinite, we start from the source node and
search for its neighbors, starting from the shortest path, then use the neighbor as the
intermediate point and check the next hop and update the shorted distance from the source to
these new locations. Every time if find a shorter path from S to N, we will overwrite the old
value. and assume the N as the new searching point.
505. The Maze II
There is a ball in a maze with empty spaces and walls. The ball can go through empty
spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the
ball stops, it could choose the next direction.
Given the ball's start position, the destination and the maze, find the shortest distance for
the ball to stop at the destination. The distance is defined by the number of empty
spaces traveled by the ball from the start position (excluded) to the destination (included). If
the ball cannot stop at the destination, return -1.
The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty
space. You may assume that the borders of the maze are all walls. The start and destination
coordinates are represented by row and column indexes.
Example 1:
0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0
Output: 12
Explanation: One shortest way is : left -> down -> left -> down -> right ->
down -> right.
The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12.
Example 2:
0 0 1 0 0
0 0 0 0 0
0 0 0 1 0
1 1 0 1 1
0 0 0 0 0
Output: -1
Note:
/// <summary>
/// Leet code #505. The Maze II
/// </summary>
void LeetCode::shortestDistance(vector<vector<int>>& maze, vector<vector<int>>&
visited, vector<int>& start,
priority_queue<pair<int, vector<int>>> &process_queue)
{
vector<vector<int>> next_list;
int distance = visited[start[0]][start[1]];
for (size_t i = 0; i < 4; i++)
{
int step = 0;
vector<int> pos = start;
if (i == 0)
{
while ((pos[0] > 0) && (maze[pos[0] - 1][pos[1]] == 0))
{
step++;
pos[0]--;
}
}
else if (i == 1)
{
while ((pos[0] < (int)maze.size() - 1) && (maze[pos[0] + 1][pos[1]] ==
0))
{
step++;
pos[0]++;
}
}
else if (i == 2)
{
while ((pos[1] > 0) && (maze[pos[0]][pos[1] - 1] == 0))
{
step++;
pos[1]--;
}
}
else
{
while ((pos[1] < (int)maze[0].size() - 1) && (maze[pos[0]][pos[1] + 1]
== 0))
{
step++;
pos[1]++;
}
}
if (pos == start) continue;
if (visited[pos[0]][pos[1]] > distance + step)
{
visited[pos[0]][pos[1]] = distance + step;
process_queue.push(make_pair(-visited[pos[0]][pos[1]], pos));
}
}
}
/// <summary>
/// Leet code #505. The Maze II
///
/// There is a ball in a maze with empty spaces and walls. The ball can
/// go through empty spaces by rolling up, down, left or right, but it
/// won't stop rolling until hitting a wall. When the ball stops, it could
/// choose the next direction.
///
/// Given the ball's start position, the destination and the maze, find the
/// shortest distance for the ball to stop at the destination. The distance
/// is defined by the number of empty spaces traveled by the ball from the
/// start position (excluded) to the destination (included). If the ball
/// cannot stop at the destination, return -1.
///
/// The maze is represented by a binary 2D array. 1 means the wall and 0 means
/// the empty space. You may assume that the borders of the maze are all walls.
/// The start and destination coordinates are represented by row and column
/// indexes.
///
/// Example 1
/// Input 1: a maze represented by a 2D array
///
/// 0 0 1 0 0
/// 0 0 0 0 0
/// 0 0 0 1 0
/// 1 1 0 1 1
/// 0 0 0 0 0
/// Input 2: start coordinate (rowStart, colStart) = (0, 4)
/// Input 3: destination coordinate (rowDest, colDest) = (4, 4)
///
/// Output: 12
/// Explanation: One shortest way is :
/// left -> down -> left -> down -> right -> down -> right.
/// The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12.
///
/// Example 2
/// Input 1: a maze represented by a 2D array
///
/// 0 0 1 0 0
/// 0 0 0 0 0
/// 0 0 0 1 0
/// 1 1 0 1 1
/// 0 0 0 0 0
/// Input 2: start coordinate (rowStart, colStart) = (0, 4)
/// Input 3: destination coordinate (rowDest, colDest) = (3, 2)
/// Output: -1
/// Explanation: There is no way for the ball to stop at the destination.
///
/// Note:
/// 1.There is only one ball and one destination in the maze.
/// 2.Both the ball and the destination exist on an empty space,
/// and they will not be at the same position initially.
/// 3.The given maze does not contain border (like the red rectangle in the
/// example
/// pictures), but you could assume the border of the maze are all walls.
/// 4.The maze contains at least 2 empty spaces, and both the width and height of
/// the maze won't exceed 100.
/// </summary>
int LeetCode::shortestDistance(vector<vector<int>>& maze, vector<int>& start,
vector<int>& destination)
{
if (maze.empty() || maze[0].empty()) return -1;
vector<vector<int>> visited(maze.size(), vector<int>(maze[0].size(),
INT_MAX));
visited[start[0]][start[1]] = 0;
priority_queue<pair<int, vector<int>>> process_queue;
process_queue.push(make_pair(0, start));
while (!process_queue.empty())
{
start = process_queue.top().second;
process_queue.pop();
if (start == destination) break;
shortestDistance(maze, visited, start, process_queue);
}
int shortest_distance = visited[destination[0]][destination[1]];
if (shortest_distance == INT_MAX) return -1;
else return shortest_distance;
}
The graph is given as follows: edges[k] is a list of integer pairs (i, j, n) such that (i, j) is
an edge of the original graph,
Then, the edge (i, j) is deleted from the original graph, n new nodes (x_1, x_2, ...,
x_n) are added to the original graph,
and n+1 new edges (i, x_1), (x_1, x_2), (x_2, x_3), ..., (x_{n-1}, x_n), (x_n,
j) are added to the original graph.
Now, you start at node 0 from the original graph, and in each move, you travel along
one edge.
Example 1:
Note:
/// <summary>
/// Leet code #882. Reachable Nodes In Subdivided Graph
///
/// Starting with an undirected graph (the "original graph") with nodes from
/// 0 to N-1, subdivisions are made to some of the edges.
///
/// The graph is given as follows: edges[k] is a list of integer pairs
/// (i, j, n) such that (i, j) is an edge of the original graph,
///
/// and n is the total number of new nodes on that edge.
///
/// Then, the edge (i, j) is deleted from the original graph, n new nodes
/// (x_1, x_2, ..., x_n) are added to the original graph,
///
/// and n+1 new edges (i, x_1), (x_1, x_2), (x_2, x_3), ..., (x_{n-1}, x_n),
/// (x_n, j) are added to the original graph.
///
/// Now, you start at node 0 from the original graph, and in each move, you
/// travel along one edge.
///
/// Return how many nodes you can reach in at most M moves.
///
/// Example 1:
///
/// Input: edges = [[0,1,10],[0,2,1],[1,2,2]], M = 6, N = 3
/// Output: 13
/// Explanation:
/// The nodes that are reachable in the final graph after M = 6 moves are
/// indicated below.
///
/// Example 2:
///
/// Input: edges = [[0,1,4],[1,2,6],[0,2,8],[1,3,1]], M = 10, N = 4
/// Output: 23
///
/// Note:
///
/// 1. 0 <= edges.length <= 10000
/// 2. 0 <= edges[i][0] < edges[i][1] < N
/// 3. There does not exist any i != j for which edges[i][0] == edges[j][0] and
/// edges[i][1] == edges[j][1].
/// 4. The original graph has no parallel edges.
/// 5. 0 <= edges[i][2] <= 10000
/// 6. 0 <= M <= 10^9
/// 7. 1 <= N <= 3000
/// 8. reachable node is a node that can be travelled to using at most M moves
/// starting from node 0.
/// </summary>
int LeetCode::reachableNodes(vector<vector<int>>& edges, int M, int N)
{
int result = 0;
unordered_map<int, unordered_map<int, int>> edge_nodes;
vector<int> visited(N);
for (size_t i = 0; i < edges.size(); i++)
{
edge_nodes[edges[i][0]][edges[i][1]] = edges[i][2];
edge_nodes[edges[i][1]][edges[i][0]] = edges[i][2];
}
priority_queue<pair<int, int>>search;
search.push(make_pair(M, 0));
while (!search.empty())
{
pair<int, int> pos = search.top();
search.pop();
int node = pos.second;
int step = pos.first;
if (visited[node] == 1) continue;
result++;
visited[node] = 1;
for (auto itr : edge_nodes[node])
{
int target_node = itr.first;
int nodes_in_middle = itr.second;
if (nodes_in_middle + 1 <= step)
{
result += nodes_in_middle;
if (visited[target_node] == 0)
{
search.push(make_pair(step - nodes_in_middle - 1,
target_node));
edge_nodes[node][target_node] = 0;
edge_nodes[target_node][node] = 0;
}
}
else
{
result += step;
edge_nodes[node][target_node] -= step;
edge_nodes[target_node][node] -= step;
}
}
}
return result;
}
Union Find
The union find is used for grouping the related vertices in a graph. Every union group can be
considered as a tree. When merge two groups we simply point the root of one group to another
root.
The code pattern is that we normally use a hash table (or array to map to the parents, finally to
the root, by default every new node point to itself, when need to merge it will point to a new
root. This is also to say after union find process, the one who point to itself is a root.
Example 1:
Input: [4,6,15,35]
Output: 4
Example 2:
Input: [20,50,9,63]
Output: 2
Example 3:
Input: [2,3,6,7,4,12,21,39]
Output: 8
Note:
/// <summary>
/// Leet code #952. Largest Component Size by Common Factor
///
/// Given a non-empty array of unique positive integers A, consider the
/// following graph:
///
/// There are A.length nodes, labelled A[0] to A[A.length - 1];
/// There is an edge between A[i] and A[j] if and only if A[i] and A[j]
/// share a common factor greater than 1.
/// Return the size of the largest connected component in the graph.
///
/// Example 1:
/// Input: [4,6,15,35]
/// Output: 4
///
/// Example 2:
/// Input: [20,50,9,63]
/// Output: 2
///
/// Example 3:
/// Input: [2,3,6,7,4,12,21,39]
/// Output: 8
///
/// Note:
/// 1. 1 <= A.length <= 20000
/// 2. 1 <= A[i] <= 100000
/// </summary>
int LeetCode::largestComponentSize(vector<int>& A)
{
vector<vector<int>> factors(A.size());
unordered_map<int, int> prime_map;
vector<int> union_map(A.size());
vector<int> count(A.size(), 1);
int result = 1;
for (size_t i = 0; i < A.size(); i++)
{
union_map[i] = i;
count[i] = 1;
for (auto p : factors[i])
{
if (prime_map.count(p) == 0)
{
prime_map[p] = i;
}
else
{
int source = i;
int target = prime_map[p];
while (union_map[source] != source) source = union_map[source];
while (union_map[target] != target) target = union_map[target];
if (source != target)
{
union_map[source] = target;
count[target] += count[source];
result = max(count[target], result);
}
}
}
}
return result;
}