5. графи определение и основни понятия



страница1/9
Дата23.10.2018
Размер0.63 Mb.
#93968
  1   2   3   4   5   6   7   8   9

5.ГРАФИ

5.1.Определение и основни понятия


Графът е структура от данни, която се използва за решаването на редица интересни задачи от практиката. През последните десетилетия графите са обект на особен интерес както от страна на математиците, така и от страна на информатиците. Теорията на графите се обособи като обширен клон от дискретната математика с много интересни теореми и полезни приложения. Тук нямa да се занимаваме с теорията на графите, а ще обърнем внимание на създаването на програми на С++, използващи графи.

Г
раф
(фиг.27) наричаме множество от върхове (възли) и ребра. Всяко ребро свързва двойка върхове.

а
) Неориентиран безтегловен граф б) Неориентиран тегловен граф

в) Ориентиран безтегловен граф г) Ориентиран тегловен граф

Фиг. 27. Графи

Върховете ще означаваме с числа, които можем да разглеждаме като номера на върховете. Всяко ребро ще означаваме с двойка числа, например 3, 4, които са номерата на върховете, свързвани от това ребро. Ребрата показват връзката между върховете, т.е. възмoжността да се премине от един връх в друг връх. Например двойката числа 3, 4 представя реброто, свързващо върховете 3 и 4 и допускащо преминаване от връх 3 във връх 4. Всяко ребро може да бъде ориентирано или неориентирано.

Ориентираното ребро е със стрелка, която указва посоката на възможното придвижване по него, а двойката, която го представя е ориентирана, например двойката 3, 4 показва, че е възможно придвижване от връх 3 във връх 4, а двойката 4, 3, че е възможно придвижване от връх 4 във връх 3.

Неориентираното ребро няма стрелка, двойката числа, която го представя не е ориентирана, и разрешава придвижване в двете посоки.

Графът е неориентиран, когато всичките му ребра са неориентирани, и обратно, графът е ориентиран, когато всичките му ребра са ориентирани. Всяко неориентирано ребро може да се представи чрез две ориентирани ребра (фиг.28).

Т
ова позволява да превърнем в граф, който има неориентирани ребра, в ориентиран граф.
a) Граф с две неориентирани ребра б) Ориентиран граф

Фиг. 28. Превръщане на граф, съдържащ неориентирани ребра, в ориентиран граф

Даден граф може да бъде тегловен или безтегловен. Графът е тегловен граф, когато на върховете или/и ребрата са съпоставени тегла. Тук ще използваме тегловни графи с тегла, съпоставени на ребрата.

Същността на решаваната задача определя какъв граф да се използва, ориентиран или неориентиран, тегловен или безтегловен.



Бримка се нарича ребро, което има един и същи връх за начален и краен, например реброто 3, 3 е бримка.

Паралелни ребра са ребрата, свързващи една и съща двойка върхове.

Граф, който не съдържа бримки, паралелни ребра и ориентирани ребра, се нарича обикновен граф или просто граф.



Съседни върхове на даден връх в неориентиран граф са всички върхове, които са свързани с него чрез ребра.

Степен на връх на неориентиран граф ще наричаме броя на съседните му върхове. Връх без съседи е изолиран връх, а връх само с един съсед е висящ връх.

В ориентиран граф всеки връх има входящи и изходящи ребра. Входящи за върха са ребрата, по които от съседни върхове може да се стигне до него. Изходящи за върха са ребрата, по които от него може да се стигне до съседни върхове.

В ориентиран граф всеки връх има две степени: входна степен – брой на входящите ребра и изходна степен – брой на изходящите ребра.

Път в граф ще наричаме последователност от свързани (съседни) върхове. Пътят е прост, когато в него не се повтарят върхове и ребра. Пътят е цикъл, когато първият и последният връх от него съвпадат.

Цикълът е прост, когато не съдържа вложени цикли. Цикълът е хамилтонов, когато през всеки възел на графа се преминава еднократно. Цикълът е ойлеров, когато по всяко ребро на графа се преминава еднократно.



Пълен граф е този, в който всеки връх е свързан с всички останали.

Празен граф е този, в който няма нито едно ребро.

Допълнителен граф на даден граф е графът, който има същите върховете и само онези ребра, които не достигат на дадения, за да е пълен граф.

Еднороден граф е граф от върхове с еднаква степен. Пълният граф е винаги еднороден.

Равнинен е графът, който може да се изобрази в равнината без пресичане на ребра.

Двудялов е графът, чийто върхове са разделени на две подмножества и няма ребра между върхове, принадлежащи на различни множества.

Графите се използват за представяне на реални обекти, системи от обекти, процеси, явления и др. Например пътната мрежа в даден район може да се разглежда като тегловен граф, в който върхове са населените места и важните кръстовища, а ребра са пътищата между тях. С граф може да се представи и реализацията на някакъв процес, като всеки връх представя състоянието, до което се достига след реализиране на определен етап, а ребро до този връх – това, което трябва да се направи, за да се реализира този етап.


5.2.Представяне на графи

5.2.1.Външно представяне


Става дума за представяне на графа на външен носител, например текстов файл. Един удобен начин е на първия ред във файла да се запише броят на върховете, а на всеки следващ ред – по едно ребро. Реброто се представя с две числа, когато графът е безтегловен, и с три числа, когато графът е тегловен. Третото число представя теглото, предписано на това ребро. Когато реброто е ориентирано, първото число е върхът, от който може да се тръгне по това ребро, а второто – върхът, в който може да се стигне по това ребро. Когато реброто не е ориентирано, редът на числата е без значение, но обикновено като първо се записва по-малкото число.

8

0 2


0 7

1 5


1 6

2 4


2 7

3 4


3 5

4 5


5 6

6 7
Външно представяне на графа от фиг. 27.а)


8

0 2 8


0 7 10

1 5 12


1 6 15

2 4 12


2 7 13

3 4 20


3 5 10

4 5 32


5 6 10

6 7 20
Външно представяне на графа от фиг. 27.б)


8

0 2


1 5

3 4


4 2

5 3


5 4

6 1


6 5

6 7


7 0

7 2
Външно представяне на графа от фиг. 27.в)


8

0 2 8


1 5 12

3 4 20


4 2 12

5 3 10


5 4 32

6 1 15


6 5 10

6 7 20


7 0 10

7 2 13
Външно представяне

на графа от фиг. 27г)


Фиг. 29. Външно представяне на граф

5.2.2.Вътрешно представяне


Става дума за представяне на графа в ОП по време на изпълнение на програма, ползваща структурата граф. То се оценява по два критерия:

  • необходима памет;

  • сложност на реализация на операциите: определяне дали два върха са съседни и намиране на всички възли, съседни на даден възел.

5.2.2.1.Вътрешно представяне чрез матрица на съседство.


Матрицата на съседство, представяща граф, е квадратна матрица с размер, равен на броя на върховете. Номерата на редовете, както и номерата на стълбовете, са номера на върхове. Матриците на съседство на графите от фиг. 27 са следните:

0 1 2 3 4 5 6 7

0 0 0 1 0 0 0 0 1

1 0 0 0 0 0 1 1 0

2 1 0 0 0 1 0 0 1

3 0 0 0 0 1 1 0 0

4 0 0 1 1 0 1 0 0

5 0 1 0 1 1 0 1 0

6 0 1 0 0 0 1 0 1

7 1 0 1 0 0 0 1 0

Матрица на съседство

за графа от фиг.1а)

0 1 2 3 4 5 6 7

0 0 0 8 0 0 0 0 10

1 0 0 0 0 0 12 15 0

2 8 0 0 0 12 0 0 13

3 0 0 0 0 20 10 0 0

4 0 0 12 20 0 32 0 0

5 0 12 0 10 32 0 10 0

6 0 15 0 0 0 10 0 20

7 10 0 13 0 0 0 20 0

Матрица на съседство

за графа от фиг.1. б)


0 1 2 3 4 5 6 7

0 0 0 1 0 0 0 0 0

1 0 0 0 0 0 1 0 0

2 0 0 0 0 0 0 0 0

3 0 0 0 0 1 0 0 0

4 0 0 1 0 0 0 0 0

5 0 0 0 1 1 0 0 0

6 0 1 0 0 0 1 0 1

7 1 0 1 0 0 0 0 0

Матрица на съседство

за графа от фиг.1.в)


0 1 2 3 4 5 6 7

0 0 0 8 0 0 0 0 0

1 0 0 0 0 0 12 0 0

2 0 0 0 0 0 0 0 0

3 0 0 0 0 20 0 0 0

4 0 0 12 0 0 0 0 0

5 0 0 0 10 32 0 0 0

6 0 15 0 0 0 10 0 20

7 10 0 13 0 0 0 0 0

Матрица на съседство

за графа от фиг.1. г)


Фиг. 30. Вътрешно представяне на граф чрез матрица на съседство

Елементите на матрицата показват дали съществуват ребра между отделните върхове или не съществуват. Например елементът, който се намира на i-ия ред и на j-ия стълб, има стойност 1 или 0 в зависимост от това, дали съществува реброто i,j или не. При тегловен граф стойността на елемент, представящ дадено ребро, е самото тегло, а не 1. Матрицата на съседство, представяща неориентиран граф, е симетрична, защото, ако съществува реброто i,j, съществува и реброто j,i . Елементите от главния диагонал са нулеви, ако графът не съдържа бримки.

Матрицата на съседство предлага определена прегледност. В i-ия ред, ненулеви са всички елементи, които се намират в стълбове, съответстващи на върхове, с които i-ият връх е свързан с изходящи ребра. В j-ия стълб, ненулеви са всички елементи, с които j-ият връх е свързан с входящи ребра Или броят на ненулевите елементи в i-ия ред дава изходящата степен на i-ия връх, а броят на ненулевите елементи в j-ия стълб дава входящата степен на j-ия връх.

Матрицата на съседство е много подходящ начин за представяне на граф от гледна точка на втория крирерий. От гледна точка на първия критерий (необходима памет), обаче, тя не е особено подходяща, защото граф с n върха изисква матрица с n2 елемента, при това често преобладаващата част от тях са с нулеви стойности.



Програма 5.1. Програма за създаване и визуализация на матрицата на съседство на граф, представен външно в текстов файл

#include

#include

#include

#include

unsigned BrV; //Брой върхове в графа

ifstream fin; //Глобален входен поток за четене от текстовия файл

ofstream fout; //Глобален изходен поток за четене от текстов файл

//Клас граф

class clasGraph{

int **A; //Указател за създаване на матрицата на съседство

public:


clasGraph(); //Конструктор

~clasGraph(); //Деструктор

void make_graf(); //Запълва матрицата на съседство

void display_graf(ostream &);//Визуализира матрицата на съседство

};

//Конструктор за създаване и инициализация на матрицата на съседство



clasGraph::clasGraph()

{

int i,j;



if (!(A=new int*[BrV])) exit(1);

for (i=0; i

for (i=0; i

for (j=0; j

}

//Деструктор, който изтрива матрицата на съседство



clasGraph::~clasGraph()

{

for (int i=0; i

delete []A; cout<<"\nМатрицата на съседство е изтрита.\n";

}

//Член-функция за запълване на матрицата на съседство



void clasGraph::make_graf()

{

char kod_or,kod_teg; int i,j,teg;



cout<<"Графът ориентиран ли е /D,d,Д,д/? "; cin>>kod_or;

cout<<"Графът тегловен ли е /D,d,Д,д/? "; cin>>kod_teg;

for (;;) {

fin>>i>>j;if (fin.fail()) break;

if (kod_teg=='D'||kod_teg=='d'||kod_teg=='Д'||kod_teg=='д') fin>>teg;

else teg=1;

A[i][j]=teg;

if (kod_or!='D'&&kod_or!='d'&&kod_or!='Д'&&kod_or!='д') A[j][i]=teg;

}

fin.close();



}

//Член-функция за извеждане на матрицата на съседство на екрана или в //текстов файл

void clasGraph::display_graf(ostream &p=cout)

{

if (p!=cout) fout.open("DopInf",ios::out);



int i,j;

p<<"\n MATРИЦА НА СЪСЕДСТВО\n";

p<<" ";

for (i=0;i

for (i=0;i

p<

for (j=0;j

p<


}

if (p!=cout){

fout.close();

cout<<"\nОтрорете текст. файл DopInf.\n";

}

}

//Главна функция



void main()

{

char ime_vh_fl[21];



cout<<"\nИме на входния файл: "; cin>>ime_vh_fl;

fin.open(ime_vh_fl,ios::in);

if (fin.bad()) {cout<<"Няма такъв файл.\n";getch();return;}

fin>>BrV; //Въвежда от текстовия файл броя на върховете на графа

clasGraph Graph; //Създава обекта Graph

Graph.make_graf(); //Запълва матрицата на съседство

Graph.display_graf(); //Извежда матрицата на съседство на екрана

Graph.display_graf(fout);//Извежда матрицата на съседство в текстов //файл

getch();

}
Главната функция прочита броя на върховете BrV от текстовия файл.

Конструкторът на класа създава матрицата на съседство като двумерен динамичен масив с BrV реда и BrV стълба и инициализира всички елементи със стойност 0.

Член-функцията make_graf(), след два уточняващи въпроса (дали графът е ориентиран и дали е тегловен), прочита данните от файла външно представяне на графа и заменя част от нулите в матрицата с 1 или с теглата на съответните ребра.

Член-функцията display_graf(ostream &p=cout) извежда матрицата:


  • на екрана, когато фиктивният аргумент р не e заместен с действителен аргумент;

  • в текстов файл с име “DopInf”, когато фиктивният аргумент р е заместен с изходния поток fout.

Деструкторът изтрива матрицата непосредствено преди излизане от програмата.

Самата програма не се нуждае от по-подробно описание, защото е проста и достатъчно наситена с пояснителни текстове.


5.2.2.2.Вътрешно представяне чрез масив от списъци на съседство


На следващата фигура се показани списъците на съседство, представящи графите от фиг.27.а) и фиг.27.г) по два начина, отляво – както вече сме го предстсвяли в в гл. 3 и отдясно – както го визуализира член-функцията void display_graf(ostream &) от следващата програма.

П
ървоначално се създава масив от указатели към списъци, които се инициализират като празни. След това започва добавянето на възли в списъците. Всеки възел представя едно ребро, например възелът 7 в 0-вия списък представя реброто 0,7, а възелът 2 от същия списък – реброто 0,2. Списъците не са подредени, поради което всеки нов възел може да се представи на произволно място в съответния списък. С цел да се постигне по-голямо бързодействие на програмата, всеки нов възел се разполага в челото на съответния списък. Всяко ребро се описва само с номера на върха, до който то води или с номера на върха и теглото на реброто, когато графът е тегловен. Например i-ия списък има толкова елемента, колкото са ребрата, излизащи от i-ия връх.


Масив от списъци на съседство за графа от фиг.27.а)
М
асив от списъци на съседство за графа от фиг.27 г)

Фиг. 31. Вътрешно представяне на граф чрез масив от списъци на съседство.

При компютърната визуализация, числото в първата колона е индекс на елемент от масива от списъци и същевременно номер на върха, чиито съседи представя този списък-елемент на масива. Знакът -> е указател към ребро. Числото след знака -> е номер на върха-съсед.

Например редът 0 -> 7 -> 2 ->

съдържа информацията: върхът 0 има за съседи върховете 7 и 2.

А редът 5 -> 4 32 -> 3 10 ->

съдържа информацията: върхът 5 има за съседи върховете 4 и 3, като теглото на 5, 4 е 32, а на 5, 3 е 10.

Този начин за вътрешно представяне на граф е по-добър от гледна точка на първия критерий. Всеки списък на съседство съдържа точно толкова елемента, колкото е броят на изходящите от върха ребра.

По отношение на втория критерий има малък проблем, по-трудно се намират входящите съседи на върховете. Това обаче не е чак толкова голям проблем, защото, ако в програмата често се ползват входящите съседи, може да се създаде и масив от списъци на входящите съседи.

Програма 5.2. Програма за създаване и визуализация на масив от списъци на съседство на граф, представен външно в текстов файл

#include

#include

#include

#include

unsigned BrV; //Брой на върховете в графа

//Структура ребро.

struct rebro{int nom, teg; rebro *next;};

ifstream fin; //Глобален входен поток за четене от текстовия файл

ofstream fout; //Глобален изходен поток за четене от текст. файл

//Структура връх от графа

struct vrah { rebro *first; };

//Клас граф

class clasGraph {

vrah *a;//Указател, необходим за съсдаване на масива от списъци на съседство

public:


clasGraph(); //Конструктор за създаване и инициализация на масива от списъци

~clasGraph();//Деструктор за изтриване на граф

void make_graf();

void display_graf(ostream &);

};

// Конструктор за създаване и инициализация на масива от списъци на съседство



clasGraph::clasGraph()

{

a=new vrah[BrV];



for (int i=0;i

}

//Деструктор за изтриване на масива от списъци на съседство



clasGraph::~clasGraph()

{

rebro* p;



for (int i=0;i

while (a[i].first){

p=a[i].first;

a[i].first=p->next;

delete p;

}

}



delete []a; cout<<"Масивът от списъци е изтрит."<

}

char kod_teg;



//Член-функция за създаване списъците на съседство-елементи на масива

void clasGraph::make_graf()

{

rebro *p; char kod_or; unsigned i,j,teglo;



cout<<"Графът ориентиран ли е /D,d,Д,д/? "; cin>>kod_or;

if (kod_or=='D'||kod_or=='d'||kod_or=='Д'||kod_or=='д') kod_or=1;

else kod_or=0;

cout<<"Графът тегловен ли е /D,d,Д,д/? "; cin>>kod_teg;

if (kod_teg=='D'||kod_teg=='d'||kod_teg=='Д'||kod_teg=='д') kod_teg=1;

else kod_teg=0;

for (;;) {

fin>>i>>j; if (fin.fail()) break;

if (kod_teg) fin>>teglo;

else teglo=1;

p=new rebro; p->nom=j; p->teg=teglo; p->next=a[i].first; a[i].first=p;

if (!kod_or) {

p=new rebro; p->nom=i; p->teg=teglo; p->next=a[j].first; a[j].first=p;

}

}



fin.close ();

}

//Член-функция за визуализaция на масива от списъци на съседство



void clasGraph::display_graf(ostream &pp=cout)

{

if (pp!=cout) fout.open("DopInf",ios::out);



int i; rebro *p;

pp<<"\nМАСИВ ОТ СПИСЪЦИ НА СЪСЕДСТВО:\n";

for (i=0;i

pp< ";

p=a[i].first;

while (p!=0){

pp<nom; if (kod_teg) pp<teg; pp<<" -> ";

p=p->next;

} pp<

}

if (pp!=cout){



fout.close();

cout<<"\nОтрорете текст. файл DopInf.\n";

}

}

//Главна функция



void main()

{

char ime_vh_fl[21];



cout<<"\nИме на входния файл: "; cin>>ime_vh_fl;

fin.open(ime_vh_fl,ios::in);

if (fin.bad()) {cout<<"Няма такъв файл.\n";getch();return;}

fin>>BrV; //Въвеждаме от текстовия файл броя на върховете на графа

clasGraph Graph; //Създаваме обекта Graph

Graph.make_graf(); //Създаваме списъците на съседство

Graph.display_graf(fout); //Извеждаме на екрана масива от списъци на съседство

getch();


}

Главната програма прочита броя на върховете BrV от текстовия файл.

Конструкторът на класа създава динамичен масив от указатели с BrV елемента и инициализира всички указатели със стойност 0.

Член-функцията make_graf(), след два уточняващи въпроса дали графът е ориентиран и дали е тегловен, прочита данните от файла за външно представяне на графа и добавя към списъците на съседство елементи, съответстващи на изходящите съседни ребра.

Член-функцията display_graf(ostream &pp=cout) извежда масива от списъци:


  • на екрана, когато фиктивният аргумент р не e заместен с действителен аргумент;

  • в текстов файл с име “DopInf”, когато фиктивният аргумент р е заместен с изходния поток fout.

Деструкторът изтрива масива от списъци на съседство непосредствено преди излизане от програмата.

Самата програма не се нуждае от по-подробно описание, защото е проста и достатъчно наситена с пояснения.




Сподели с приятели:
  1   2   3   4   5   6   7   8   9




©obuch.info 2024
отнасят до администрацията

    Начална страница