Tere kÔigile.
Selles mĂ€rkmes otsustasin loetleda peamised andmestruktuurid, mida kasutatakse graafide hoidmiseks infotehnoloogias, ja rÀÀkida ka paarist sellisest struktuurist, mis mul ĂŒhel hetkel "kristalliseerusid".
Nii et alustame. Kuid mitte algusest - ma arvan, et me kÔik teame, mis on graaf ja millised need vÔivad olla (suunatud, suunamata, kaalutud, kaalutud, mitme servaga ja silmadega vÔi ilma),
Nii et edasi. Milliseid andmestruktuuride valikuid âgraafide sĂ€ilitamiseksâ meil on.
1. Matriitsistruktuurid
1.1 Siduva matriits. Siduva matriits on matriit, kus ridade ja veergude pealkirjad vastavad graafi sĂ”lmede numbritele ning iga elemendi vÀÀrtus a(i,j) mÀÀratakse sĂ”lmede i ja j vahelise serva olemasolu vĂ”i puudumise jĂ€rgi (selgelt, et suunamata graafi selline matriit on sĂŒmmeetriline, vĂ”i saame kokku leppida, et kĂ”ik vÀÀrtused salvestame ainult peadiagonaali kohal). Kaalumata graafide puhul saab a(i,j) mÀÀrata servade arvu i-st j-ni (kui sellist servi pole, siis a(i,j)= 0), kaalutud graafide puhul samuti - serva massiga (kokkuvĂ”tlik kaal).
1.2 Incidendi matriits. Sellisel juhul salvestatakse meie graaf samuti tabelisse, kus reede numbrid vastavad tavaliselt tema sĂ”lmedele ja veeru numbrid eelnevalt nummerdatud servadele. Kui sĂ”lm ja serv on omavahel seotud, siis vastavasse lahtrisse kirjutatakse mitte null vÀÀrtus (suunamata graafide puhul kirjutatakse 1, kui sĂ”lm ja serv on seotud, suunatud graafide puhul - "1", kui serv "vĂ€ljub" sĂ”lmest ja "-1", kui see "siseneb" (kergesti meeldejÀÀv, kuna mĂ€rk âmiinusâ siseneb ka numbrite "-1" hulka)). Kaalutud graafide puhul saab taas 1 ja -1 asemel mĂ€rkida serva kogumassi.
2. Loendid andmestruktuurid
2.1 KĂŒlgne loend. Siin on kĂ”ik ĂŒsna lihtne. Iga graafi tippu saab ĂŒldiselt vastandada mis tahes loetelustruktuur (loend, vektor, massiiv, ...), kus hoitakse iga tippu, mis on antud tippu naabrid. Suunatud graafide puhul lisame sellesse loendisse vaid need tipud, kuhu on "suunatud" serv tipust-nĂ€itajast. Kaalutud graafide puhul on rakendamine keerulisem.
2.2 Servade loend. Ăsna populaarne andmestruktuur. Servade loend, nagu meile ĂŒtleb Kapten Ilmselgus, esindab tegelikult graafi servade loendit, millest igaĂŒks on mÀÀratud algtippu, lĂ”pptippu (suunamata graafide puhul pole siin jĂ€rjekorra olemasolu oluline, kuigi ĂŒhtsuse huvides vĂ”ib kasutada erinevaid reegleid, nĂ€iteks mÀÀrata tipud kasvavas jĂ€rjekorras) ja kaalu (ainult kaalutud graafide puhul).
Kasutatud loendi-matriitside kohta vÔib lÀhemalt (ja illustratsioonidega) lugeda, nÀiteks .
2.3 Naabruse massiiv. Ei ole kĂ”ige sagedamini esinev struktuur. Sisult esindab see loendusehnvuse massiivi «pakendamist» ĂŒhte loetelustruktuuri (massiiv, vektor). Esimesed n (graafi tippude arvu jĂ€rgi) elementi sellises massiivis sisaldavad antud massiivi algindeksid, alates millest on selles jĂ€rjestikku kirjas kĂ”ik naabrina olevad tipud.
Siit leidsin kÔige arusaadavam (enda jaoks) selgitus:
3. Naabruse vektor ja Assotsiatiivne naabruse massiiv
Nii juhtus, et nende ridade autor, olles professionaalne programmijatega, kuid erilisel juhul olnud graafidega, on tihti pidanud tegelema servade loenditega. TÔepoolest, on mugav, kui graafis on mitu silmust ja serva. Ja nii, arendades klassikalisi servade loendeid, pakun vÀlja tÀhelepanu pöörata nende "arendamisele / harutamisele / modifitseerimisele / mutatsioonile", nimelt: naabruse vektorile ja assotsiatiivsele naabruse massiivile.
3.1 Naabruse vektor
Juhtum (a1): kaalutum graaf
Nimelt nimetame kaalumatuks graafiks naabruse vektoriks paarisarvuliste tĂ€isarvude jĂ€rjestatud kogumit (a[2i], a[2i+1],âŠ, kus i nummerdatakse 0-st), kus iga numbri paar a[2i], a[2i+1] mÀÀrab graafi serva tipude vahel a[2i] ja a[2i+1] vastavalt.
See record format does not contain information on whether the graph is directed (both options are possible). When using the format for an org graph, it is assumed that the edge is directed from a[2i] to a[2i+1]. Hereinafter: for undirected graphs, if necessary, the requirements for the order of writing vertices can be applied (for example, to have the vertex with the smaller assigned number go first).
In C++, it is advisable to define the adjacency vector using std::vector, hence the name of this data structure.
Case (a2): unweighted graph, edge weights are integers
By analogy with case (a1), we will call the adjacency vector for a weighted graph with integer edge weights an ordered set (dynamic array) of numbers (a[3i], a[3i+1], a[3i+2],âŠ, where i is numbered from 0), where each 'triplet' of numbers a[3i], a[3i+1], a[3i+2] defines an edge of the graph between the vertices numbered a[3i] and a[3i+1] respectively, and the value a[3i+2] is the weight of this edge. Such a graph can also be directed or undirected.
Case (b): unweighted graph, edge weights are non-integer
Given that a single array (vector) cannot store heterogeneous elements, for example, the following implementation is possible. The graph is stored in a pair of vectors, where the first vector is the adjacency vector of the graph without specifying weights, and the second vector contains the corresponding weights (possible implementation for C++: std::pair). Thus, for an edge defined by a pair of vertices at indices 2i, 2i+1 of the first vector, the weight will be equal to the element at index i of the second vector.
But why is this necessary?
Well, to the author of these lines, it seemed quite useful for solving a number of problems. From a formal point of view, there will be such advantages:
- The adjacency vector, like any other 'enumerative' structure, is quite compact, occupies less memory than the adjacency matrix (for sparse graphs), and is relatively easy to implement.
- The vertices of the graph can, in principle, be labeled with negative numbers. After all, such 'twisting' may be needed.
- Graphs can contain multiple edges and multiple loops, with different weights (positive, negative, or even zero). There are no restrictions here.
- Samuti saab harudele mÀÀrata erinevaid omadusi â kuid selle kohta vt p. 4.
Siiski tuleb tunnistada, et see ânimekiriâ ei tĂ€henda kiiret juurdepÀÀsu harule. Ja siin tĂ”ttab meile appi Assotsiatiivne kĂŒlgne massiiv, millest rÀÀgime allpool.
3.2 Assotsiatiivne kĂŒlgne massiiv
Seega, kui juurdepÀÀs konkreetsele harule, selle kaalule ja muudele omadustele on meie jaoks kriitilise tĂ€htsusega, kuid mĂ€lunĂ”uded ei luba kasutada kĂŒlgnevad maatriksit, siis mĂ”elgem, kuidas saaksime kĂŒlgnuektorist muuta, et seda ĂŒlesannet lahendada. Seega on vĂ”tmekĂŒsimuseks graafi haru, mille saab esitada jĂ€rjestatud paarina tĂ€isarve. Mida see meenutab? Kas see pole nagu vĂ”tme assotsiatiivses massiivis? Ja kui nii, siis miks mitte seda ellu viia? Olgu meil selline assotsiatiivne massiiv, kus igale vĂ”tmele â jĂ€rjestatud paar tĂ€isarve â vastab vÀÀrtus â tĂ€isarv vĂ”i reaalnumber, mis mÀÀrab haru kaalu. C++-s on mĂ”istlik seda struktuuri rakendada konteineri std::map (std::map <std::pair , int> vĂ”i std::map <std::pair , double>), vĂ”i std::multimap, kui eeldatakse mitmeid harusid. Noh, ja meil on struktuur graafide sĂ€ilitamiseks, mis kasutab vĂ€hem mĂ€lu kui âmaatrikstruktuuridâ, suudab mÀÀrata graafe mitmete silmade ja harudega ning ei oma isegi ranged nĂ”uded tippude mitte-negatiivsusele (ma ei tea, kellele see vajalik on, kuid siiski).
4. Andmestruktuurid on kui âvalmistadaâ, kuid midagi on puudu
Ja tĂ”epoolest: mitmete ĂŒlesannete lahendamisel vĂ”ib meil tekkida vajadus mÀÀrata graafi harudele mingid omadused ja seega need sĂ€ilitada. Kui need omadused saab ĂŒheselt tĂ€isarvudeks vĂ€hendada, siis on vĂ”imalik sĂ€ilitada selliseid âlisamĂ€rkidega graafikuidâ, kasutades laiendatud versioone kĂŒlgneva vektori ja assotsiatiivse massiivi pĂ”hjal.
Nii et, olgu meil kaalumata graaf, mille iga serva kohta on vajalik salvestada nĂ€iteks 2 tĂ€iendavat omadust, mis on mÀÀratud tĂ€isarvudega. Sellisel juhul on vĂ”imalik mÀÀrata selle kĂŒlgvektor jĂ€rjestatud kogumina mitte "paaridest", vaid "nelikutest" tĂ€isarve (a[2i], a[2i+1], a[2i+2], a[2i+3]...), kus a[2i+2] ja a[2i+3] mÀÀravad vastava servi omadused. Kaalutud servadega graafi puhul on jĂ€rjekord ĂŒldiselt sarnane (erinevus on vaid selles, et omadused jĂ€rgivad serva kaalu ja on mÀÀratud elementidega a[2i+3] ja a[2i+4], ning ise serv on mÀÀratud mitte 4, vaid 5 jĂ€rjestatud numbriga). Niisiis, graafi puhul, mille servade kaalud ei ole tĂ€isarvud, saab omadused talletada selle kaalumatutes komponentides.
Kasutades kĂŒlgseose assotsiatiivset massi, on vĂ”imalik kaalutud servadega graafide puhul mÀÀrata vÀÀrtusena mitte eraldi number, vaid massiiv (vektor) numbreid, mis mÀÀravad, peale serva kaalu, kĂ”ik tema muud vajalikud omadused. Selle juurde kĂ€iv ebamugavus, kui kaalud ei ole tĂ€isarvud, on vajadus mÀÀrata omadus ujuva koma numbriga (jah, see on ebameeldivus, kuid kui selliseid omadusi ei ole liiga palju ja kui mitte mÀÀrata neid liiga ânutikateâ double'itega, siis vĂ”ib-olla pole see ka halb). Nii et C++-is vĂ”ivad laiendatud assotsiatiivsed massi kĂŒlgseosed olla mÀÀratud jĂ€rgmised: std::map <std::pair , std::vector> vĂ”i std::map <std::pair , std::vector>, kusjuures esimene vÀÀrtus "vektor- vÀÀrtus-vĂ”tme jĂ€rgi" on serva kaal ja seejĂ€rel asetsevad tema omaduste numbrilised tĂ€hised.
KĂ€esolev kirjandus:
Graafide ja algoritmide kohta ĂŒldiselt:
1. Cormen, Thomas H., Leiserson, Charles E., Rivest, Ronald L., Stein, Clifford. Algoritmid: loomine ja analĂŒĂŒs, 2. vĂ€ljaanne: TĂ”lge inglise keelest â Moskva: Williamsi kirjastus, 2011.
2. Harari, Frank. Graafide teooria. Moskva: Mir, 1973.
Autori ettekande kohta nende vektori ja assotsiatiivse massi kĂŒlgseose kohta:
3. ЧДŃĐœĐŸŃŃ
ĐŸĐČ ĐĄ.Đ. KĂŒlleu vÀÀrtus ja seosekaart kui andmestruktuurid graafide esitamiseks ja salvestamiseks // S.A. Chernouhov. Seoste vektor ja seosekaart andmestruktuuridena graafi esitlemiseks // Rahvusvahelise teadus- ja praktikaseminaride kogumik teemal "Uuenduslike arenduste tulemuste rakendamise probleemid ja nende lahendamise teed" (Saratov, 14.09.2019). â Sterlitamak: AMI, 2019, lk 65-69
Kasulikud internetiallikad teemal:
4.
5.
Allikas: habr.com
