CO trzeba umieć:
//////////////////////////////////////////////////////////////////
Graf
reprezentujemy za pomocą macierzy przejść, tzn. takiej tabelki, w
której pokazany jest wierzchołek, a potem wierzchołki gdzie możemy
przejść.
np.:
0
1,3,4
1
0,4
2
1,3
3
0,2,4
4
3
Wierzchołki
będziemy numerować od 0, ponieważ informatycy liczą od zera :P.
Nazwy wierzchołków mogą być dowolne nawet literami alfabetu, ale
jednak używanie liczb naturalnych jest wygodniejsze. A i oczywiście
zakładam, że wiesz co oznaczają te strzałeczki i kreski na grafie
(np. z Teorii Grafów i Sieci).
Jak
widzimy możemy to zinterpretować za pomocą list. Więc mamy jedną
listę, w której trzymamy wierzchołki od 0 do 4. Następnie mamy
drugą listę w której trzymamy gałęzie. A teraz żebyś sobie to
jakoś wyobraziła. Mamy funkcję hash-ująca „wierzchołek” i
temu wierzchołkowi przypisuje element, ale ten element jest
elementem listy z gałęziami. Element lista z gałęziami (tak
naprawdę z łukami) zawiera adres wierzchołka do którego wchodzi.
Dodawanie
połączenia między wierzchołkami (dodajemy łuk, czyli np. od 0 do
4). W praktyce oznacza to do listy gałęzi wierzchołka 0 dodajemy
wierzchołek 4. Jeśli nasz graf ma krawędź z 0 do 4 to musimy
dodać dodatkowo połączenie od 4 do 0.
Tyle
jeśli chodzi o samą ideę grafów. Reszta to już działania na
listach, a że jak już dotąd dotarłeś to listy juś umiesz, więc
już nie będę się w nie zagłębiał tylko od razu polecimy z
kodami. :)
STRUKTURY:
struct
graf{ // struktura do listy z wierzchołkami
int
top; // nazwa wierzchołka
struct
graf *next,*prev; // wskazania listy dwukierunkowej
struct
branch *branch;
//
wskazanie na listę z gałęziami,
// dla każdego wierzchołka inna głowa (inna lista)
// dla każdego wierzchołka inna głowa (inna lista)
};
struct
branch{ // struktura listy gałęzi(łuków)
int
top; // nazwa wierzchołka
struct
branch *next, *prev; // wskazania listy dwukierunkowej
struct
graf *adress; //adres wierzchołka w liście wierzchołków
};
////////////////////////////////////////////////////////////////////////////////
///............................GRAF..........................................///
////////////////////////////////////////////////////////////////////////////////
void
add_top(struct graf **root, int top){
//
funkcja dodająca elementy do listy w określonej kolejności
//
omawiałem ją przy listach, nie wiele się zmieniła
struct
graf *new_top=(struct graf *)malloc(sizeof(struct
graf));
struct
graf *w=*root;
new_top->next=new_top->prev=NULL;
new_top->branch=NULL;
new_top->top=top;
if(!(*root))
*root=new_top;
else{
while(w->next
&& w->top != top) w=w->next;
if(!w->next
&& w->top != top){
// warunek, aby nie dodwało tego samego wierzchołka drugi raz
// warunek, aby nie dodwało tego samego wierzchołka drugi raz
w->next=new_top;
new_top->prev=w;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void
add_branch(struct branch **head, struct graf *top){
//
funkcja podobna do tej wyżej, różnicą jest to,
// że dodajemy wierzchołek grafu
// że dodajemy wierzchołek grafu
struct
branch *new_branch=(struct branch *)malloc(sizeof(struct
branch));
struct
branch *w=*head,*y;
int
f=1; // flaga
new_branch->next=new_branch->prev=NULL;
new_branch->adress=top;
new_branch->top=top->top;
if(!(*head))
*head=new_branch;
else{
while(w
&& w->top < top->top){
y=w;
w=w->next;
}
if(w)if(w->top
== top->top) f=0;
// warunek, aby nie dodawało tego samego połączenia jeszcze raz
// warunek, aby nie dodawało tego samego połączenia jeszcze raz
if(f==1){
if(w==*head){
new_branch->next=*head;
(*head)->prev=new_branch;
*head=new_branch;
}else{
y->next=new_branch;
new_branch->prev=y;
new_branch->next=w;
if(w)
w->prev=new_branch;
}
}
}
}
Samo
dodawanie elementów do grafu jest bardzo proste pod warunkiem, że
umie się listy i to umie się całkiem dobrze te listy. W zasadzie
nie ma specjalnie czego omawiać, bo już to zrobiłem, gdy omawiałem
listy, więc zawsze można tam ponownie zajrzeć.
////////////////////////////////////////////////////////////////////////////////
struct
graf *find(struct graf *root, int top){
//
funkcja szuka wierzchołka w liście z wierzchołkami i zwraca jego
adres
while(root
&& root->top != top) root=root->next;
return
root;
}
////////////////////////////////////////////////////////////////////////////////
///.....................................WYPISYWANIE..........................///
void
write_branch(struct branch *head){
//
wypisuje powiązania danego wierzchołka
while(head){
printf("%d
", head->top);
head=head->next;
}
}
////////////////////////////////////////////////////////////////////////////////
void
write_graf(struct graf *root){
//
wypisuje wierzchołki
while(root){
printf("%d
: ", root->top); // wierzchołek
write_branch(root->branch);
// wywołanie funkcji dla gałęzi
printf("\n");
root=root->next;
}
}
Tutaj
też w zasadzie wszystko powinno być jasne, bo nic nowego tutaj nie
wymyśliłem tylko zastosowałem to co już umiesz, prawda? :)
////////////////////////////////////REMOVE//////////////////////////////////////
///................................USUWANIE..................................///
////////////////////////////////////////////////////////////////////////////////
void
remove_branch(struct branch **head, int top){
//
funkcja usuwa elementy z listy z gałęziami
struct
branch *w=*head,*y;
while(w
&& w->top!=top){
y=w;
w=w->next;
}
if(w){
if(w==*head){
*head=(*head)->next;
free(w);
w=NULL;
}else{
y->next=w->next;
if(w->next)
w->next->prev=y;
free(w);
w=NULL;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void
remove_graf(struct graf **head, int top){
//
funkcja usuwa elementy z listy wierzchołków
struct
graf *w=*head,*y;
while(w
&& w->top!=top){
y=w;
w=w->next;
}
if(w){
if(w==*head){
*head=(*head)->next;
free(w);
w=NULL;
}else{
y->next=w->next;
if(w->next)
w->next->prev=y;
free(w);
w=NULL;
}
}
}
////////////////////////////////////////////////////////////////////////////////
void
remove_top(struct graf **root, int top){
//
funkcja główna naszego usuwania
struct
graf *el=find(*root,top); // el-nasz wierzchołek
remove_branch(&(el->branch),top);
// gdy są pętle w wierzchołku
while(el->branch){
remove_branch(&(el->branch->adress->branch),top);
//
kasuje element w liście wierzchołka przyległego
remove_branch(&(el->branch),el->branch->top);
//
kasuje element z listy wierzchołków przyległych
//
gdy skończą się elementy w tej liście pętla robi STOP
}
remove_graf(root,top);
// kasuje sam wierzchołek
}
Z
kasowaniem też wielkiej filozofii nie ma. Dwie funkcje praktycznie
identyczne. Obie mają za zadanie znaleźć i skasować element. Te
funkcje również omawiałem już przy listach. Natomiast ostatni
funkcja jest, powiedzmy, „nowa” i na niej możemy się chwilkę
zatrzymać. Funkcja remove_top
jest funkcją
główną naszego usuwania, więc przyjmuje graf i nazwę(numer)
wierzchołka do skasowania. Zmienne el
jest
wskazanie na nasz wierzchołek w liście wierzchołków. Pierwsze
wywołanie funkcji remove_branch
jest po to aby skasować ewentualne pętle z listy przyległości.
Następnie jest pętla, która wykonuje się jeśli w liście
przyległości naszego wierzchołka są jeszcze elementy. Więc
wchodzimy do listy przyległości przyległego wierzchołka do
naszego kasowanego i usuwamy połączenie z naszym wierzchołkiem
(kasujemy z tej listy nasz wierzchołek). Radzę to zdanie przeczytać
tak parę razy, aż się go dobrze zrozumie, bo może być zawiłe.
Następnie kasujemy przyległy wierzchołek z listy przyległości
naszego wierzchołka do skasowania. Czynności te powtarzamy, aż
skończą się wierzchołki w liście przyległości naszego
wierzchołka. Następnie możemy bez obaw usunąć nasz wierzchołek
z listy.
Wywoływanie
tych funkcji może wyglądać tak:
for(i=0;i<t;i++)
add_top(&root,i);
// dodajemy wierzchołki do listy wierzchołków
// dodajemy wierzchołki do listy wierzchołków
for(i=0;i<b;i++){
scanf("%d
%d",&x,&y); // odczyt połączenie
add_branch(&find(root,x)->branch,find(root,y));
// dodawanie ŁUK (strzałki) z x do y
add_branch(&find(root,y)->branch,find(root,x));
// dodawanie ŁUK (strzałki) z y do x
}
funkcja
add_branch dodaje
połączenie ale tylko w jedną stronę, czyli mamy połączenie z x
do y, jeśli zakładamy że chcemy mieć graf nieskierowany (taki
który nie ma łuków)
musimy wywołać naszą funkcję jeszcze raz i dodać połączenie z
y do x. Jeśli natomiast nasz graf
może posiadać krawędzie, łuki i pętle
to po prostu dodajemy
połączenia pamiętając, że krawędź to łuk z x do y i z y do x.

Brak komentarzy:
Prześlij komentarz