# 演算法作業8
###### tags: `演算法`
1. Given an adjacency-list representation of a directed graph, how long does it take to compute the out-degree of every vertex? How long does it take to compute the in-degrees?
Answer:
假設此adjacency-list都是紀錄該vertex所指向的vertex,計算全部vertex的out degree的時間會是$O(|V|+|E|)$,計算所有vertex的時間同樣是$O(|V|+|E|)$,因為同樣需要將每個vertex的list都歷遍。
```cpp=
input: G//G為一個graph
Out_Deg(G){
for each u in G.V
u.out_deg=0
for each v in G.Adj[u]
u.out_deg+=1
}
In_Deg(G){
for each u in G.V
u.in_deg=0
for each u in G.V
for each v in G.Adj[u]
v.in_deg+=1
}
```
2. The transpose of a directed graph $G = (V, E)$ is the graph $G^T = (V, E^T)$, where $E^T = { (v,u) ∈ V x V : (u,v) ∈ E }$. Thus, $G^T$ is G with all its edges reversed. Describe efficient algorithms for computing $G^T$ from G, for both the adjacency-list and adjacencymatrix representations of G. Analyze the running times of your algorithms.
Answer:
對於adjacency matrix來說,將G做transpose相當於將adjacency matrix做transpose,可以經由以下pseudo code看到總共會做兩層1~n的迴圈,因此時間複雜度是$O(n^2)$,也就是$O(|V|^2)$
對於adjacency list來說,做transpose相當於在計算各vertex的in degree的變形,可經由以下pseudo code看到雖然有兩層迴圈,但是實際上做的事情就是把原本的adj_list全部歷遍一次,相當於把每個點跟邊都走過一次,因此時間複雜度是$O(|V|+|E|)$
```cpp=
G.Adj為原圖的adj matrix,G.T_Adj為轉換過的adj matrix
Transpose(G){
let G.T_Adj be a new matrix
for i=1:|G.V|
for j=1:|G.V|
G.T_Adj[j][i]=G.Adj[i][j]
return G.T_Adj[][]
}
G.Adj為原圖的adj list,G.T_Adj為轉換過的adj list
Transpose(G){
let G.T_Adj be a new adj list
for each u in G.V
for each v in G.Adj[u]
G.T_Adj[v].add(u)
return G.T_Adj
}
```
3. The square of a directed graph G = (V, E) is the graph $G^2 = (V, E^2)$ such that $(u, v) ∈ E^2$ if and only if G contains a path with at most two edges between u and v.Describe efficient algorithms for computing $G^2$ from G for both the adjacency-list and adjacency-matrix representations of G. Analyze the running times of your algorithms.
Answer:
$(u,v) ∈ E^2$,代表(u,v)在G當中相隔的邊數不超過2
adjacency list的square中每次u碰到自己的鄰近vertex,就會把該vertex的adjacency list再搜索一次
假設$(u,v) ∈ E$,那在找Square的過程中,(u,v)會被碰到的情況有,找u的square_list的時候會碰到1+v.out_degree次,任何$(w,u) ∈ E$也都會各找(u,v)一次,會有u.in_degree次,所以對於任意$(u,v) ∈ E$來說,計算Square過程總共會碰到它 1+(u.in_degree+v.out_degree),將全部的邊碰到的次數加起來會是$\sum{(1+u.In+v.Out)}=|E|+\sum{(u.In+v.Out)}=|E|+\sum{(u.In)(u.Out)}$
最糟的情況會是每個vertex的子vertex都是|V|-1個,代表每次都必須將整個adjacency list走過一遍,複雜度會是$O(|E|)$,總共有|V|個vertex所以複雜度是$O(|V||E|)$
adj_matrix則是除了將原本的matrix走過一遍之外,如果碰到有邊的元素,會將該j的row也挑出來走一次,最糟情況就是每個j都被挑出來走一次,所以時間複雜度是$O(n^3)$也就是$O(|V|^3)$。
```cpp=
input: G
Square(G){
let G.Square be a new adj matrix
for i=1:|G.V|
for j=1:|G.V|
G.Square[i][j]=0
for k=1:|G.V|
G.Square[i][j]=G.Square[i][j] or (G.Adj[i][k] and G.Adj[k][j])
return G.Square
}
Square(G){
let G.Square be a new adj list
for each u in G.V
for each v in G.Adj[u]
G.Square[u].add(v)
for each w in G.Adj[v]
G.Square[u].add(w)
return G.Square
}
```
4. There are two types of professional wrestlers: “babyfaces” (“good guys”) and “heels” (“bad guys”). Between any pair of professional wrestlers, there may or may not be a rivalry. Suppose we have n professional wrestlers and we have a list of r pairs of wrestlers for which there are rivalries. Give an O(n + r)-time algorithm that determines whether it is possible to designate some of the wrestlers as babyfaces and the remainder as heels such that each rivalry is between a babyface and a heel. If is it possible to perform such a designation, your algorithm should produce it.
Answer:
將每個wrestler當成vertex,將rivalries當成edge,可以組出一張對戰圖,使用BFS來將此圖走訪一次,如果有sibling之間的edge那就不可能組成一個完整的對戰,如果沒有那就會得到一個對戰圖
使用BFS因此時間複雜度是$O(|V|+|E|)$,在此題中也就是$O(n+r)$。
可以看到pseudo code第30行的地方,如果遇到一個vertex是灰色,且該vertex的深度和自己相同,代表該vertex是自己的sibling,也就是說此graph當中有sibling之間有邊,只要有sibling之間有邊那就沒辦法讓每個邊的兩的vertex都是babyface對上heels,所以直接回傳NULL,表示沒有這可能,如果能一直執行到結束,將每個node也就是每個wrestler訂好他們的type,那就將此圖回傳。
*延伸練習: uva10004 Bicoloring*
```cpp=
struct Wrestler{
id;
type;//babyface or heels,0代表babyface,1代表heels
depth;//在bfs tree中的深度,root深度為0
parent;//紀錄父節點
color;//紀錄顏色
};
BFS_designation(G,root){
for each v in G.V //for each vertex in the vertexs in Graph
v.color=WHITE
v.depth=INF
v.parent=NULL
root.type=0
root.color=GRAY
root.depth=0
Q={} //let Q be a new empty queue
Q.push(root)
while(!Q.empty()){
u=Q.pop()
for each v ∈ G.Adj[u]
if v.color==WHITE
v.color=GRAY
v.depth=u.depth+1
v.parent=u
v.type=(!u.type)
Q.push(v)
else if v.depth==u.depth
return NULL
u.color=BLACK
}
return G
}
```
5. Give a linear-time algorithm that takes as input a directed acyclic graph G = (V, E) and two vertices s and t, and returns the number of simple paths from s to t in G. For example, the directed acyclic graph of Figure 22.8 contains exactly four simple paths from vertex p to vertex v: pov, poryv, posryv, and psryv. (Your algorithm needs only to count the simple paths, not list them.)
Answer:
因為給定的是DAG,所以任意兩vertex之間若存在path,那一定是simple path,如果不是那代表此圖有路徑會形成cycle,那就不是DAG。所以在DAG中找到任意兩點的simple paths數量就用DFS稍微修改就可以了,時間複雜度和DFS一樣是$O(|V|+|E|)$
另外可以用topologocal sort或bfs也都能夠解此題,時間複雜度也都是$O(|V|+|E|)$
```cpp=
input: DAG,start,end //DAG為一個graph的資料結構,start和end為vertex
FIND_PATHs(DAG,start,end){
for each v in DAG.V
v.paths=0
return BFS(DAG,start,end) //start是dfs的root
}
BFS(G,start,end){
start.paths=1
Q={}//init a FIFO queue
Q.push(start)
while(!Q.empty()){
u=Q.pop() //取得front的元素
for each v in G.V
v.paths+=u.paths
Q.push(v)
}
return end.paths
}
//Topolocal sort method
Simple(G,s,t){
G.V=Topological_Sort(G) //將vertex按照topological sort來排列
for each v in G.V
v.paths=0
s.paths=1
for each u in G.V
for each v in G.Adj[u]
v.paths+=u.paths
return t.paths
}
```
6. Give an algorithm that determines whether or not a given undirected graph G = (V, E) contains a cycle. Your algorithm should run in O(V) time, independent of |E|.
Answer:
**undirected graph中的edge不是tree edge就是back edge**
無向圖如果沒有環代表此圖也是一顆樹,或是forests,也就是說$|E|=|V|-|T|$,$|T|$是樹的數量,如果是單一顆樹就是1,如果是forests那|T|就是此forests當中的tree數量,所以$|T|>=1$,所以沒有環且每個vertex degree都大於等於1的時候$|E| \le |V|-1$,但我們不能確定是不是每個vertex degree都大於0,不過我們可以確定的是如果$|E|>|V|-1$,那肯定有環,我們可以用DFS做檢查,因為DFS的複雜度是$O(|V|+|E|)$決定,但我們知道不需要檢查$|E|$超過$|V|$次,所以 $|E|=O(|V|)$,若 $|E|<=|V|-1$,則檢查有沒有back edge,若存在back edge則為有環。
所以總時間複雜度就是DFS的時間複雜度 $O(|V|+|E|)=O(|V|+|V|-1)=O(|V|)$
**假設此圖不是multigraph**
```cpp=
input: G//G為一個graph
global variable:edge_num,vertex_num
Cyclic(G){
if |G.E|>=|G.V|
return true
for each v in G.V
v.visit=false
v.parent=NULL
for each v in G.V
if v.visit==false
v.visit=true
if(DFS_check(G,v))
return true //有環
return false //如果整個圖都跑過一遍且沒環回傳false
}
DFS_check(G,v){
for each u in G.Adj[v]
if !u.visit
u.visit=true
u.parent=v
if DFS_check(G,u)
return true
else if v.parent!=u //back edge, v.parent==u代表此edge是tree edge,只是因為undirected graph會重複走一次
return true
return false //以v為root做dfs沒有找到環
}
```
如果是directed graph,那不可能在O(|V|)的時間內檢查此圖有沒有cycle,因為沒有辦法確定|E|>|V|-1就代表有環,必須將edge走過一遍或是使用topological sorting來判別。
7. Give an algorithm that determines whether or not a given undirected graph G = (V, E) contains an euler circuit,and if the answer is positive your algorithm should output an euler circuit.
Answer:
如果一個graph有euler circuit,代表至少可以從任意一個node,以它為起點出發,每個邊都剛好只經過一次且能繞回到該node。代表每個node的degree都必須是偶數且大於等於2,因為每個node都要可以走進去再走出來,只要檢查每個node的degree看有沒有符合此條件就行,如果用adjacency list則時間複雜度會是$O(|V|+|E|)$
```cpp=
input: G
Euler_Cir(G,root){ //randomly choose a vertex to be root
for each u in G.V
u.visit=false
count=DFS_count(G,root) //計算從root能到達且合法的vertex數量
if count<|G.V|
return false
stack S_cur,S_e
S_cur.push(root)
for each edge (u,v) in G.E
(u,v).visit=false
while(!S_cur.empty()){
u=S_cur.top()
done=true
for each v in G.Adj[u]
if !(u,v).visit
S_cur.push(v)
(u,v).visit=true
(v,u).visit=true
done=false
if done
S_e.push(u)
S_cur.pop()
}
while(!S_e.empty())
print S_e.top()
S_e.pop()
return true
}
DFS_count(G,u){
count=0
for each v in G.Adj[u]
if !v.visit
v.visit=true
count+=DFS_count(G,u)
if u.degree%2==0 and u.degree>=2
count+=1
return count
}
```
=======================================================
補充:
DFS所得到每個vertex的discover time還有endtime,如果將vertex用discover time排序,會得到這個DFS forest的preorder,如果用end time排序,會得到postorder