DFS and BFS
This commit is contained in:
parent
38c6cda3ed
commit
a0abff3f0a
248
luku12.tex
248
luku12.tex
|
@ -1,38 +1,38 @@
|
||||||
\chapter{Graph search}
|
\chapter{Graph search}
|
||||||
|
|
||||||
Tässä luvussa tutustumme
|
This chapter introduces two fundamental
|
||||||
syvyyshakuun ja leveyshakuun, jotka
|
graph algorithms:
|
||||||
ovat keskeisiä menetelmiä verkon läpikäyntiin.
|
depth-first search and breadth-first search.
|
||||||
Molemmat algoritmit lähtevät liikkeelle
|
Both algorithms are given a starting
|
||||||
tietystä alkusolmusta ja
|
node in the graph,
|
||||||
käyvät läpi kaikki solmut,
|
and they visit all nodes that can be reached
|
||||||
joihin alkusolmusta pääsee.
|
from the starting node.
|
||||||
Algoritmien erona on,
|
The difference in the algorithms is the order
|
||||||
missä järjestyksessä ne kulkevat verkossa.
|
in which they visit the nodes.
|
||||||
|
|
||||||
\section{Syvyyshaku}
|
\section{Depth-first search}
|
||||||
|
|
||||||
\index{syvyyshaku@syvyyshaku}
|
\index{depth-first search}
|
||||||
|
|
||||||
\key{Syvyyshaku}
|
\key{Depth-first search} (DFS)
|
||||||
on suoraviivainen menetelmä verkon läpikäyntiin.
|
is a straightforward graph search technique.
|
||||||
Algoritmi lähtee liikkeelle tietystä
|
The algorithm begins at a starting node,
|
||||||
verkon solmusta ja etenee siitä
|
and proceeds to all other nodes that are
|
||||||
kaikkiin solmuihin, jotka ovat
|
reachable from the starting node using
|
||||||
saavutettavissa kaaria kulkemalla.
|
the edges in the graph.
|
||||||
|
|
||||||
Syvyyshaku etenee verkossa syvyyssuuntaisesti
|
Depth-first search always follows a single
|
||||||
eli kulkee eteenpäin verkossa niin kauan
|
path in the graph as long as it finds
|
||||||
kuin vastaan tulee uusia solmuja.
|
new nodes.
|
||||||
Tämän jälkeen haku perääntyy kokeilemaan
|
After this, it returns back to previous
|
||||||
muita suuntia.
|
nodes and begins to explore other parts of the graph.
|
||||||
Algoritmi pitää kirjaa vierailemistaan solmuista,
|
The algorithm keeps track of visited nodes,
|
||||||
jotta se käsittelee kunkin solmun vain kerran.
|
so that it processes each node only once.
|
||||||
|
|
||||||
\subsubsection*{Esimerkki}
|
\subsubsection*{Example}
|
||||||
|
|
||||||
Tarkastellaan syvyyshaun toimintaa
|
Let's consider how depth-first search processes
|
||||||
seuraavassa verkossa:
|
the following graph:
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle] (1) at (1,5) {$1$};
|
\node[draw, circle] (1) at (1,5) {$1$};
|
||||||
|
@ -48,13 +48,11 @@ seuraavassa verkossa:
|
||||||
\path[draw,thick,-] (2) -- (5);
|
\path[draw,thick,-] (2) -- (5);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Syvyyshaku voi lähteä liikkeelle
|
The algorithm can begin at any node in the graph,
|
||||||
mistä tahansa solmusta,
|
but we will now assume that it begins
|
||||||
mutta oletetaan nyt,
|
at node 1.
|
||||||
että haku lähtee liikkeelle solmusta 1.
|
|
||||||
|
|
||||||
Solmun 1 naapurit ovat solmut 2 ja 4,
|
The search first proceeds to node 2:
|
||||||
joista haku etenee ensin solmuun 2:
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
||||||
|
@ -72,8 +70,7 @@ joista haku etenee ensin solmuun 2:
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
|
\path[draw=red,thick,->,line width=2pt] (1) -- (2);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Tämän jälkeen haku etenee vastaavasti
|
After this, nodes 3 and 5 will be visited:
|
||||||
solmuihin 3 ja 5:
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
||||||
|
@ -93,13 +90,12 @@ solmuihin 3 ja 5:
|
||||||
\path[draw=red,thick,->,line width=2pt] (3) -- (5);
|
\path[draw=red,thick,->,line width=2pt] (3) -- (5);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Solmun 5 naapurit ovat 2 ja 3,
|
The neighbors of node 5 are 2 and 3,
|
||||||
mutta haku on käynyt jo molemmissa,
|
but the search has already visited both of them,
|
||||||
joten on aika peruuttaa taaksepäin.
|
so it's time to return back.
|
||||||
Myös solmujen 3 ja 2 naapurit on käyty,
|
Also the neighbors of nodes 3 and 2
|
||||||
joten haku peruuttaa solmuun 1 asti.
|
have been visited, so we'll next proceed
|
||||||
Siitä lähtee kaari, josta pääsee
|
from node 1 to node 4:
|
||||||
solmuun 4:
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
||||||
|
@ -117,74 +113,70 @@ solmuun 4:
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Tämän jälkeen haku päättyy,
|
After this, the search terminates because it has visited
|
||||||
koska se on käynyt kaikissa solmuissa.
|
all nodes.
|
||||||
|
|
||||||
Syvyyshaun aikavaativuus on $O(n+m)$,
|
The time complexity of depth-first search is $O(n+m)$
|
||||||
missä $n$ on solmujen määrä ja $m$ on kaarten määrä,
|
where $n$ is the number of nodes and $m$ is the
|
||||||
koska haku käsittelee kerran jokaisen solmun ja kaaren.
|
number of edges,
|
||||||
|
because the algorithm processes each node and edge once.
|
||||||
|
|
||||||
\subsubsection*{Toteutus}
|
\subsubsection*{Implementation}
|
||||||
|
|
||||||
Syvyyshaku on yleensä mukavinta toteuttaa
|
Depth-first search can be conveniently
|
||||||
rekursiolla.
|
implemented using recursion.
|
||||||
Seuraava funktio \texttt{haku}
|
The following function \texttt{dfs} begins
|
||||||
suorittaa syvyyshaun sille parametrina
|
a depth-first search at a given node.
|
||||||
annetusta solmusta lähtien.
|
The function assumes that the graph is
|
||||||
Funktio olettaa, että
|
stored as adjacency lists in array
|
||||||
verkko on tallennettu vieruslistoina
|
|
||||||
taulukkoon
|
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
vector<int> v[N];
|
vector<int> v[N];
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
ja pitää lisäksi yllä taulukkoa
|
and also maintains an array
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int z[N];
|
int z[N];
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
joka kertoo, missä solmuissa haku on käynyt.
|
that keeps track of the visited nodes.
|
||||||
Alussa taulukon jokainen arvo on 0,
|
Initially, each array value is 0,
|
||||||
ja kun haku saapuu solmuun $s$,
|
and when the search arrives at node $s$,
|
||||||
kohtaan \texttt{z}[$s$] merkitään 1.
|
the value of \texttt{z}[$s$] becomes 1.
|
||||||
Funktion toteutus on seuraavanlainen:
|
The function can be implemented as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
void haku(int s) {
|
void dfs(int s) {
|
||||||
if (z[s]) return;
|
if (z[s]) return;
|
||||||
z[s] = 1;
|
z[s] = 1;
|
||||||
// solmun s käsittely tähän
|
// process node s
|
||||||
for (auto u: v[s]) {
|
for (auto u: v[s]) {
|
||||||
haku(u);
|
dfs(u);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
|
|
||||||
\section{Leveyshaku}
|
\section{Breadth-first search}
|
||||||
|
|
||||||
\index{leveyshaku@leveyshaku}
|
\index{breadth-first search}
|
||||||
|
|
||||||
\key{Leveyshaku}
|
\key{Breadth-first search} (BFS) visits the nodes
|
||||||
käy solmut läpi järjestyksessä sen mukaan,
|
in increasing order of their distance
|
||||||
kuinka kaukana ne ovat alkusolmusta.
|
from the starting node.
|
||||||
Niinpä leveyshaun avulla pystyy laskemaan
|
Thus, we can calculate the distance
|
||||||
etäisyyden alkusolmusta kaikkiin
|
from the starting node to all other
|
||||||
muihin solmuihin.
|
nodes using breadth-first search.
|
||||||
Leveyshaku on kuitenkin vaikeampi
|
However, breadth-first search is more difficult
|
||||||
toteuttaa kuin syvyyshaku.
|
to implement than depth-first search.
|
||||||
|
|
||||||
Leveyshakua voi ajatella niin,
|
Breadth-first search goes through the nodes
|
||||||
että se käy solmuja läpi kerros kerrallaan.
|
one level after another.
|
||||||
Ensin haku käy läpi solmut,
|
First the search explores the nodes whose
|
||||||
joihin pääsee yhdellä kaarella
|
distance from the starting node is 1,
|
||||||
alkusolmusta.
|
then the nodes whose distance is 2, and so on.
|
||||||
Tämän jälkeen vuorossa ovat
|
This process continues until all nodes
|
||||||
solmut, joihin pääsee kahdella
|
have been visited.
|
||||||
kaarella alkusolmusta, jne.
|
|
||||||
Sama jatkuu, kunnes uusia käsiteltäviä
|
|
||||||
solmuja ei enää ole.
|
|
||||||
|
|
||||||
\subsubsection*{Esimerkki}
|
\subsubsection*{Example}
|
||||||
|
|
||||||
Tarkastellaan leveyshaun toimintaa
|
Let's consider how the algorithm processes
|
||||||
seuraavassa verkossa:
|
the following graph:
|
||||||
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
|
@ -204,11 +196,9 @@ seuraavassa verkossa:
|
||||||
\path[draw,thick,-] (5) -- (6);
|
\path[draw,thick,-] (5) -- (6);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Oletetaan jälleen,
|
Assume again that the search begins at node 1.
|
||||||
että haku alkaa solmusta 1.
|
First, we process all nodes that can be reached
|
||||||
Haku etenee ensin kaikkiin solmuihin,
|
from node 1 using a single edge:
|
||||||
joihin pääsee alkusolmusta:
|
|
||||||
\\
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
||||||
|
@ -234,8 +224,7 @@ joihin pääsee alkusolmusta:
|
||||||
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
\path[draw=red,thick,->,line width=2pt] (1) -- (4);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Seuraavaksi haku etenee solmuihin 3 ja 5:
|
After this, we procees to nodes 3 and 5:
|
||||||
\\
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
||||||
|
@ -261,8 +250,7 @@ Seuraavaksi haku etenee solmuihin 3 ja 5:
|
||||||
\path[draw=red,thick,->,line width=2pt] (2) -- (5);
|
\path[draw=red,thick,->,line width=2pt] (2) -- (5);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Viimeisenä haku etenee solmuun 6:
|
Finally, we visit node 6:
|
||||||
\\
|
|
||||||
\begin{center}
|
\begin{center}
|
||||||
\begin{tikzpicture}
|
\begin{tikzpicture}
|
||||||
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
\node[draw, circle,fill=lightgray] (1) at (1,5) {$1$};
|
||||||
|
@ -288,14 +276,13 @@ Viimeisenä haku etenee solmuun 6:
|
||||||
\path[draw=red,thick,->,line width=2pt] (5) -- (6);
|
\path[draw=red,thick,->,line width=2pt] (5) -- (6);
|
||||||
\end{tikzpicture}
|
\end{tikzpicture}
|
||||||
\end{center}
|
\end{center}
|
||||||
Leveyshaun tuloksena selviää etäisyys
|
Now we have calculated the distances
|
||||||
kuhunkin verkon solmuun alkusolmusta.
|
from the starting node to all nodes in the graph.
|
||||||
Etäisyys on sama kuin kerros,
|
The distances are as follows:
|
||||||
jossa solmu käsiteltiin haun aikana:
|
|
||||||
|
|
||||||
\begin{tabular}{ll}
|
\begin{tabular}{ll}
|
||||||
\\
|
\\
|
||||||
solmu & etäisyys \\
|
node & distance \\
|
||||||
\hline
|
\hline
|
||||||
1 & 0 \\
|
1 & 0 \\
|
||||||
2 & 1 \\
|
2 & 1 \\
|
||||||
|
@ -306,49 +293,50 @@ solmu & etäisyys \\
|
||||||
\\
|
\\
|
||||||
\end{tabular}
|
\end{tabular}
|
||||||
|
|
||||||
Leveyshaun aikavaativuus on syvyyshaun tavoin $O(n+m)$,
|
Like in depth-first search,
|
||||||
missä $n$ on solmujen määrä ja $m$ on kaarten määrä.
|
the time complexity of breadth-first search
|
||||||
|
is $O(n+m)$ where $n$ is the number of nodes
|
||||||
|
and $m$ is the number of edges.
|
||||||
|
|
||||||
\subsubsection*{Toteutus}
|
\subsubsection*{Implementation}
|
||||||
|
|
||||||
Leveyshaku on syvyyshakua hankalampi toteuttaa,
|
Breadth-first search is more difficult
|
||||||
koska haku käy läpi solmuja verkon eri
|
to implement than depth-first search
|
||||||
puolilta niiden etäisyyden mukaan.
|
because the algorithm visits nodes
|
||||||
Tyypillinen toteutus on pitää yllä jonoa
|
in different parts in the graph.
|
||||||
käsiteltävistä solmuista.
|
A typical implementation is to maintain
|
||||||
Joka askeleella otetaan käsittelyyn seuraava
|
a queue of nodes to be processed.
|
||||||
solmu jonosta ja uudet solmut lisätään
|
At each step, the next node in the queue
|
||||||
jonon perälle.
|
will be processed.
|
||||||
|
|
||||||
Seuraava koodi toteuttaa leveyshaun
|
The following code begins a breadth-first
|
||||||
solmusta $x$ lähtien.
|
search at node $x$.
|
||||||
Koodi olettaa, että verkko on tallennettu
|
The code assumes that the graph is stored
|
||||||
vieruslistoina, ja pitää yllä jonoa
|
as adjacency lists and maintains a queue
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
queue<int> q;
|
queue<int> q;
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
joka sisältää solmut käsittelyjärjestyksessä.
|
that contains the nodes in increasing order
|
||||||
Koodi lisää aina uudet vastaan tulevat solmut
|
of their distance.
|
||||||
jonon perään ja ottaa seuraavaksi käsiteltävän
|
New nodes are always added to the end
|
||||||
solmun jonon alusta,
|
of the queue, and the node at the beginning
|
||||||
minkä ansiosta solmut käsitellään
|
of the queue is the next node to be processed.
|
||||||
kerroksittain alkusolmusta lähtien.
|
|
||||||
|
|
||||||
Lisäksi koodi käyttää taulukoita
|
In addition, the code uses arrays
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
int z[N], e[N];
|
int z[N], e[N];
|
||||||
\end{lstlisting}
|
\end{lstlisting}
|
||||||
niin, että taulukko \texttt{z} sisältää tiedon,
|
so that array \texttt{z} indicates
|
||||||
missä solmuissa haku on käynyt,
|
which nodes the search already has visited
|
||||||
ja taulukkoon \texttt{e} lasketaan lyhin
|
and array \texttt{e} will contain the
|
||||||
etäisyys alkusolmusta kaikkiin verkon solmuihin.
|
minimum distance to all nodes in the graph.
|
||||||
Toteutuksesta tulee seuraavanlainen:
|
The search can be implemented as follows:
|
||||||
\begin{lstlisting}
|
\begin{lstlisting}
|
||||||
z[s] = 1; e[x] = 0;
|
z[s] = 1; e[x] = 0;
|
||||||
q.push(x);
|
q.push(x);
|
||||||
while (!q.empty()) {
|
while (!q.empty()) {
|
||||||
int s = q.front(); q.pop();
|
int s = q.front(); q.pop();
|
||||||
// solmun s käsittely tähän
|
// process node s
|
||||||
for (auto u : v[s]) {
|
for (auto u : v[s]) {
|
||||||
if (z[u]) continue;
|
if (z[u]) continue;
|
||||||
z[u] = 1; e[u] = e[s]+1;
|
z[u] = 1; e[u] = e[s]+1;
|
||||||
|
|
Loading…
Reference in New Issue