DataStructure Graph SSSP Problem
SSSP Problems for unweighted graphs
We still focus on SSSP problems: single source shortest path. (We focus on directed graphs)
We will use BFS algorithm and Dynamic Programming to dynamically update the value from the beginning.
- maintain a queue for BFS:
- Attention: this queue holds the increasing order for the distance from the single source vertex .
- Hence, for every vertex, the first time it appears in the queue is the shortest path to the source vertex .
You can see this video for more information
During the updating process, you can recording the information of its adjacency for the single path. Hence, despite the SSSP problems, we can still know how to go to the source via the found shortest path.
The Slides are from: https://github.com/wangshusen/AdvancedAlgorithms, really a great course for learning data structures!
Time Complexity
Time complexity:
- Initialization:
- Queue Operations:
- Every vertex will be euqueued and dequeued only once.
- Every Edge will be visited only once: .
Hence, the time complexity is .
SSSP Problems for weighted graphs
Especially for Weighted Shorted Path Problem
For unweighted shorted path problems, we can simply use BFS algorithms to solve it!
Lecture Note for MIT 6.006
We will introduce several algorithms:
Previously
DFS & BFS problems:
- Single Source Shortest Path:
- Single Source Reachability:
- Connected components:
- Topological Sort:
Weighted Graph
For weighted graph, each edge has a weight. (Full of applications)
How to store weights:
- with each adjacency, store a weight. Given a node , for every adjacency , stores a weight!
- Set data structure: .
Weighted Path: . We need to find the shortest path from given vertices to .
(if no path, define )
Some problems occurred! Especially when there are some circle paths and the weight of the circle path is negative! Then due to the definition, should be .
Negative Weight Cycles: Not good!
For the shortest path algorithms: we can use simple BFS algorithms: Expand an weighted edge to several simple edges from to . (Then we can count the shortest path by the definition of the number of the edges.): Use BFS to solve this shortest-path problems!
Disadvantages: It is linear time, needs quite large memory expansion if the weight is quite large.
Proofs
Thm: For weighted shortest path, only need parent of () for only finite
- Init: P is empty, is None.
- For each , where is finite:
- For each :
- if not and . (Suppose it is the shortest path!)
- and can be the shortest path. So set .
- For each :
is used to construct one of the existing shortest path.
DAG
有向无环图(DAG, Directed Acyclic Graph)
It can handle with negative weights, but it cannot handle with Negative Weight Cycles.
Relaxation
For edges , we need to check it is possible to shorten the shortest path from to .
Obviously, we have:
Hence, if , then we can update
It is DP Problems! Because we can assert that there is no loop in the DAGs, hence we use Topological Order to ensure all the nodes are sorted in sequence.
Assume is ahead in the topological order of , then is computed before the .
For the first vertex is itself, init , and .
In topological order, we update by using the adjacency ,(which is computed previously) of .
Then we update the optimization! Finally, .
Time complexity:
- Topo sort:
- relaxation:
DAG is efficiency in SSSP! For it is based on dynamic programming. But it should ensure it is DAG! (Needs topo sort first).
Code
1 |
|
Bellman-Ford Algorithms
In DAG, we can perfectly solve this problem with linear time: . But we do not know how to deal with the negative cycles. We will introduce Bellman-Ford algorithm, which could solve this problem in the general graph (even with the negative cycles with ).
Hence, we want to solve:
- return the shortest path from every vertex to a given vertex .
- if not reachable, return
- if has negative loops, return
- we want to detect whether a graph has a negative cycle.
- Given a graph , how can we detect whether there has a negative cycle in the graph.
Negative Weight Edges
For negative weight edges:
- For undirected graph: it is meaningless, for a single negative weight can cause .
- Only for directed graphs.
If we have an algorithm that can solve this problem in time, we can actually optimize this!
First, use Kosaraju algorithms to find all the connected components for this directed graph with linear time . Then, for each component, we can use the algorithms, where . Then we can prove that in this case: .
Simple Shortest Path
Claim1: If is finite, then there exists a shortest path that is simple.
- This path has less than edges.
We define means the shortest path using less than edges.
\lim_{k \to \infty} \delta_k(s,v) = \delta(s,v)$$. If $\delta_{|V|}(s,v) < \delta_{|V|-1}(s,v)$, then we know $\delta(s,v) = -\infty$.  - witness can be described as a vertex in the negative loop cycle. Use **graph duplication**: make multiple copies (or levels) of the graph. $|V| + 1$ levels: vertex $v_k $ in level $ k$ represents reaching vertex$ v$ from $s$ using $≤ k$ edges. If edges only increase in levels, we can use **DAG**!  For the graph $G'$, it has $|V|\times |V + 1|$ vertices and $|V| \times |E|$ edges. And it is a directed graph! We can solve it using DAG with linear time. $O((|V| \times |E|)\times |V|)$. Then we can use $\delta_{|V|}(s,v) < \delta_{|V|-1}(s,v)$ to judge whether it is $-\infty$. $$\delta_k(s,v) = \delta'(s,v_k)$$. ### Fix: Bellman-Ford Version 2 ## Dijkstra Algorithm - DAG: - Best performance, linear time: $O(|V| + |E|)$. - **Restriction**: cannot handle with circle path. - Bellman-Ford: - $O(|V|(|V| + |E|))$ - for all kinds of graphs, general. - **Dijkstra Algorithms**: for non-negative weights, almost linear. ### Intuition
Finally, I know it is SPFA…
We will also use the triangle inequality, which is of the core principle for the greedy algorithms:
where means the shortest length from to .
If we can ensure the correct path of will be calculated before the , then we can ensure the rightness of !
实际上,上文的Dijkstra算法是可以处理负权边的,因为其一个节点可以访问多次,代表着其值可以被不断的更新。并且我们会保证有穷最短路径的走法一定会被走到,因此我们可以保证最后答案的正确性。
但是在原始的Dijkstra算法中,会记录每一个节点是否被访问,并且不使用优先级队列来实现。(这个不同算法的实现方式不一样),如果记录每一个节点是否被访问,这个时候为了保证贪心算法的正确性,我们不可以处理负权边。
当然,肯定不可以处理 negative weight cycles.
从的节点到允许使用号节点,考虑:
- 号节点被用做最短路的路径,即此时的最短路是,根据最短路的性质,的最短路肯定和的最短路是重合的(因为能保证第k个节点一定在x到y的最短路中)
- 因此这一项就是:
- 号节点没有被用作最短路的路径,就是.
这就是Floyd算法的核心!
不过缺点显而易见:时空复杂度都非常高,因为我们需要维护非常庞大的状态数组。我们下面证明数组的第一位没有影响,即:
对于给定的 k
,当更新 f[k][x][y]
时,涉及的元素总是来自 f[k-1]
数组的第 k
行和第 k
列。然后我们可以发现,对于给定的 k
,当更新 f[k][k][y]
或 f[k][x][k]
,总是不会发生数值更新,因为按照公式 f[k][k][y] = min(f[k-1][k][y], f[k-1][k][k]+f[k-1][k][y])
,f[k-1][k][k]
为 0,因此这个值总是 f[k-1][k][y]
,对于 f[k][x][k]
的证明类似。
因此,如果省略第一维,在给定的 k
下,每个元素的更新中使用到的元素都没有在这次迭代中更新,因此第一维的省略并不会影响结果。
不过三层循环不可以省略,只是对空间的小优化:
1 |
|
拆点
分层图最短路,如:有次零代价通过一条路径,求总的最小花费。
同样的,使用状态转移方程,
其中是的父亲节点。
事实上,这个 DP 就相当于把每个结点拆分成了个结点,每个新结点代表使用不同多次免费通行后到达的原图结点。换句话说,就是每个结点表示使用 次免费通行权限后到达 结点。