Tere kÔigile.
Selles mÀrkuses otsustasin loetleda peamised andmestruktuurid, mida kasutatakse graafide salvestamiseks infotehnoloogias, samuti rÀÀkida paarist muust struktuurist, mis on kuidagi iseendale "kristalliseerunud".
Nii et alustame. Aga mitte tĂ€iesti algusest â arvan, et teame kĂ”ik, mis on graaf ja milliseid tĂŒĂŒpe need on (suunatud, suunamata, kaalutud, kaalutud, mitme servaga ja silmusega vĂ”i ilma).
Nii et lÀhme edasi. Milliseid andmestruktuuri variante graafide salvestamiseks meil on?
1. Matrilised andmestruktuurid
1.1 Naabermatriits. Sidemaatriks on maatriks, kus ridade ja veergude pealkirjad vastavad graafi tippude numbritele ning iga elemendi vÀÀrtus a(i,j) mÀÀritakse tippude i ja j vaheliste servade olemasolu vĂ”i puudumise jĂ€rgi (on selge, et suunamata graafi puhul on selline maatriks sĂŒmmeetriline, vĂ”i siis vĂ”ime kokku leppida, et kĂ”ik vÀÀrtused hoiame ainult peamise diagonaali kohal). Kaaluva graafi puhul saab a(i,j) mÀÀrata servade arvu i-st j-ni (kui sellist serva ei ole, siis a(i,j)= 0), samuti ka nende servade kaaluga (kokku kaalud).
1.2 Kande maatriks. Sellisel juhul salvestatakse meie graaf ka tabelis, kus ridadele vastavad tavaliselt graafimeetodite numbrid ja veergudele eelnevalt nummerdatud servad. Kui tipp ja serv on ĂŒksteisega kĂŒlgnev, siis vastavas lahtris salvestatakse mitte-null vÀÀrtus (mitte suunatud graafide puhul kirjutatakse 1, kui tipp ja serv on kĂŒlgne, suunatud graafide puhul kirjutatakse â1â, kui serv âvĂ€ljubâ tipust ja â-1â, kui see âsisenebâ sellesse (seda on piisavalt lihtne meeles pidada, kuna mĂ€rk âmiinusâ âsisenebâ ka numbrisse â-1â)). Kaalutud graafide puhul vĂ”ib samuti 1 ja -1 asemel mĂ€rkida serva kogumassi.
2. Loetlevad andmestruktuurid
2.1 Naabrusloend. Siin nĂ€ib kĂ”ik olevat lihtne. Iga graafi tippu saab ĂŒleĂŒldiselt seostada mis tahes loetleva struktuuriga (loend, vektor, massiiv, âŠ), kus hoitakse kĂ”igi naabertippude numbreid. Suunatud graafide puhul kantakse sellesse loendisse vaid need tipud, kuhu on suunatud serv tipp-meetmest. Kaalutud graafide rakendamine on keerulisem.
2.2 Nurgareede nimekiri. Aeg-ajalt populaarne andmestruktuur. Nurgareede nimekiri, nagu ĂŒtleb Kapten Ilmselgus, on graafi nurgareede nimekiri, millest igaĂŒhel on alguspunkt, lĂ”pppunkt (suunamata graafide puhul ei ole siinne jĂ€rjestus oluline, kuigi ĂŒhtsuse nimel vĂ”ib kasutada erinevaid reegleid, nĂ€iteks mĂ€rkida nurgad kasvavas jĂ€rjekorras) ja kaal (ainult kaalutud graafide puhul).
Ălevaatamiseks nende nimetatud nimekirjade-matriiside kohta saab lĂ€hemalt (ja illustratsioonidega) vaadata nĂ€iteks .
2.3 Kaudsusmassiiv. Mitte kĂ”ige sagedamini esinev struktuur. Oma olemuselt esindab see kĂ”ikide nurgareede nimekirjade andmestruktuuri âpakendamiseâ vormi (massiiv, vektori). Selle massiivi esimesed n (graafi nurgade arvu jĂ€rgi) elementi sisaldavad sama massiivi algindekseid, alates millest on jĂ€rjestikku kirjas kĂ”ik nurgad, mis on antud nurgaga kĂŒlgnevad.
Siit leidsin kÔige arusaadavam (endale) selgituse:
3. Kaudsusvektor ja Assotsiatiivne kaudsusmassiiv
Nii on juhtunud, et nende ridade autor, olles mitte professionaalne programmeerija, on siiski aeg-ajalt kokku puutunud graafide ja enamasti tegeleb servade loenditega. TÔepoolest, on mugav, kui graafis on mitu silmust ja serva. SeetÔttu pakun klassikaliste servade loendite arendamisel tÀhelepanu pöörata ka nende "arendamise/harunemise/modifitseerimise/mutatsiooni" aspektile, nimelt: naabervektorile ja naabermaatriksile.
3.1 Naabervektor
Juhtum (a1): kaalutud graaf
Kutsume naabervektoriks kaalutud graafi jÀrjekindlat paari paaride arvu tÀisarve (a[2i], a[2i+1],⊠, kus i on nummerdatud alates 0), kus iga paar numbreid a[2i], a[2i+1] mÀÀrab graafi serva tippude vahel a[2i] ja a[2i+1] vastavalt.
See vorming ei sisalda andmeid, kas graaf on suunatud (mĂ”lemad variandid on vĂ”imalikud). Kui seda formaati kasutatakse suunatud graafi jaoks, siis eeldatakse, et serv on suunatud a[2i] â lt a[2i+1] â le. Edasi: mitte suunatud graafide puhul vĂ”ivad vajadusel olla rakendatud tippude jĂ€rjekorra nĂ”uded (nĂ€iteks, et vĂ€iksema vÀÀrtusega number oleks esimesena).
C++-s on naabervÀljade vektori mÀÀramine otstarbekas std::vector abil, mis on ka pÔhjuseks, miks see andmestruktuuri nimi valiti.
Juhtum (a2): kaalumata graaf, servade kaalud on tÀisarvud.
Sarnaselt juhtumile (a1) nimetame kaalumata graafi naabervĂ€ljade vektoriks jĂ€rjestatud kogumi (dĂŒnaamilise massiivi) numbreid (a[3i], a[3i+1], a[3i+2],âŠ, kus i nummerdatakse alates 0), kus iga 'triplet' numbreid a[3i], a[3i+1], a[3i+2] mÀÀrab graafi servade vahele tippude numbritega a[3i] ja a[3i+1] vastavalt, ning vÀÀrtus a[3i+2] on selle serva kaal. Selline graaf vĂ”ib olla nii suunatud kui ka mitte.
Juhtum (b): kaalumata graaf, servade kaalud ei ole tÀisarvud.
Kuna ĂŒhes massiivis (vektoris) ei saa hoida erinevaid elemente, siis on vĂ”imalik jĂ€rgmine rakendus. Graafik salvestatakse kahes vektoriseeritud paaris, kus esimene vektor on graafi kĂŒlgsuhete vektor ilma kaaludeta ja teine vektor sisaldab vastavaid kaalusid (vĂ”imalik rakendus C++-s: std::pair). Seega, serva, mis on mÀÀratud paariga tippe indeksitega 2i, 2i+1 esimeses vektor, kaal on vĂ”rdne elemendiga indeksi i all teises vektor.
Aga miks seda vaja on?
Noh, selle teksti autori jaoks on see mĂ”ne ĂŒlesande lahendamiseks tundunud piisavalt kasulik. Ametlikult vaadatuna on siin jĂ€rgmised eelised:
- KĂŒlgsuhete vektor, nagu iga teine «loetletav» struktuur, on piisavalt kompaktne, see vĂ”tab vĂ€hem mĂ€lu kui kĂŒlgtabel (haruldaste graafide puhul) ja on suhteliselt lihtne rakendada.
- Graafi tipud vÔivad olla pÔhimÔtteliselt ka tÀhistatud negatiivsete arvudega. VÔib-olla on selline «eraklik» vajadus.
- Graafidel vÔivad olla mitmed servad ja mitmed silmus, millel on erinevad kaalud (positiivsed, negatiivsed ja isegi null). Siin ei ole mingeid piiranguid.
- Lisaks saab servadele mÀÀrata erinevaid omadusi â aga sellest rÀÀgitakse allpool punktis 4.
Siiski tuleb tunnistada, et see "loendamine" ei eelda kiiret juurdepÀÀsu servadele. Siin tuleb appi kĂŒlgnevuse assotsiatiivne massiiv, millest rÀÀgitakse allpool.
3.2 KĂŒlgnevuse assotsiatiivne massiiv
Nii et kui juurdepÀÀs konkreetsele servale, selle kaalule ja teistele omadustele on meile kriitilise tĂ€htsusega, ning mĂ€lunĂ”uded ei luba kasutada kĂŒlgmatrit, siis mĂ”elgem, kuidas saaksime kĂŒlgjĂ€rjendi muuta, et lahendada see ĂŒlesanne. Seega on vĂ”tmemoment graafi serv, mille saame mÀÀrata jĂ€rjendatud paarina tĂ€isarvudest. Millele see sarnaneb? Kas mitte vĂ”tmerikkusele assotsiatiivses massiivis? Ja kui nii, siis miks me seda ellu ei viiks? Laske meil luua selline assotsiatiivne massiiv, kus igale vĂ”tmele â jĂ€rjendatud paarile tĂ€isarvudest â vastab vÀÀrtus â tĂ€isarv vĂ”i reaalnumber, mis mÀÀrab serva kaalu. C++ keeles oleks mĂ”istlik rakendada seda struktuuri konteineril std::map (std::map<std::pair, int> vĂ”i std::map<std::pair, double>), vĂ”i std::multimap, kui eeldatakse mitmeid servi. Ja nĂŒĂŒd on meil graafide salvestamiseks struktuur, mis kasutab vĂ€hem mĂ€lu kui "matrix" struktuurid, suudab mÀÀrata graafe, millel on mitmeid silmust ja servi ning isegi ei oma ranget nĂ”uet positsioonide numbrite mitte-negatiivsuse osas (ma ei tea, kellele seda vaja on, aga siiski).
Andmete struktuur on nagu "tĂ€itke ĂŒles", kuid midagi on puudu
TĂ”epoolest: teatud ĂŒlesannete lahendamisel vĂ”ib meil tekkida vajadus mÀÀrata graafide servadele mingid omadused ning vastavalt nende hoidmiseks. Kui need omadused on vĂ”imalik ĂŒheselt tĂ€isarvudeks mÀÀrata, on vĂ”imalik hoida selliseid "graafe tĂ€iendavate omadustega", kasutades laiendatud naabrusvektoreid ja assotsiatiivseid naabrusmassive.
Niisiis, oletame, et meil on kaalumata graaf, mille iga serva jaoks tuleb salvestada nĂ€iteks 2 tĂ€iendavat tunnust, mis on mÀÀratud tĂ€isarvudega. Sellisel juhul saab mÀÀrata selle naabervektorina jĂ€rjestatud komplekti, mitte âpaaridestâ, vaid âkvartettidestâ tĂ€isarvu (a[2i], a[2i+1], a[2i+2], a[2i+3]âŠ), kus a[2i+2] ja a[2i+3] mÀÀravad vastava serva omadused. Kaaludega graafi puhul on jĂ€rjekord sisuliselt sarnane (erinevuseks on vaid asjaolu, et omadused jĂ€rgivad serva kaalu ja on mÀÀratud elementidega a[2i+3] ja a[2i+4], ning serva ennast mÀÀratakse mitte 4, vaid 5 jĂ€rjestatud numbriga). Kuid graafi, mille servade kaalud ei ole tĂ€isarvud, omadused saab kirjutada selle kaalumatutesse komponentidesse.
Raske anatoomsete graafide kasutamisel, mille harude kaalud on tĂ€isarvud, on vĂ”imalik mÀÀrata vÀÀrtuseks mitte eraldi number, vaid massiiv (vektor) numbreid, mis mÀÀravad, lisaks haru kaalule, kĂ”ik muud vajalikud omadused. Probleemiks on see, et mitte tĂ€isarvuliste kaalude puhul tuleb omadus mÀÀrata ujuva komaga. Jah, see on ebamugav, kuid kui selliseid omadusi pole liiga palju ja kui neid ei mÀÀrata liiga âpeeneltâ jaotatud numbritena, siis vĂ”ib see veel töötada. Seega C++-is vĂ”ivad ulatuslikud anatoomsete graafide massiivid olla mÀÀratud jĂ€rgmiselt: std::map<std::pair, std::vector> vĂ”i std::map<std::pair, std::vector>, kus âvÀÀna vÀÀrtuse vektorâ esimene vÀÀrtus on haru kaal ja seejĂ€rel asuvad selle omaduste numbrilised tĂ€hised.
KĂŒbernĂ€ide:
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 â M.: Williamsi kirjastus, 2011.
2. Harari, Frank. Graafiteooria. M.: Mir, 1973.
Autori ettekande vektorist ja naabrusassotsiatiivsetest massiividest:
3. Chernouhov S.A. Naabrusvektor ja naabruskaardid kui graafide esitamise ja salvestamise viisid / S.A. Chernouhov. Adjacency vector and adjacency map as data structures to represent a graph // Rahvusvahelise teadus-praktilise konverentsi "Innovaatiliste arenduste tulemuste rakendamise probleemid ja nende lahendamise teed" artiklite kogumik (Saratov, 14.09.2019). â Sterlitamak: AMI, 2019, lk 65-69
Kasulikud internetiallikad teemal:
4.
5.
Allikas: habr.com
