C # binare Arbori și Dicționare

voturi
15

Mă luptă cu conceptul de când să utilizeze arbori binari de căutare și când să folosească dicționare.

În cererea mea am făcut un mic experiment care a folosit biblioteca C5 TreeDictionary(care cred că este un copac roșu-negru binar de căutare), și C # dicționar. Dicționarul a fost întotdeauna mai rapid la add / găsi operațiuni și , de asemenea , utilizate întotdeauna mai puțin spațiu de memorie. De exemplu, la 16809 <int, float>intrări, dicționarul utilizat 342 KiB în timp ce arborele folosit 723 KiB.

M-am gândit că BST lui ar fi trebuit să fie mai eficient memoria, dar se pare că un nod al arborelui necesită mai mult de o intrare de octeți într-un dicționar. Ce dă? Există un punct în cazul în care BST sunt mai bune decât dicționare?

De asemenea, ca o întrebare parte, nimeni nu știe dacă există o mai rapid + mai multe structuri de date de memorie pentru stocarea eficientă <int, float>perechi de acces de tip dicționarul decât oricare dintre structurile menționate?

Întrebat 28/01/2010 la 02:46
sursa de către utilizator
În alte limbi...                            


6 răspunsuri

voturi
1

Mi se pare că faci o optimizare prematura.

Ceea ce mi-aș sugera să vă este de a crea o interfață pentru a izola pe care structura pe care îl utilizați, de fapt, și apoi punerea în aplicare a interfeței cu ajutorul dicționarului (care pare să funcționeze cel mai bine).

În cazul în care memoria / performanța devine o problemă (care, probabil, nu va pentru 20K- numere), atunci puteți crea alte implementări de interfață și verificați care unul funcționează bests. Nu va trebui să se schimbe aproape nimic în restul codului (cu excepția celor pe care punerea în aplicare pe care îl utilizați).

Publicat 28/01/2010 la 03:26
sursa de către utilizator

voturi
1

Acesta are nici un sens ca un nod copac ar avea nevoie de mai mult de stocare decât o intrare de dicționar. Un nod arbore binar are nevoie pentru a stoca valoarea și ambele subramificații stânga și dreapta. Generică Dictionary<TKey, TValue>este implementată ca un tabel hash pe care - eu sunt presupunând că - fie utilizează o listă de legătură pentru fiecare compartiment (valoare plus un indicator / de referință) sau un fel de remaparea (doar valoarea). Ar trebui să aibă o privire în reflector pentru a fi sigur, dar pentru scopul acestei întrebări nu cred că e așa de important.

Sparser tabela hash, mai puțin eficient din punct de vedere stocare / memorie. Dacă creați un tabel hash (dicționar) și inițializa capacitatea sa de la 1 milion, și doar se umple cu 10.000 de elemente, atunci eu sunt destul de sigur că ar mânca mult mai multă memorie decât o BST cu 10.000 de noduri.

Cu toate acestea, nu aș face griji despre asta în cazul în care cantitatea de noduri / chei este doar în mii. Asta va fi măsurată în kiloocteți, în comparație cu GB de RAM fizică.


Dacă întrebarea este „de ce doriți să utilizați un arbore binar în loc de un tabel hash?“ Apoi, cel mai bun răspuns este că OMI arbori binari sunt ordonate în timp ce tabele de dispersie nu sunt. Puteți căuta doar un tabel hash pentru chei, care sunt exact egale cu ceva; cu un copac, puteți căuta o gamă de valori, cea mai apropiată valoare, etc Aceasta este o distincție destul de important, dacă creați un index sau ceva similar.

Publicat 28/01/2010 la 03:39
sursa de către utilizator

voturi
0

Interfața pentru un copac și un tabel Hash (care eu sunt ghicitul este ceea ce Dicționarul se bazează unul) ar trebui să fie foarte asemănătoare. revolving întotdeauna în jurul valorii de căutări tastate.

Am crezut întotdeauna un dicționar a fost mai bun pentru a crea lucruri o dată și apoi de a face apoi o mulțime de căutări pe ea. În timp ce un copac a fost mai bine dacă ai o modifica in mod semnificativ. Cu toate acestea, eu nu știu de unde am luat ideea de la.

(Limbi funcționale folosesc adesea copaci ca bază pentru ei colecții după cum puteți re-utiliza cea mai mare parte de copac, dacă ai face mici modificări la acesta).

Publicat 28/01/2010 la 03:40
sursa de către utilizator

voturi
0

Nu te compara „mere cu mere“, un BST va da o ordonat reprezentare in timp ce un dicționar vă permite să faceți o căutare pe o pereche valoare - cheie (în cazul dumneavoastră).

Nu mă aștept de mult dimensiunea în amprenta de memorie între 2, dar dicționarul pe care îl va da o căutare mult mai rapid. Pentru a găsi un element dintr-un BST tine (potențial) trebuie să traverseze întregul copac. Dar pentru a face o căutare dictnary pur și simplu lookup pe baza cheii.

Publicat 28/01/2010 la 04:05
sursa de către utilizator

voturi
8

M-am gândit că BST lui ar fi trebuit să fie mai eficient memoria, dar se pare că un nod al arborelui necesită mai mult de o intrare de octeți într-un dicționar. Ce dă? Există un punct în cazul în care BST sunt mai bune decât dicționare?

Am personal auzit niciodată de un astfel de principiu. Chiar și încă, ei doar un principiu general, nu un fapt categoric gravat în țesătura universului.

În general, Dicționare sunt într-adevăr doar un înveliș în jurul valorii de fantezie o serie de liste legate. Tu inserați în dicționar ca ceva:

LinkedList<Tuple<TKey, TValue>> list =
    internalArray[internalArray % key.GetHashCode()];
if (list.Exists(x => x.Key == key))
    throw new Exception("Key already exists");
list.AddLast(Tuple.Create(key, value));

Deci , ei aproape O (1) operațiune. Dicționarul folosește O (internalArray.Length + n) de memorie, unde n este numărul de elemente din colecție.

În BSTs generale pot fi puse în aplicare ca:

  • legate de liste, care folosesc O (n) spațiu, unde n este numărul elementelor din colecție.
  • matrice , care folosesc O (2 ore - n) spațiu unde h este înălțimea arborelui și n este numărul de elemente din colecție.
    • Deoarece copaci rosu-negru au o înălțime de mărginit O (1,44 * n), o implementare matrice trebuie să aibă o utilizare mărginite memorie de aproximativ O (2 1.44n - n)

Cote sunt, C5 TreeDictionary este implementat folosind matrice, care este probabil responsabilă pentru spațiul irosit.

Ce dă? Există un punct în cazul în care BST sunt mai bune decât dicționare?

Dicționare au unele proprietăți nedorite:

  • S-ar putea să nu fie suficient de blocuri continugous de memorie pentru a organiza dicționarul, chiar dacă cerințele sale de memorie sunt mult mai puțin decât decât memoria RAM totală disponibilă.

  • Evaluarea funcției de distribuire poate avea o lungime arbitrar lungă de timp. Siruri de caractere, de exemplu, utilizați reflector pentru a examina System.String.GetHashCodemetoda - veți observa hashing un șir de caractere O ia întotdeauna timp (n), ceea ce înseamnă că poate lua timp considerabil pentru șiruri foarte lungi. Pe de o parte, comparând siruri de inegalitate aproape întotdeauna mai repede decât hashing, deoarece acesta poate solicita uita la doar primele câteva caractere. Sale în întregime posibil ca inserturi de arbori să fie mai rapid decât în cazul în care evaluarea inserturi dicționar cod hash durează prea mult.

    • Int32 lui GetHashCodemetoda este literalmente doar return this, asa ca ar fi hardpressed pentru a găsi un caz în care un hashtable cu chei int este mai lent decât un dicționar copac.

RB Copacii au unele proprietăți dorite:

  • Puteți găsi / elimina elementele minime si maxime în O (log n) timp, în comparație cu O dată (n) folosind un dicționar.

  • În cazul în care un copac este pus în aplicare sub forma unei liste legate , mai degrabă decât o matrice, copacul este de obicei mai mult spațiu eficient decât un dicționar.

  • De asemenea, ei ridicol de ușor de a scrie versiuni imuabile de copaci care susțin inserați / căutare / șterge în O (log n). Dicționare nu se adaptează bine la imuabilitatea, din moment ce trebuie să copiați întreaga matrice internă pentru fiecare operațiune ( de fapt, am am văzut unele implementări bazate pe matrice de arbori deget imuabile, un fel de scop general , structura de date dicționar, dar punerea în aplicare este foarte complex).

  • Puteți traversa toate elementele dintr-un copac, pentru sortat în spațiu constant și timp O (n), în timp ce ai nevoie să arunce un tabel hash într-o matrice și sortați-l pentru a obține același efect.

Deci, alegerea structurii de date într-adevăr depinde de ceea ce proprietăți aveți nevoie. Dacă doriți doar o pungă neordonate și poate garanta că funcția hash evalua rapid, du-te cu un dicționar .Net. Dacă aveți nevoie de un sac de ordonat sau au o funcție hash lent de rulare, du-te cu TreeDictionary.

Publicat 28/01/2010 la 04:16
sursa de către utilizator

voturi
0

Un BST echilibrat este de preferat în cazul în care aveți nevoie pentru a proteja structura de date de la crampoane latență și atacuri coliziuni hash.

Cea dintâi se întâmplă atunci când o structură susținută-matrice crește o devine redimensionate, acesta din urmă este o proprietate inevitabilă a hashing algoritm ca o proiecție de spațiu infinit la un interval întreg limitat.

O altă problemă în .NET este că există LOH, și cu un dicționar suficient de mare ai alerga într-o fragmentare LOH. În acest caz, puteți utiliza un BST, plătește un preț de clasă complexitate algoritmică mai mare.

Pe scurt, cu o BST susținută de mormanul de alocare te timp cel mai rău caz O (log (N)), cu hashtable ai O (N) cel mai rău caz timpul.

BST vine la un preț de O (log (N)) timpul mediu, localitatea cache mai rău și mai multe alocări heap, dar are garanții de latență și este protejat de atacuri de dicționar și fragmentarea memoriei.

menționând în valoare de faptul că BST este, de asemenea, un subiect la fragmentarea memoriei pe alte platforme, nu folosind un colector de gunoi de compactare.

În ceea ce privește dimensiunea memoriei, clasa .NET Dictionary`2 este mult mai eficient de memorie, deoarece stochează date ca o listă legată off-heap, valoare care doar stochează și informații de offset. BST are pentru a stoca antet obiect (ca fiecare nod este un exemplu de clasă pe heap), două indicii, și unele date de arbori augmentată pentru copaci echilibrate. De exemplu, un arbore rosu-negru ar avea nevoie de un boolean interpretat ca culoare (roșu sau negru). Acest lucru este de cel puțin 6 cuvinte de mașină, dacă nu mă înșel. Deci, fiecare nod într-un arbore rosu-negru pe sistemul pe 64 de biți este un minim de:

3 cuvinte pentru antet = 24 octeți 2 cuvinte pentru copil pointerii = 16 octeți 1 cuvânt pentru culoare = 8 octeți de cel puțin 1 cuvânt pentru valoarea 8+ octeți = 24 + 16 + 8 + 8 = 56 octeți (+8 octeți în cazul în care arborele folosește un nod pointer părinte).

În același timp, dimensiunea minimă a intrării în dicționarul ar fi doar 16 bytes.

Publicat 10/12/2018 la 13:18
sursa de către utilizator

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more