Ćwiczenie 67.
Napisać algorytmy porządkowania wierzchołków grafu spójnego w po- rządku preorder i w porządku postorder (użyć pseudokodu lub jakiegokolwiek języka programowania).
Rozwiązanie
Program, którego kod źródłowy został załączony, został napisany w ję- zyku C++.
Odczytuje on ze standardowego wejścia dane w następującym formacie:
• Rozdzielone spacją liczby |V | oraz |E| zapisane w pierwszej linii.
• |V | oddzielonych spacjami nazw wierzchołków w drugiej linii.
• W następnych |E| liniach - pary nazw wierzchołków, które są połączone krawędziami.
• W ostatniej linii - typ przeszukiwania (”PREORDER” albo ”PO- STORDER”) oraz nazwa wierzchołka, od którego przeszukiwanie zo- stanie rozpoczęte.
Kolejność podawania wierzchołków oraz krawędzi nie ma znaczenia. Do- konano następujących założeń: wierzchołki mają różne nazwy, dla każdej krawędzi oba jej końce są istniejącymi wierzchołkami, wierzchołek od któ- rego ma być rozpoczęte przeszukiwanie istnieje, oraz graf jest spójny. Ze- staw testowy jest przykładem grafu mającym na celu zademonstrowanie, że program prawidłowo obsługuje krawędzie wielokrotne i pętle, oraz że po- za numerami preorder lub postorder zaznaczane jest wybrane drzewo prze- szukiwania. Drzewo to jest zdefiniowane jednoznacznie - relacją porządku w zbiorze sąsiadów każdego wierzchołka jest porównywanie nazw jako napi- sów.
Program wypisuje w odpowiedzi opis grafu w formacie dot, który pozwo-
lił na wyrenderowanie wizualizacji grafów zawartych w opisie rozwiązania za
pomocą narzędzia graphviz.
Przykład pliku do wczytania:
12 19
a b c d e f g h i j k l
j f j k g e e l d k d h h d d a h c b f g f d j g l b k a j a g l e i i i k
PREORDER a
Wyjście programu w języku dot:
gr aph G {
a [ l a b e l =”a : 1 ” , p e r i p h e r i e s = 2 ] ; d [ l a b e l =”d : 2 ” ] ;
h [ l a b e l =”h : 3 ” ] ; c [ l a b e l =”c : 4 ” ] ; h −− c ;
d −− h ;
d −− h [ s t y l e =”d a s h e d ” ] ; j [ l a b e l =” j : 5 ” ] ;
f [ l a b e l =” f : 6 ” ] ; b [ l a b e l =”b : 7 ” ] ; k [ l a b e l =”k : 8 ” ] ; i [ l a b e l =” i : 9 ” ] ;
i −− i [ s t y l e =”d a s h e d ” ] ; k −− i ;
b −− k ; f −− b ;
g [ l a b e l =”g : 1 0 ” ] ; e [ l a b e l =”e : 1 1 ” ] ; l [ l a b e l =” l : 1 2 ” ] ; e −− l ;
e −− l [ s t y l e =”d a s h e d ” ] ; g −− e ;
g −− l [ s t y l e =”d a s h e d ” ] ; f −− g ;
j −− f ;
j −− k [ s t y l e =”d a s h e d ” ] ; d −− j ;
d −− k [ s t y l e =”d a s h e d ” ] ; a −− d ;
a −− g [ s t y l e =”d a s h e d ” ] ; a −− j [ s t y l e =”d a s h e d ” ] ;
} ;
Oraz wygenerowana na podstawie tego wyjścia wizualizacja grafu:
a:1
d:2
j:5
g:10 h:3
k:8
c:4 f:6
b:7
i:9
e:11
l:12
Zmieniając ostatnią linijkę, dla tego samego grafu mogę ponumerować wierzchołki w kolejności postorder:
c:1 h:2
d:11
k:4 j:10
i:3 b:5 f:9
g:8
l:6 e:7 a:12
Albo preorder (po lewej) oraz postorder (po prawej), zaczynając od wierzchołka i (wizualizacje różnią się w granicach dopuszczalnych dla na- rzędzia do ich tworzenia - drzewo przeszukiwania jest w obu przypadkach takie samo):
i:1
k:2
b:3
f:4
g:5
j:10 a:6 e:11
l:12 d:7
h:8
c:9
i:12
k:11
c:1 h:2 d:4
j:3 b:10
a:5 g:8
l:6 e:7 f:9
Kod źródłowy w języku C++
#i n c l u d e < c s t d l i b >
#i n c l u d e <c a s s e r t >
#i n c l u d e <i o s t r e a m >
#i n c l u d e <s t r i n g >
#i n c l u d e <v e c t o r >
#i n c l u d e <s e t >
#i n c l u d e <map>
// W i e r z c h o l e k s t r u c t V e r t e x {
// R e l a c j a p o r z a d k u pomiedzy w i e r z c h o l k a m i ( p o r z a d e k l e k s y k o g r a f i c z n y nazw ) s t r u c t PointerCompare {
b o o l o p e r a t o r( ) ( V e r t e x ∗ c o n s t &l e f t , V e r t e x ∗ c o n s t &r i g h t ) c o n s t { r e t u r n l e f t −>name < r i g h t −>name ;
} } ;
// Typ do p r z e c h o w y w a n i a uporzadkowanego z b i o r u s a s i a d o w t y p e d e f s t d : : m u l t i s e t <V e r t e x ∗ , PointerCompare> n e i g h b o u r s S e t ;
i n t i d ; // numer w p a m i e c i
s t d : : s t r i n g name ; // nazwa w i e r z c h o l k a
n e i g h b o u r s S e t n e i g h b o u r s ; // uporzadkowany z b i o r s a s i a d o w // K o n s t r u k t o r i d e s t r u k t o r
V e r t e x ( s t d : : s t r i n g c o n s t & name , i n t c o n s t & i d ) { name = name ; i d = i d ; }
˜ V e r t e x ( ) { }
// Dodawanie s a s i a d a
v o i d addNeighbour ( V e r t e x ∗ c o n s t &nPtr ) { n e i g h b o u r s . i n s e r t ( nPtr ) ;
} } ;
// G r a f
s t r u c t Graph {
// P o r z a d k i numerowania p r z y p r z e s z u k i w a n i u DFS enum V i s i t i n g O r d e r { PREORDER, POSTORDER } ;
// Wektor z danymi k o l e j n o u m i e s z c z a n y c h w g r a f i e w i e r z c h o l k o w s t d : : v e c t o r <Vertex> V ;
// T a b l i c a p o z w a l a j a c a u s t a l i c p o z y c j e w i e r z c h o l k a w w e k t o r z e w e g l u g nazwy s t d : : map<s t d : : s t r i n g , i n t> vertexIdByName ;
// Dodawanie w i e r z c h o l k a
v o i d a d d V e r t e x ( s t d : : s t r i n g c o n s t &name ) { i n t v e r t e x I d = V . s i z e ( ) ;
// A s e r c j a − w i e r z c h o l k i maja r o z n e nazwy
a s s e r t ( vertexIdByName . f i n d ( name ) == vertexIdByName . end ( ) ) ; V . p u s h b a c k ( V e r t e x ( name , v e r t e x I d ) ) ;
vertexIdByName [ name ] = v e r t e x I d ; }
// Dodawanie k r a w e d z i
v o i d addEdge ( s t d : : s t r i n g c o n s t &uName , s t d : : s t r i n g c o n s t &vName ) {
// A s e r c j a − w i e r z c h o l k i , k t o r e maja byc p o l a c z o n e k r a w e d z i a − i s t n i e j a a s s e r t ( vertexIdByName . f i n d ( uName ) != vertexIdByName . end ( ) ) ;
a s s e r t ( vertexIdByName . f i n d ( vName ) != vertexIdByName . end ( ) ) ;
i n t uId = vertexIdByName [ uName ] ;
i n t v I d = vertexIdByName [ vName ] ; V [ uId ] . addNeighbour (&(V [ v I d ] ) ) ;
i f( uId != v I d ) { // warunek z a p o b i e g a j a c y d o d a n i u p e t l i dwa r a z y V [ v I d ] . addNeighbour (&(V [ uId ] ) ) ;
} }
// Metoda d f s p r z y j m u j a c a p o r z a d e k p r z e s z u k i w a n i a i nazwe k o r z e n i a v o i d d f s ( V i s i t i n g O r d e r c o n s t &vm, s t d : : s t r i n g c o n s t &rootName ) {
// A s e r c j a − k o r z e n budowanego drzewa p r z e s z u k i w a n i a w s z e r z i s t n i e j e a s s e r t ( vertexIdByName . f i n d ( rootName ) != vertexIdByName . end ( ) ) ; i n t c o u n t e r = 0 ;
b o o l ∗ v i s i t e d = new b o o l[ V . s i z e ( ) ] ; // i n i c j a l i z a c j a : n i c n i e o d w i e d z o n o d f s (vm, v i s i t e d , c o u n t e r , &(V [ vertexIdByName [ rootName ] ] ) ) ;
a s s e r t ( c o u n t e r == (i n t) V . s i z e ( ) ) ; // G r a f s p o j n y d e l e t e[ ] v i s i t e d ;
}
// Wewnetrzna metoda s t a n o w i a c a r e k u r e n c y j n a i m p l e m e n e t a c j e p r z e b i e g u d f s v o i d d f s ( V i s i t i n g O r d e r c o n s t &vm, b o o l ∗ c o n s t &v i s i t e d , i n t &c o u n t e r ,
V e r t e x ∗ c o n s t &vPtr , V e r t e x ∗ c o n s t &p a r e n t=NULL) { V e r t e x &v = ∗ vPtr ;
v i s i t e d [ v . i d ] = t r u e;
// J e s l i p r e o r d e r , t o n a d a j numerek w i e r z c h o l k o w i t u t a j
i f(vm == PREORDER) v i s i t V e r t e x ( v . name , c o u n t e r , p a r e n t==NULL ) ; // P r z e s z u k i w a n i e s a s i a d o w
// Uwaga − p o j e d y n c z a i t e r a c j a miedzy k o l e j n y m i s a s i a d a m i ma z l o z o n o s c // p e s y m i s t y c z n a O( l o g ( n ) ) , g d z i e n − r o z m i a r z b i o r u s a s i a d o w , j e d n a k // k o s z t i t e r a c j i po calym z b i o r z e a m o r t y z u j e s i e do O( n ) ( m u l t i s e t t o // drzewo czerwono−c z a r n e ) . D l a t e g o z l o z o n o s c c a l e j p r o c e d u r y d f s t o // O ( | E | ) .
f o r( V e r t e x : : n e i g h b o u r s S e t : : i t e r a t o r i t = v . n e i g h b o u r s . b e g i n ( ) ; i t != v . n e i g h b o u r s . end ( ) ; i t ++) {
i f( ∗ i t != p a r e n t ) { // d l a s a s i a d o w poza ojcem i f(n o t v i s i t e d [ ( ∗ i t )−> i d ] ) {
// J e z e l i n i e o d w i e d z o n y − z a g l a b s i e w n i e g o r e k u r e n c y j n i e d f s (vm, v i s i t e d , c o u n t e r , ∗ i t , vPtr ) ;
// I d o d a j do o p i s u w y s w i e t l a n i a krawedz j a k o n a l e z a c a do // drzewa p r z e s z u k i w a n i a
d i s p l a y E d g e ( v . name , ( ∗ i t )−>name , t r u e) ; } e l s e i f( v . name <= ( ∗ i t )−>name ) {
// J e z e l i od wi e dz on y i n i e j e s t p o p r z e d n i k i e m wedlug // p o r z a d k u w i e r z c h o l k o w ( z a p o b i e g a n i e r y s o w a n i u k r a w e d z i // dwa r a z y ) − d o d a j do o p i s u w y s w i e t l a n i a krawedz j a k o // n i e n a l e z a c a do drzewa p r z e s z u k i w a n i a
d i s p l a y E d g e ( v . name , ( ∗ i t )−>name , f a l s e) ; }
} }
// J e s l i p o s t o r d e r , t o n a d a j numerek w i e r z c h o l k o w i t u t a j
i f(vm == POSTORDER) v i s i t V e r t e x ( v . name , c o u n t e r , p a r e n t==NULL ) ; }
// O d w i e d z a n i e w i e r z c h o l k a
v o i d v i s i t V e r t e x ( s t d : : s t r i n g c o n s t &name , i n t &c o u n t e r , b o o l c o n s t &i s R o o t=f a l s e) {
c o u n t e r ++; // p o d b i j numer o 1 ( z a c z y n a s i e od 0 ) // Dodaj do w y s w i e t l a n i a w i e r z c h o l e k z b i e z a c y m numerem d i s p l a y V e r t e x ( name , c o u n t e r , i s R o o t ) ;
}
// W y s w i e t l a n i e w i e r z c h o l k a ( j e z y k d o t )
v o i d d i s p l a y V e r t e x ( s t d : : s t r i n g c o n s t &name , i n t &c o u n t e r , b o o l c o n s t &i s R o o t=f a l s e) {
i f( i s R o o t ) { // k o r z e n b e d z i e w podwojnym obramowaniu
s t d : : c o u t << name << ” [ l a b e l =\”” << name << ” : ” << c o u n t e r
<< ” \ ” , p e r i p h e r i e s = 2 ] ; \ n”; } e l s e {
s t d : : c o u t << name << ” [ l a b e l =\”” << name << ” : ” << c o u n t e r
<< ” \ ” ] ; \ n”; }
}
// W y s w i e t l a n i e k r a w e d z i ( j e z y k d o t )
v o i d d i s p l a y E d g e ( s t d : : s t r i n g c o n s t &uName , s t d : : s t r i n g c o n s t &vName , b o o l c o n s t &i n T r e e ) {
s t d : : c o u t << uName << ” −− ” << vName ;
i f(n o t i n T r e e ) { // krawedz s p o z a drzewa b e d z i e l i n i a p r z e r y w a n a s t d : : c o u t << ” [ s t y l e =\” d a s h e d \ ” ] ”;
}
s t d : : c o u t << ” ; \ n”; }
} ;
// P r o c e d u r a glowna i n t main ( ) {
Graph G;
i n t v , e ;
// Wczytaj l i c z b e w i e r z c h o l k o w i k r a w e d z i s t d : : c i n >> v >> e ;
// Wczytaj nazwy w i e r z c h o l k o w i d o d a j w i e r z c h o l k i do g r a f u f o r(i n t i = 0 ; i < v ; i ++) {
s t d : : s t r i n g name ; s t d : : c i n >> name ; G. a d d V e r t e x ( name ) ; }
// O d c z y t a j i n f o r m a c j e o k r a w e d z i a c h i d o d a j k r a w e d z i e do g r a f u f o r(i n t i = 0 ; i < e ; i ++) {
s t d : : s t r i n g uName , vName ; s t d : : c i n >> uName >> vName ; G. addEdge ( uName , vName ) ; }
// Wczytaj r o d z a j p r z e s z u k i w a n i a i nazwe k o r z e n i a s t d : : s t r i n g rootName , orderName ;
s t d : : c i n >> orderName >> rootName ;
// A s e r c j a − wskazany p o r z a d e k p r z e s z u k i w a n i a j e s t p r a w i d l o w y a s s e r t ( orderName == ”PREORDER” o r orderName == ”POSTORDER”) ;
// Wypisuj o p i s g r a f u w j e z y k u dot , p r z e s z u k u j a c go w t r a k c i e i n a d a j a c // numery p o s t o r d e r l u b p r e o r d e r
s t d : : c o u t << ” g ra ph G {\ n”; i f( orderName == ”PREORDER”) {
G. d f s ( Graph : : PREORDER, rootName ) ;
} e l s e i f( orderName == ”POSTORDER”) {
G. d f s ( Graph : : POSTORDER, rootName ) ; }
s t d : : c o u t << ” } ; \ n”; r e t u r n 0 ;
}